diff --git a/.ignore b/.ignore
index 4356adf..2df43f6 100644
--- a/.ignore
+++ b/.ignore
@@ -3,3 +3,4 @@
$bundle.lua
*.js
*.js.map
+*.lua
diff --git a/build/import.moon b/build/import.moon
index ba99b03..92d4397 100644
--- a/build/import.moon
+++ b/build/import.moon
@@ -9,7 +9,7 @@
require 'mmm'
require 'lfs'
-import Fileder, Key from require 'mmm.mmmfs.fileder'
+import Key from require 'mmm.mmmfs.fileder'
import SQLStore from require 'mmm.mmmfs.stores.sql'
-- usage:
diff --git a/build/render_all.moon b/build/render_all.moon
index 02da12c..b09957e 100644
--- a/build/render_all.moon
+++ b/build/render_all.moon
@@ -8,7 +8,7 @@
add '?/init.server'
require 'mmm'
-import load_tree from require 'mmm.mmmfs.fileder'
+import Fileder from require 'mmm.mmmfs.fileder'
import get_store from require 'mmm.mmmfs.stores'
import render from require 'mmm.mmmfs.layout'
@@ -20,7 +20,7 @@
STATIC = true
store = get_store store
-tree = load_tree store
+tree = Fileder store
tree = tree\walk startpath if startpath
for fileder in coroutine.wrap tree\iterate
diff --git a/build/server.moon b/build/server.moon
index 693f00c..4e7ad3a 100644
--- a/build/server.moon
+++ b/build/server.moon
@@ -9,7 +9,7 @@
require 'mmm'
-import Key, dir_base, load_tree from require 'mmm.mmmfs.fileder'
+import dir_base, Key, Fileder from require 'mmm.mmmfs.fileder'
import convert from require 'mmm.mmmfs.conversion'
import get_store from require 'mmm.mmmfs.stores'
import render from require 'mmm.mmmfs.layout'
@@ -38,7 +38,7 @@
assert @server\loop!
handle: (method, path, facet) =>
- fileder = load_tree @store, path -- @tree\walk path
+ fileder = Fileder @store, path
if not fileder
-- fileder not found
@@ -50,7 +50,7 @@
when '?interactive'
export BROWSER
- root = load_tree @store
+ root = Fileder @store
BROWSER = Browser root, path
render BROWSER\todom!, fileder, noview: true, scripts: "
"
+ rplc\render!
+
+if MODE == 'CLIENT'
+ export ^
+ export o
+ eval = js.global\eval
+ GRID_W = 50
+ GRID_H = 40
+
+ SVG =
+ doc: eval "(function() { return SVG(document.createElement('svg')); })",
+ G: eval "(function() { return new SVG.G(); })",
+ setmetatable SVG, __call: => @doc!
+
+ o = do
+ mkobj = eval "(function () { return {}; })"
+ (tbl) ->
+ with obj = mkobj!
+ for k,v in pairs(tbl)
+ obj[k] = v
+
+ class Diagram
+ new: (f) =>
+ @svg = SVG!
+ @arrows = SVG.G!
+ @width, @height = 0, 0
+ @y = 0
+
+ f @
+
+ txtattr = o {
+ fill: 'white',
+ 'font-size': '14px',
+ 'text-anchor': 'middle',
+ }
+ block: (color, label, h=1) =>
+ @svg\add with SVG.G!
+ with \rect GRID_W, h * GRID_H
+ \attr o fill: color
+ if label
+ with \plain label
+ \move GRID_W/2, 0
+ \attr txtattr
+
+ \move @width * GRID_W, (@y + h) * -GRID_H
+ @y += h
+ if @y > @height
+ @height = @y
+
+ arrattr = o {
+ fill: 'white',
+ 'font-size': '18px',
+ 'text-anchor': 'middle',
+ }
+ arrow: (char, x, y) =>
+ with @arrows\plain char
+ \attr arrattr
+ \move (x + 1) * GRID_W, (y - 0.5) * -GRID_H - 11
+
+ -- inout: (x=@width, y=@y) => @arrow '⇋', x, y -- U+21CB
+ -- inn: (x=@width, y=@y) => @arrow '↼', x, y+0.25 -- U+21BC
+ -- out: (x=@width, y=@y) => @arrow '⇁', x, y-0.25 -- U+21C1
+ inout: (x=@width, y=@y) => @arrow '⇆', x, y -- U+21C6
+ inn: (x=@width, y=@y) => @arrow '←', x, y+0.25 -- U+2190
+ out: (x=@width, y=@y) => @arrow '→', x, y-0.25 -- U+2192
+
+ mind: (label='mind', ...) => @block '#fac710', label, ...
+ phys: (label='phys', ...) => @block '#8fd13f', label, ...
+ digi: (label='digi', ...) => @block '#9510ac', label, ...
+
+ next: =>
+ @y = 0
+ @width += 1
+
+ finish: =>
+ return if @node
+ @svg\add @arrows
+
+ @width += 1
+ w, h = @width * GRID_W, @height * GRID_H
+
+ l = GRID_W / 6.5
+ @svg\add with @svg\line 0, -GRID_H, w, -GRID_H
+ \stroke o width: 2, color: '#ffffff', dasharray: "#{l}, #{l}"
+
+ @svg\size w, h
+ @svg\viewbox 0, -h, w, h
+ @node = @svg.node
+
+addlabel = (label, diagram) ->
+ with div style: { display: 'inline-block', margin: '20px', 'text-align': 'center' }
+ \append diagram
+ \append div label
+
+figures = do
+ style =
+ display: 'flex'
+ 'align-items': 'flex-end'
+ 'justify-content': 'space-evenly'
+ (...) -> div { :style, ... }
+
+sources = do
+ short = => "#{@id} #{@year}"
+ long = => @names, " (#{@year}): ", (i @title), ", #{@published}"
+ {
+ {
+ id: 'Milgram',
+ title: 'Augmented Reality: A class of displays on the reality-virtuality continuum',
+ published: 'in SPIE Vol. 2351',
+ names: 'P. Milgram, H. Takemura, A. Utsumi, F. Kishino',
+ year: 1994
+ :long, :short,
+ },
+ {
+ id: 'Marsh',
+ title: 'Nested Immersion: Describing and Classifying Augmented Virtual Reality',
+ published: 'IEEE Virtual Reality Conference 2015',
+ names: 'W. Marsh, F. Mérienne',
+ year: 2015
+ :long, :short,
+ },
+ {
+ id: 'Billinghurst',
+ title: 'The MagicBook: a transitional AR interface',
+ published: 'in Computer & Graphics 25',
+ names: 'M. Billinghurst, H. Kato, I. Poupyrev',
+ year: 2001,
+ :long, :short,
+ },
+ {
+ id: 'Matrix',
+ title: 'The Matrix',
+ year: 1999,
+ names: 'L. Wachowski, A. Wachowski',
+ long: => @names, " (#{@year}): ", (i @title), " (movie)"
+ short: => tostring @year
+ },
+ {
+ id: 'Naam',
+ title: 'Nexus',
+ published: 'Angry Robot (novel)',
+ names: 'R. Naam',
+ year: 2012,
+ :long, :short,
+ }
+ }
+
+ref = do
+ fmt = (id) ->
+
+ local src
+ for _src in *sources
+ if _src.id == id
+ src = _src
+ break
+
+ if src
+ a { src\short!, href: "##{src.id}" }
+ else
+ span id
+
+ ref = (...) ->
+ refs = { ... }
+ with span "(", fmt refs[1]
+ for i=2, #refs
+ \append ", "
+ \append fmt refs[i]
+ \append ")"
+
+references = ->
+ with ol!
+ for src in *sources
+ \append li { id: src.id, src\long! }
+
+sect = (label) ->
+ with section style: 'page-break-inside': 'avoid'
+ \append h2 label
+
+append with article style: { margin: 'auto', 'max-width': '750px' }
+ \append div 'Sol Bekic', style: 'text-align': 'right'
+
+ \append h1 {
+ style: { 'text-align': 'center', 'font-size': '2em' },
+ "Reality Stacks",
+ div "a Taxonomy for Multi-Reality Experiences", style: 'font-size': '0.6em'
+ }
+
+ \append with sect "Abstract"
+ \append p "With the development of mixed-reality experiences and the corresponding interface devices
+ multiple frameworks for classification of these experiences have been proposed. However these past
+ attempts have mostly been developed alongside and with the intent of capturing specific projects ",
+ (ref 'Marsh', 'Billinghurst'), " or are nevertheless very focused on existing methods and technologies ",
+ (ref 'Milgram'), ". The existing taxonomies also all assume physical reality as a fixpoint and constant and are
+ thereby not suited to describe many fictional mixed-reality environments and altered states of consciousness.
+ In this paper we describe a new model for describing such experiences and examplify it's use with currently
+ existing as well as idealized technologies from popular culture."
+
+ \append with sect "Terminology"
+ \append p "We propose the following terms and definitions that will be used extensively for the remainder of the paper:"
+ for definition in *{
+ { "layer of reality": "a closed system consisting of a world model and a set of rules or dynamics operating on and
+ constraining said model." },
+ { "world model": "describes a world state containing objects, agents and/or concepts on an arbitrary abstraction level." },
+ '------',
+ { "reality stack": "structure consisting of all layers of reality encoding an agent's interaction with his environment
+ in their world model at a given moment, as well as all layers supporting these respectively." },
+ '------',
+ { "physical reality": "layer of reality defined by physical matter and the physical laws acting upon it.
+ While the emergent phenomena of micro- and macro physics as well as layers of social existence etc. may be seen
+ as separate layers, for the purpose of this paper we will group these together under the term of physical reality." },
+ { "mental reality": "layer of reality perceived and processed by the brain of a human agent." },
+ { "digital reality": "layer of reality created and simulated by a digital system, e.g. a virtual reality game." },
+ { "phys, mind, digi": "abbreviations for physical, mental and digital reality respectively." },
+ }
+ if 'string' == type definition
+ \append hr!
+ continue
+ \append with div style: { 'margin-left': '2rem' }
+ term = next definition
+ \append span term, style: {
+ display: 'inline-block',
+ 'margin-left': '-2rem',
+ 'font-weight': 'bold',
+ 'min-width': '140px'
+ }
+ \append span definition[term]
+
+ \append with sect "Introduction"
+ \append p "We identify two different types of relationships between layers in multi-reality environments.
+ The first is layer nesting. Layer nesting describes how some layers are contained in other layers; i.e. they exist
+ within and can be represented fully by the parent layer's world model and the child layer's rules emerge natively from
+ the parent layer's dynamics. Layer nesting is visualized on the vertical axis in the following diagrams.
+ For each layer of reality on the bottom of the diagram the nested parent layers can be found by tracing a line upwards
+ to the top of the diagram. Following a materialistic point of view, physical reality therefore must completely encompass
+ the top of each diagram."
+
+ \append p "The second type of relationship describes the information flow between a subject and the layers of reality
+ the subject is immersed in. In a multi-reality experience the subject has access to multiple layers of reality and
+ their corresponding world models simultaneously.", br!,
+ "Depending on the specific experience, different types of and directions for information exchange
+ can exist between these layers and the subject's internal representation of the experience.
+ For the sake of this paper we distinguish only between ", (i "input"), " and ", (i "output"), " data flow (from the
+ perspective of the subject); categorized loosely as information the subject receives from the environment
+ (", (i "input"), ", e.g. visual stimuli) and actions the subject can take to influence the state of the world model
+ (", (i "output"), ", e.g. motor actions) respectively."
+
+ \append p "In the following diagrams, information flow is visualized horizontally, in the region below the dashed line
+ at the bottom of the diagram. The subject's internal mental model and layer of reality are placed on the bottom left
+ side of the diagram.
+ The layers of reality that the subject experiences directly and that mirror it's internal representations are placed
+ on the far right. There may be multiple layers of reality sharing this space, visualized as a vertical stack of
+ layers. Since the subject must necessarily have a complete internal model of the multi-reality experience around
+ him to feel immersed, the subject's mental layer of reality must span the full height of all the layers visible
+ on the right side of the diagram.", br!,
+ "Information flow itself is now visualized concretely using arrows that cross layer boundaries in the lower part of
+ the diagram as described above. Arrows pointing leftwards denote ", (i "input"), " flow, whilst arrows pointing
+ rightwards denote ", (i "output"), "-directed information flow. In some cases information doesn't flow directly
+ between the layers the subject is directly aware of and the subject's internal representation and instead
+ traverses ", (i "intermediate layers"), " first."
+
+ \append p "Before we take a look at some reality stacks corresponding to current VR and AR technology,
+ we can take a look at waking life as a baseline stack. To illustrate the format of the diagram we will compare it
+ to the stack corresponding to a dreaming state:"
+
+ \append with figures!
+ \append addlabel "Waking Life", Diagram =>
+ @mind!
+ @inout!
+ @phys!
+
+ @next!
+ @phys '', 2
+ @finish!
+
+ \append addlabel "Dreaming", Diagram =>
+ @mind!
+ @phys!
+ @finish!
+
+ \append p "In both cases, the top of the diagram is fully occupied by the physical layer of reality, colored in green.
+ This is due to the fact that, according to the materialistic theory of mind, human consciousness owes its existance
+ to the physical and chemical dynamics of neurons in our brains. Therefore our mental reality must be considered
+ fully embedded in the physical reality, and consequently it may only appear underneath it in the diagram."
+
+ \append p "During waking life, we concern ourselves mostly with the physical reality surrounding us.
+ For this reason the physical reality is placed in the lower right corner of the diagram as the layer holding the
+ external world model relevant to the subject. Information flows in both directions between the physical world model
+ and the subject's mental model, as denoted by the two white arrows: Information about the state of the world model
+ enter the subjects mind via the senses (top arrow, pointing leftwards), and choices the subject makes inside of and
+ based on his mental model can feed back into the physical layer through movements (lower arrow, pointing rightwards)."
+
+ \append p "In the dreaming state on the other hand, the subject is unaware of the physical layer of reality, though
+ the mind remains embedded inside it. When dreaming, subjects' mental models don't depend on external models, hence
+ the mental layer of reality must be the only layer along the bottom of the diagram."
+
+ \append with sect "Current Technologies"
+ \append p "Since recent technological advancements have enabled the development of VR and AR consumer devices,
+ AR and VR have been established as the potential next frontier of digital entertainment.", br!,
+ "As the names imply, the notion of reality is at the core of both technologies.
+ In the following section we will take a look at the respective stacks of both experience types:"
+
+ \append with figures!
+ \append addlabel "VR", Diagram =>
+ @mind!
+ @phys!
+ @inout nil, 1
+
+ @next!
+ @phys '', 2
+ @inout nil, 1
+
+ @next!
+ @digi!
+ @phys ''
+ @finish!
+
+
+ \append addlabel "AR", Diagram =>
+ @mind!
+ @inout nil, 1.25
+ @inn nil, 0.5
+ @phys!
+
+ @next!
+ @phys '', 2
+ @inn nil, .5
+
+ @next!
+ @digi nil, .5
+ @phys '', 1.5
+ @finish!
+
+ \append p "In both cases we find the physical layer of reality as an ", (i "intermediate layer"), " between the mental
+ and digital layers. Actions taken by the subject have to be acted out physically (corresponding to the
+ information traversing the barrier between mental and physical reality) before they can be again digitized using
+ the various tracking and input technologies (which in turn carry the information across the boundary of the physical
+ and digital spaces)."
+
+ \append p "The difference between AR and VR lies in the fact that in AR the subject experiences a mixture of the
+ digital and physical world models. This can be seen in the diagram, where we find that right of the diagram origin
+ and the mental model, the diagram splits and terminates in both layers: while information reaches the subject both
+ from the digital reality through the physical one, as well as directly from the physical reality, the subject only
+ directly manipulates state in the physical reality."
+
+ \append p "The data conversions necessary at layer boundaries incur at the least losses in quality and accuracy of
+ information for purely technical reasons. However ", (i "intermediate layers"), " come at a cost larger than just
+ an additional step of conversion:
+ For information to flow through a layer, it must be encodable within that layer’s world model.
+ This means that the 'weakest link' in a given reality stack determines the upper bound of information possible to
+ encode within said stack and thereby limits the overall expressivity of the stack.", br!,
+ "As a practical example we can consider creating an hypothetical VR application that allows users to traverse a
+ large virtual space by flying. While the human mind is perfectly capable of imagining to fly and control the motion
+ appropriately, it is extremely hard to devise and implement a satisfying setup and control scheme because the
+ physical body of the user needs to be taken into account and it, unlike the corresponding representations in the
+ mental and digital world models, cannot float around freely."
+
+ \append with sect "Future Developments"
+ \append p "In the previous section we found that the presence of the physical layer in the information path of
+ VR and AR stacks limits the experience as a whole. It follows that the removal of that indirection should be
+ an obvious goal for future developments:"
+
+ \append figures addlabel "holy grail of VR: 'The Matrix'", Diagram =>
+ @mind!
+ @inout!
+ @phys!
+
+ @next!
+ @digi!
+ @phys ''
+ @finish!
+
+ \append p "In the action movie 'The Matrix' ", (ref 'Matrix'), ", users of the titular VR environment interface with it
+ by plugging cables into implanted sockets that connect the simulation directly to their central nervous system.", br!,
+ "While these cables and implanted devices are physical devices, they don't constitute the presence of the
+ physical layer of reality in the information path because while they do transmit information, the information
+ remains in either the encoding of the mental model (neural firing patterns) or the encoding of the digital model
+ (e.g. a numeric encoding of a player character's movement in digital space) and the conversion is made directly
+ between those two - the data never assumes the native encoding of the physical layer (e.g. as a physical motion)."
+
+ \append p "While we are currently far from being able to read arbitrary high-level information from the brain
+ or to synthesize sensual input in human perception by bypassing the sensory organs, brain-computer interfaces (BCI)
+ are a very active area of research with high hopes for comparable achievements in the near future."
+
+ \append p "Applying this same step of removing the physical layer of reality from AR, we end up with something similar
+ to the nano-particle drug in ", (i "Nexus"), " ", (ref 'Naam'), ". However this does not grant the user a similar
+ amount of control over his experience as the holy grail of VR does, since the user and the physical part of the
+ environment remain bound by the physical layer of reality's laws.", br!,
+ "Instead the holy grail of AR is reached with the creation of a god machine that can manipulate the state of the
+ physical world according to the user's wishes. In this way the digital and physical realities become unified and
+ fully 'augmented'."
+
+ \append with figures!
+ \append addlabel "'Nexus'", Diagram =>
+ @mind!
+ @inout nil, 0.75
+ @inout nil, 1.25
+ @phys!
+
+ @next!
+ @digi nil, .5
+ @phys '', 1.5
+ @finish!
+
+ \append addlabel "holy grail of AR: 'Deus Machina'", Diagram =>
+ col = '#92807c'
+
+ @mind!
+ @inout!
+ @block col, ''
+
+ @next!
+ @block col, '', 2
+ @svg\plain('phys + digi')\attr(o fill: 'white', 'font-size': '14px')\move 6, -2 * GRID_H
+ @finish!
+
+ \append p "Despite the similarities of VR and AR, the two can be considered polar opposites, as becomes evident when
+ we compare their respective utopian implementations: they share the goal of allowing us to experience realities
+ different from the one we naturally inhabit, but while VR seeks to accomplish this by creating a new, nested reality
+ inside ours, thus giving us full control over it.
+ AR, on the other hand, is instead an attempt to retrofit our specific needs directly into the very reality we exist
+ in.", br!,
+ "This is in direct contrast with the popular notion of the 'reality-virtuality continuum' ", (ref 'Milgram'), ":
+ the reality-virtuality continuum places common reality and VR (virtuality) as the two extreme poles, while AR
+ is represented as an intermediate state between the two. Here however we propose to view instead AR and VR as the
+ respective poles and find instead reality at the centerpoint, where the two opposing influences 'cancel out'."
+
+ \append with sect "Conclusion and Further Work"
+ \append p "In this paper we have proposed a taxonomy and visualization style for multi-reality experiences, as well
+ as demonstrated it's flexibility by applying them as examples. Through the application of the proposed theory,
+ we have also gained a new and contrasting view on preceding work such as the reality-virtuality-continuum.
+ We have also found that the taxonomy can be used outside the research field of media studies and its use may extend
+ as far as philosophy of consciousness (see Appendix below)."
+
+ \append p "Further research could enhance the proposed theory with better and more concrete definitions.
+ In the future, the proposed taxonomy might be used to create a more extensive and complete classification
+ of reality stacks and to analyse the relationships between them."
+
+ \append with sect 'References'
+ \append references!
+
+ \append with sect "Appendix: Relation to Theories of Mind"
+ \append p "This paper starts from a deeply materialistic point of view that borders on microphysicalism.
+ However it should be noted that the diagram style introduced above lends itself also to display other
+ philosophical theories of mind. As an example, the following graphics show a typical VR stack as interpreted by
+ Materialism, Cartesian Dualism and Solipsism respectively:"
+
+ \append with figures!
+ \append addlabel "VR in Materialism", Diagram =>
+ @mind!
+ @inout nil, 1
+ @phys!
+
+ @next!
+ @phys '', 2
+ @inout nil, 1
+
+ @next!
+ @digi!
+ @phys ''
+
+ @finish!
+
+ \append addlabel "VR in Solipsism", Diagram =>
+ @mind nil, 2
+ @inout nil, 1
+
+ @next!
+ @digi!
+ @mind ''
+ @finish!
+
+ \append addlabel "VR in Cartesian Dualism", Diagram =>
+ @mind nil, 2
+ @inout nil, 1
+ @next!
+
+ @phys nil, 2
+ @inout nil, 1
+ @next!
+
+ @digi!
+ @phys ''
+ @finish!
+
+ \append p "However these philosophical theories of minds also constitute reality stacks by themselves and as such can
+ be compared directly:"
+
+ \append with figures!
+ \append addlabel "Materialism", Diagram =>
+ @mind!
+ @inout!
+ @phys!
+
+ @next!
+ @phys '', 2
+ @finish!
+
+ \append addlabel "Solipsism", Diagram =>
+ @mind!
+ @finish!
+
+ \append addlabel "Cartesian Dualism", Diagram =>
+ @mind!
+ @inout!
+ @next!
+
+ @phys!
+ @finish!
+
+_content
diff --git a/root/articles/realities/text$moonscript -> mmm$dom.moon b/root/articles/realities/text$moonscript -> mmm$dom.moon
deleted file mode 100644
index 7689dbb..0000000
--- a/root/articles/realities/text$moonscript -> mmm$dom.moon
+++ /dev/null
@@ -1,542 +0,0 @@
-import elements from require 'mmm.component'
-import h1, h2, p, a, i, div, ol, li, br, hr, span, button, section, article from elements
-
-_content = div!
-append = _content\append
-
-if MODE == 'SERVER'
- export ^
- class Diagram
- style = {
- display: 'inline-block',
- width: '150px',
- height: '80px',
- 'line-height': '80px',
- color: '#fff',
- background: '#666',
- }
-
- @id = 1
- new: (@func) =>
- @id = "diagram-#{@@id}"
- @@id += 1
-
- render: =>
- rplc = with div id: @id, :style
- \append '(diagram goes here)'
- -- \append ""
- rplc\render!
-
-if MODE == 'CLIENT'
- export ^
- export o
- eval = js.global\eval
- GRID_W = 50
- GRID_H = 40
-
- SVG =
- doc: eval "(function() { return SVG(document.createElement('svg')); })",
- G: eval "(function() { return new SVG.G(); })",
- setmetatable SVG, __call: => @doc!
-
- o = do
- mkobj = eval "(function () { return {}; })"
- (tbl) ->
- with obj = mkobj!
- for k,v in pairs(tbl)
- obj[k] = v
-
- class Diagram
- new: (f) =>
- @svg = SVG!
- @arrows = SVG.G!
- @width, @height = 0, 0
- @y = 0
-
- f @
-
- txtattr = o {
- fill: 'white',
- 'font-size': '14px',
- 'text-anchor': 'middle',
- }
- block: (color, label, h=1) =>
- @svg\add with SVG.G!
- with \rect GRID_W, h * GRID_H
- \attr o fill: color
- if label
- with \plain label
- \move GRID_W/2, 0
- \attr txtattr
-
- \move @width * GRID_W, (@y + h) * -GRID_H
- @y += h
- if @y > @height
- @height = @y
-
- arrattr = o {
- fill: 'white',
- 'font-size': '18px',
- 'text-anchor': 'middle',
- }
- arrow: (char, x, y) =>
- with @arrows\plain char
- \attr arrattr
- \move (x + 1) * GRID_W, (y - 0.5) * -GRID_H - 11
-
- -- inout: (x=@width, y=@y) => @arrow '⇋', x, y -- U+21CB
- -- inn: (x=@width, y=@y) => @arrow '↼', x, y+0.25 -- U+21BC
- -- out: (x=@width, y=@y) => @arrow '⇁', x, y-0.25 -- U+21C1
- inout: (x=@width, y=@y) => @arrow '⇆', x, y -- U+21C6
- inn: (x=@width, y=@y) => @arrow '←', x, y+0.25 -- U+2190
- out: (x=@width, y=@y) => @arrow '→', x, y-0.25 -- U+2192
-
- mind: (label='mind', ...) => @block '#fac710', label, ...
- phys: (label='phys', ...) => @block '#8fd13f', label, ...
- digi: (label='digi', ...) => @block '#9510ac', label, ...
-
- next: =>
- @y = 0
- @width += 1
-
- finish: =>
- return if @node
- @svg\add @arrows
-
- @width += 1
- w, h = @width * GRID_W, @height * GRID_H
-
- l = GRID_W / 6.5
- @svg\add with @svg\line 0, -GRID_H, w, -GRID_H
- \stroke o width: 2, color: '#ffffff', dasharray: "#{l}, #{l}"
-
- @svg\size w, h
- @svg\viewbox 0, -h, w, h
- @node = @svg.node
-
-addlabel = (label, diagram) ->
- with div style: { display: 'inline-block', margin: '20px', 'text-align': 'center' }
- \append diagram
- \append div label
-
-figures = do
- style =
- display: 'flex'
- 'align-items': 'flex-end'
- 'justify-content': 'space-evenly'
- (...) -> div { :style, ... }
-
-sources = do
- short = => "#{@id} #{@year}"
- long = => @names, " (#{@year}): ", (i @title), ", #{@published}"
- {
- {
- id: 'Milgram',
- title: 'Augmented Reality: A class of displays on the reality-virtuality continuum',
- published: 'in SPIE Vol. 2351',
- names: 'P. Milgram, H. Takemura, A. Utsumi, F. Kishino',
- year: 1994
- :long, :short,
- },
- {
- id: 'Marsh',
- title: 'Nested Immersion: Describing and Classifying Augmented Virtual Reality',
- published: 'IEEE Virtual Reality Conference 2015',
- names: 'W. Marsh, F. Mérienne',
- year: 2015
- :long, :short,
- },
- {
- id: 'Billinghurst',
- title: 'The MagicBook: a transitional AR interface',
- published: 'in Computer & Graphics 25',
- names: 'M. Billinghurst, H. Kato, I. Poupyrev',
- year: 2001,
- :long, :short,
- },
- {
- id: 'Matrix',
- title: 'The Matrix',
- year: 1999,
- names: 'L. Wachowski, A. Wachowski',
- long: => @names, " (#{@year}): ", (i @title), " (movie)"
- short: => tostring @year
- },
- {
- id: 'Naam',
- title: 'Nexus',
- published: 'Angry Robot (novel)',
- names: 'R. Naam',
- year: 2012,
- :long, :short,
- }
- }
-
-ref = do
- fmt = (id) ->
-
- local src
- for _src in *sources
- if _src.id == id
- src = _src
- break
-
- if src
- a { src\short!, href: "##{src.id}" }
- else
- span id
-
- ref = (...) ->
- refs = { ... }
- with span "(", fmt refs[1]
- for i=2, #refs
- \append ", "
- \append fmt refs[i]
- \append ")"
-
-references = ->
- with ol!
- for src in *sources
- \append li { id: src.id, src\long! }
-
-sect = (label) ->
- with section style: 'page-break-inside': 'avoid'
- \append h2 label
-
-append with article style: { margin: 'auto', 'max-width': '750px' }
- \append div 'Sol Bekic', style: 'text-align': 'right'
-
- \append h1 {
- style: { 'text-align': 'center', 'font-size': '2em' },
- "Reality Stacks",
- div "a Taxonomy for Multi-Reality Experiences", style: 'font-size': '0.6em'
- }
-
- \append with sect "Abstract"
- \append p "With the development of mixed-reality experiences and the corresponding interface devices
- multiple frameworks for classification of these experiences have been proposed. However these past
- attempts have mostly been developed alongside and with the intent of capturing specific projects ",
- (ref 'Marsh', 'Billinghurst'), " or are nevertheless very focused on existing methods and technologies ",
- (ref 'Milgram'), ". The existing taxonomies also all assume physical reality as a fixpoint and constant and are
- thereby not suited to describe many fictional mixed-reality environments and altered states of consciousness.
- In this paper we describe a new model for describing such experiences and examplify it's use with currently
- existing as well as idealized technologies from popular culture."
-
- \append with sect "Terminology"
- \append p "We propose the following terms and definitions that will be used extensively for the remainder of the paper:"
- for definition in *{
- { "layer of reality": "a closed system consisting of a world model and a set of rules or dynamics operating on and
- constraining said model." },
- { "world model": "describes a world state containing objects, agents and/or concepts on an arbitrary abstraction level." },
- '------',
- { "reality stack": "structure consisting of all layers of reality encoding an agent's interaction with his environment
- in their world model at a given moment, as well as all layers supporting these respectively." },
- '------',
- { "physical reality": "layer of reality defined by physical matter and the physical laws acting upon it.
- While the emergent phenomena of micro- and macro physics as well as layers of social existence etc. may be seen
- as separate layers, for the purpose of this paper we will group these together under the term of physical reality." },
- { "mental reality": "layer of reality perceived and processed by the brain of a human agent." },
- { "digital reality": "layer of reality created and simulated by a digital system, e.g. a virtual reality game." },
- { "phys, mind, digi": "abbreviations for physical, mental and digital reality respectively." },
- }
- if 'string' == type definition
- \append hr!
- continue
- \append with div style: { 'margin-left': '2rem' }
- term = next definition
- \append span term, style: {
- display: 'inline-block',
- 'margin-left': '-2rem',
- 'font-weight': 'bold',
- 'min-width': '140px'
- }
- \append span definition[term]
-
- \append with sect "Introduction"
- \append p "We identify two different types of relationships between layers in multi-reality environments.
- The first is layer nesting. Layer nesting describes how some layers are contained in other layers; i.e. they exist
- within and can be represented fully by the parent layer's world model and the child layer's rules emerge natively from
- the parent layer's dynamics. Layer nesting is visualized on the vertical axis in the following diagrams.
- For each layer of reality on the bottom of the diagram the nested parent layers can be found by tracing a line upwards
- to the top of the diagram. Following a materialistic point of view, physical reality therefore must completely encompass
- the top of each diagram."
-
- \append p "The second type of relationship describes the information flow between a subject and the layers of reality
- the subject is immersed in. In a multi-reality experience the subject has access to multiple layers of reality and
- their corresponding world models simultaneously.", br!,
- "Depending on the specific experience, different types of and directions for information exchange
- can exist between these layers and the subject's internal representation of the experience.
- For the sake of this paper we distinguish only between ", (i "input"), " and ", (i "output"), " data flow (from the
- perspective of the subject); categorized loosely as information the subject receives from the environment
- (", (i "input"), ", e.g. visual stimuli) and actions the subject can take to influence the state of the world model
- (", (i "output"), ", e.g. motor actions) respectively."
-
- \append p "In the following diagrams, information flow is visualized horizontally, in the region below the dashed line
- at the bottom of the diagram. The subject's internal mental model and layer of reality are placed on the bottom left
- side of the diagram.
- The layers of reality that the subject experiences directly and that mirror it's internal representations are placed
- on the far right. There may be multiple layers of reality sharing this space, visualized as a vertical stack of
- layers. Since the subject must necessarily have a complete internal model of the multi-reality experience around
- him to feel immersed, the subject's mental layer of reality must span the full height of all the layers visible
- on the right side of the diagram.", br!,
- "Information flow itself is now visualized concretely using arrows that cross layer boundaries in the lower part of
- the diagram as described above. Arrows pointing leftwards denote ", (i "input"), " flow, whilst arrows pointing
- rightwards denote ", (i "output"), "-directed information flow. In some cases information doesn't flow directly
- between the layers the subject is directly aware of and the subject's internal representation and instead
- traverses ", (i "intermediate layers"), " first."
-
- \append p "Before we take a look at some reality stacks corresponding to current VR and AR technology,
- we can take a look at waking life as a baseline stack. To illustrate the format of the diagram we will compare it
- to the stack corresponding to a dreaming state:"
-
- \append with figures!
- \append addlabel "Waking Life", Diagram =>
- @mind!
- @inout!
- @phys!
-
- @next!
- @phys '', 2
- @finish!
-
- \append addlabel "Dreaming", Diagram =>
- @mind!
- @phys!
- @finish!
-
- \append p "In both cases, the top of the diagram is fully occupied by the physical layer of reality, colored in green.
- This is due to the fact that, according to the materialistic theory of mind, human consciousness owes its existance
- to the physical and chemical dynamics of neurons in our brains. Therefore our mental reality must be considered
- fully embedded in the physical reality, and consequently it may only appear underneath it in the diagram."
-
- \append p "During waking life, we concern ourselves mostly with the physical reality surrounding us.
- For this reason the physical reality is placed in the lower right corner of the diagram as the layer holding the
- external world model relevant to the subject. Information flows in both directions between the physical world model
- and the subject's mental model, as denoted by the two white arrows: Information about the state of the world model
- enter the subjects mind via the senses (top arrow, pointing leftwards), and choices the subject makes inside of and
- based on his mental model can feed back into the physical layer through movements (lower arrow, pointing rightwards)."
-
- \append p "In the dreaming state on the other hand, the subject is unaware of the physical layer of reality, though
- the mind remains embedded inside it. When dreaming, subjects' mental models don't depend on external models, hence
- the mental layer of reality must be the only layer along the bottom of the diagram."
-
- \append with sect "Current Technologies"
- \append p "Since recent technological advancements have enabled the development of VR and AR consumer devices,
- AR and VR have been established as the potential next frontier of digital entertainment.", br!,
- "As the names imply, the notion of reality is at the core of both technologies.
- In the following section we will take a look at the respective stacks of both experience types:"
-
- \append with figures!
- \append addlabel "VR", Diagram =>
- @mind!
- @phys!
- @inout nil, 1
-
- @next!
- @phys '', 2
- @inout nil, 1
-
- @next!
- @digi!
- @phys ''
- @finish!
-
-
- \append addlabel "AR", Diagram =>
- @mind!
- @inout nil, 1.25
- @inn nil, 0.5
- @phys!
-
- @next!
- @phys '', 2
- @inn nil, .5
-
- @next!
- @digi nil, .5
- @phys '', 1.5
- @finish!
-
- \append p "In both cases we find the physical layer of reality as an ", (i "intermediate layer"), " between the mental
- and digital layers. Actions taken by the subject have to be acted out physically (corresponding to the
- information traversing the barrier between mental and physical reality) before they can be again digitized using
- the various tracking and input technologies (which in turn carry the information across the boundary of the physical
- and digital spaces)."
-
- \append p "The difference between AR and VR lies in the fact that in AR the subject experiences a mixture of the
- digital and physical world models. This can be seen in the diagram, where we find that right of the diagram origin
- and the mental model, the diagram splits and terminates in both layers: while information reaches the subject both
- from the digital reality through the physical one, as well as directly from the physical reality, the subject only
- directly manipulates state in the physical reality."
-
- \append p "The data conversions necessary at layer boundaries incur at the least losses in quality and accuracy of
- information for purely technical reasons. However ", (i "intermediate layers"), " come at a cost larger than just
- an additional step of conversion:
- For information to flow through a layer, it must be encodable within that layer’s world model.
- This means that the 'weakest link' in a given reality stack determines the upper bound of information possible to
- encode within said stack and thereby limits the overall expressivity of the stack.", br!,
- "As a practical example we can consider creating an hypothetical VR application that allows users to traverse a
- large virtual space by flying. While the human mind is perfectly capable of imagining to fly and control the motion
- appropriately, it is extremely hard to devise and implement a satisfying setup and control scheme because the
- physical body of the user needs to be taken into account and it, unlike the corresponding representations in the
- mental and digital world models, cannot float around freely."
-
- \append with sect "Future Developments"
- \append p "In the previous section we found that the presence of the physical layer in the information path of
- VR and AR stacks limits the experience as a whole. It follows that the removal of that indirection should be
- an obvious goal for future developments:"
-
- \append figures addlabel "holy grail of VR: 'The Matrix'", Diagram =>
- @mind!
- @inout!
- @phys!
-
- @next!
- @digi!
- @phys ''
- @finish!
-
- \append p "In the action movie 'The Matrix' ", (ref 'Matrix'), ", users of the titular VR environment interface with it
- by plugging cables into implanted sockets that connect the simulation directly to their central nervous system.", br!,
- "While these cables and implanted devices are physical devices, they don't constitute the presence of the
- physical layer of reality in the information path because while they do transmit information, the information
- remains in either the encoding of the mental model (neural firing patterns) or the encoding of the digital model
- (e.g. a numeric encoding of a player character's movement in digital space) and the conversion is made directly
- between those two - the data never assumes the native encoding of the physical layer (e.g. as a physical motion)."
-
- \append p "While we are currently far from being able to read arbitrary high-level information from the brain
- or to synthesize sensual input in human perception by bypassing the sensory organs, brain-computer interfaces (BCI)
- are a very active area of research with high hopes for comparable achievements in the near future."
-
- \append p "Applying this same step of removing the physical layer of reality from AR, we end up with something similar
- to the nano-particle drug in ", (i "Nexus"), " ", (ref 'Naam'), ". However this does not grant the user a similar
- amount of control over his experience as the holy grail of VR does, since the user and the physical part of the
- environment remain bound by the physical layer of reality's laws.", br!,
- "Instead the holy grail of AR is reached with the creation of a god machine that can manipulate the state of the
- physical world according to the user's wishes. In this way the digital and physical realities become unified and
- fully 'augmented'."
-
- \append with figures!
- \append addlabel "'Nexus'", Diagram =>
- @mind!
- @inout nil, 0.75
- @inout nil, 1.25
- @phys!
-
- @next!
- @digi nil, .5
- @phys '', 1.5
- @finish!
-
- \append addlabel "holy grail of AR: 'Deus Machina'", Diagram =>
- col = '#92807c'
-
- @mind!
- @inout!
- @block col, ''
-
- @next!
- @block col, '', 2
- @svg\plain('phys + digi')\attr(o fill: 'white', 'font-size': '14px')\move 6, -2 * GRID_H
- @finish!
-
- \append p "Despite the similarities of VR and AR, the two can be considered polar opposites, as becomes evident when
- we compare their respective utopian implementations: they share the goal of allowing us to experience realities
- different from the one we naturally inhabit, but while VR seeks to accomplish this by creating a new, nested reality
- inside ours, thus giving us full control over it.
- AR, on the other hand, is instead an attempt to retrofit our specific needs directly into the very reality we exist
- in.", br!,
- "This is in direct contrast with the popular notion of the 'reality-virtuality continuum' ", (ref 'Milgram'), ":
- the reality-virtuality continuum places common reality and VR (virtuality) as the two extreme poles, while AR
- is represented as an intermediate state between the two. Here however we propose to view instead AR and VR as the
- respective poles and find instead reality at the centerpoint, where the two opposing influences 'cancel out'."
-
- \append with sect "Conclusion and Further Work"
- \append p "In this paper we have proposed a taxonomy and visualization style for multi-reality experiences, as well
- as demonstrated it's flexibility by applying them as examples. Through the application of the proposed theory,
- we have also gained a new and contrasting view on preceding work such as the reality-virtuality-continuum.
- We have also found that the taxonomy can be used outside the research field of media studies and its use may extend
- as far as philosophy of consciousness (see Appendix below)."
-
- \append p "Further research could enhance the proposed theory with better and more concrete definitions.
- In the future, the proposed taxonomy might be used to create a more extensive and complete classification
- of reality stacks and to analyse the relationships between them."
-
- \append with sect 'References'
- \append references!
-
- \append with sect "Appendix: Relation to Theories of Mind"
- \append p "This paper starts from a deeply materialistic point of view that borders on microphysicalism.
- However it should be noted that the diagram style introduced above lends itself also to display other
- philosophical theories of mind. As an example, the following graphics show a typical VR stack as interpreted by
- Materialism, Cartesian Dualism and Solipsism respectively:"
-
- \append with figures!
- \append addlabel "VR in Materialism", Diagram =>
- @mind!
- @inout nil, 1
- @phys!
-
- @next!
- @phys '', 2
- @inout nil, 1
-
- @next!
- @digi!
- @phys ''
-
- @finish!
-
- \append addlabel "VR in Solipsism", Diagram =>
- @mind nil, 2
- @inout nil, 1
-
- @next!
- @digi!
- @mind ''
- @finish!
-
- \append addlabel "VR in Cartesian Dualism", Diagram =>
- @mind nil, 2
- @inout nil, 1
- @next!
-
- @phys nil, 2
- @inout nil, 1
- @next!
-
- @digi!
- @phys ''
- @finish!
-
- \append p "However these philosophical theories of minds also constitute reality stacks by themselves and as such can
- be compared directly:"
-
- \append with figures!
- \append addlabel "Materialism", Diagram =>
- @mind!
- @inout!
- @phys!
-
- @next!
- @phys '', 2
- @finish!
-
- \append addlabel "Solipsism", Diagram =>
- @mind!
- @finish!
-
- \append addlabel "Cartesian Dualism", Diagram =>
- @mind!
- @inout!
- @next!
-
- @phys!
- @finish!
-
-_content
diff --git a/root/meta/mmm.component/text$moonscript -> fn -> mmm$dom.moon b/root/meta/mmm.component/text$moonscript -> fn -> mmm$dom.moon
index ca9a13f..65556c3 100644
--- a/root/meta/mmm.component/text$moonscript -> fn -> mmm$dom.moon
+++ b/root/meta/mmm.component/text$moonscript -> fn -> mmm$dom.moon
@@ -15,10 +15,6 @@
demo = tohtml example!
div the_code, div demo, class: 'example'
-
-raw_find = (fileder, key_pat) ->
- for key, val in pairs fileder.facets
- return val if key\tostring!\match key_pat
=>
example = (name) ->
diff --git a/root/static/mmm/application$lua.lua b/root/static/mmm/application$lua.lua
new file mode 100644
index 0000000..5a6dc53
--- /dev/null
+++ b/root/static/mmm/application$lua.lua
@@ -0,0 +1,2701 @@
+local p = package.preload
+if not p["mmm.canvasapp"] then p["mmm.canvasapp"] = load("local window = js.global\
+local js = require('js')\
+local a, canvas, div, button, script\
+do\
+ local _obj_0 = require('mmm.dom')\
+ a, canvas, div, button, script = _obj_0.a, _obj_0.canvas, _obj_0.div, _obj_0.button, _obj_0.script\
+end\
+local CanvasApp\
+do\
+ local _class_0\
+ local _base_0 = {\
+ width = 500,\
+ height = 400,\
+ update = function(self, dt)\
+ self.time = self.time + dt\
+ if self.length and self.time > self.length then\
+ self.time = self.time - self.length\
+ return true\
+ end\
+ end,\
+ render = function(self, fps)\
+ if fps == nil then\
+ fps = 60\
+ end\
+ assert(self.length, 'cannot render CanvasApp without length set')\
+ self.paused = true\
+ local actual_render\
+ actual_render = function()\
+ local writer = js.new(window.Whammy.Video, fps)\
+ local doFrame\
+ doFrame = function()\
+ local done = self:update(1 / fps)\
+ self.ctx:resetTransform()\
+ self:draw()\
+ writer:add(self.canvas)\
+ if done or self.time >= self.length then\
+ local blob = writer:compile()\
+ local name = tostring(self.__class.__name) .. \"_\" .. tostring(fps) .. \"fps.webm\"\
+ return self.node.lastChild:appendChild(a(name, {\
+ download = name,\
+ href = window.URL:createObjectURL(blob)\
+ }))\
+ else\
+ return window:setTimeout(doFrame)\
+ end\
+ end\
+ self.time = 0\
+ return doFrame()\
+ end\
+ if window.Whammy then\
+ return actual_render()\
+ else\
+ window.global = window.global or window\
+ return document.body:appendChild(script({\
+ onload = actual_render,\
+ src = 'https://cdn.jsdelivr.net/npm/whammy@0.0.1/whammy.min.js'\
+ }))\
+ end\
+ end\
+ }\
+ _base_0.__index = _base_0\
+ _class_0 = setmetatable({\
+ __init = function(self, show_menu, paused)\
+ if show_menu == nil then\
+ show_menu = false\
+ end\
+ self.paused = paused\
+ self.canvas = canvas({\
+ width = self.width,\
+ height = self.height\
+ })\
+ self.ctx = self.canvas:getContext('2d')\
+ self.time = 0\
+ self.canvas.tabIndex = 0\
+ self.canvas:addEventListener('click', function(_, e)\
+ return self.click and self:click(e.offsetX, e.offsetY, e.button)\
+ end)\
+ self.canvas:addEventListener('keydown', function(_, e)\
+ return self.keydown and self:keydown(e.key, e.code)\
+ end)\
+ local lastMillis = window.performance:now()\
+ local animationFrame\
+ animationFrame = function(_, millis)\
+ self:update((millis - lastMillis) / 1000)\
+ self.ctx:resetTransform()\
+ self:draw()\
+ lastMillis = millis\
+ if not self.paused then\
+ return window:setTimeout((function()\
+ return window:requestAnimationFrame(animationFrame)\
+ end), 0)\
+ end\
+ end\
+ window:requestAnimationFrame(animationFrame)\
+ if show_menu then\
+ self.node = div({\
+ className = 'canvas_app',\
+ self.canvas,\
+ div({\
+ className = 'overlay',\
+ button('render 30fps', {\
+ onclick = function()\
+ return self:render(30)\
+ end\
+ }),\
+ button('render 60fps', {\
+ onclick = function()\
+ return self:render(60)\
+ end\
+ })\
+ })\
+ })\
+ else\
+ self.node = self.canvas\
+ end\
+ end,\
+ __base = _base_0,\
+ __name = \"CanvasApp\"\
+ }, {\
+ __index = _base_0,\
+ __call = function(cls, ...)\
+ local _self_0 = setmetatable({}, _base_0)\
+ cls.__init(_self_0, ...)\
+ return _self_0\
+ end\
+ })\
+ _base_0.__class = _class_0\
+ CanvasApp = _class_0\
+end\
+return {\
+ CanvasApp = CanvasApp\
+}\
+", "mmm/canvasapp.client.lua") end
+if not p["mmm.color"] then p["mmm.color"] = load("local rgb\
+rgb = function(r, g, b)\
+ if 'table' == type(r) then\
+ r, g, b = table.unpack(r)\
+ end\
+ return \"rgb(\" .. tostring(r * 255) .. \", \" .. tostring(g * 255) .. \", \" .. tostring(b * 255) .. \")\"\
+end\
+local rgba\
+rgba = function(r, g, b, a)\
+ if 'table' == type(r) then\
+ r, g, b, a = table.unpack(r)\
+ end\
+ return \"rgba(\" .. tostring(r * 255) .. \", \" .. tostring(g * 255) .. \", \" .. tostring(b * 255) .. \", \" .. tostring(a or 1) .. \")\"\
+end\
+local hsl\
+hsl = function(h, s, l)\
+ if 'table' == type(h) then\
+ h, s, l = table.unpack(h)\
+ end\
+ return \"hsl(\" .. tostring(h * 360) .. \", \" .. tostring(s * 100) .. \"%, \" .. tostring(l * 100) .. \"%)\"\
+end\
+local hsla\
+hsla = function(h, s, l, a)\
+ if 'table' == type(h) then\
+ h, s, l, a = table.unpack(h)\
+ end\
+ return \"hsla(\" .. tostring(h * 360) .. \", \" .. tostring(s * 100) .. \"%, \" .. tostring(l * 100) .. \"%, \" .. tostring(a or 1) .. \")\"\
+end\
+return {\
+ rgb = rgb,\
+ rgba = rgba,\
+ hsl = hsl,\
+ hsla = hsla\
+}\
+", "mmm/color.lua") end
+if not p["mmm.highlighting"] then p["mmm.highlighting"] = load("local code\
+code = require('mmm.dom').code\
+local highlight\
+local trim\
+trim = function(str)\
+ return str:match('^ *(..-) *$')\
+end\
+if MODE == 'SERVER' then\
+ highlight = function(lang, str)\
+ assert(str, 'no string to highlight')\
+ return code((trim(str)), {\
+ class = \"hljs lang-\" .. tostring(lang)\
+ })\
+ end\
+else\
+ highlight = function(lang, str)\
+ assert(str, 'no string to highlight')\
+ local result = window.hljs:highlight(lang, (trim(str)), true)\
+ do\
+ local _with_0 = code({\
+ class = \"hljs lang-\" .. tostring(lang)\
+ })\
+ _with_0.innerHTML = result.value\
+ return _with_0\
+ end\
+ end\
+end\
+local languages = setmetatable({ }, {\
+ __index = function(self, name)\
+ do\
+ local val\
+ val = function(str)\
+ return highlight(name, str)\
+ end\
+ self[name] = val\
+ return val\
+ end\
+ end\
+})\
+return {\
+ highlight = highlight,\
+ languages = languages\
+}\
+", "mmm/highlighting.lua") end
+if not p["mmm"] then p["mmm"] = load("window = js.global\
+local console\
+document, console = window.document, window.console\
+MODE = 'CLIENT'\
+local deep_tostring\
+deep_tostring = function(tbl, space)\
+ if space == nil then\
+ space = ''\
+ end\
+ if 'userdata' == type(tbl) then\
+ return tbl\
+ end\
+ local buf = space .. tostring(tbl)\
+ if not ('table' == type(tbl)) then\
+ return buf\
+ end\
+ buf = buf .. ' {\\n'\
+ for k, v in pairs(tbl) do\
+ buf = buf .. tostring(space) .. \" [\" .. tostring(k) .. \"]: \" .. tostring(deep_tostring(v, space .. ' ')) .. \"\\n\"\
+ end\
+ buf = buf .. tostring(space) .. \"}\"\
+ return buf\
+end\
+print = function(...)\
+ local contents\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ local _list_0 = {\
+ ...\
+ }\
+ for _index_0 = 1, #_list_0 do\
+ local v = _list_0[_index_0]\
+ _accum_0[_len_0] = deep_tostring(v)\
+ _len_0 = _len_0 + 1\
+ end\
+ contents = _accum_0\
+ end\
+ return console:log(table.unpack(contents))\
+end\
+warn = function(...)\
+ local contents\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ local _list_0 = {\
+ ...\
+ }\
+ for _index_0 = 1, #_list_0 do\
+ local v = _list_0[_index_0]\
+ _accum_0[_len_0] = deep_tostring(v)\
+ _len_0 = _len_0 + 1\
+ end\
+ contents = _accum_0\
+ end\
+ return console:warn(table.unpack(contents))\
+end\
+package.path = '/?.lua;/?/init.lua'\
+do\
+ local _require = require\
+ relative = function(base, sub)\
+ if not ('number' == type(sub)) then\
+ sub = 0\
+ end\
+ for i = 1, sub do\
+ base = base:match('^(.*)%.%w+$')\
+ end\
+ return function(name, x)\
+ if '.' == name:sub(1, 1) then\
+ name = base .. name\
+ end\
+ return _require(name)\
+ end\
+ end\
+end\
+if on_load then\
+ for _index_0 = 1, #on_load do\
+ local f = on_load[_index_0]\
+ f()\
+ end\
+end\
+on_load = setmetatable({ }, {\
+ __newindex = function(t, k, v)\
+ rawset(t, k, v)\
+ return v()\
+ end\
+})\
+", "mmm/init.client.lua") end
+if not p["mmm.ordered"] then p["mmm.ordered"] = load("local sort\
+sort = function(t, order_fn, only_strings)\
+ do\
+ local index\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for k, v in pairs(t) do\
+ if (not only_strings) or 'string' == type(k) then\
+ _accum_0[_len_0] = k\
+ _len_0 = _len_0 + 1\
+ end\
+ end\
+ index = _accum_0\
+ end\
+ table.sort(index, order_fn)\
+ return index\
+ end\
+end\
+local onext\
+onext = function(state, key)\
+ state.i = state.i + state.step\
+ local t, index, i\
+ t, index, i = state.t, state.index, state.i\
+ do\
+ key = index[i]\
+ if key then\
+ return key, t[key]\
+ end\
+ end\
+end\
+local opairs\
+opairs = function(t, order_fn, only_strings)\
+ if only_strings == nil then\
+ only_strings = false\
+ end\
+ local state = {\
+ t = t,\
+ i = 0,\
+ step = 1,\
+ index = sort(t, order_fn, only_strings)\
+ }\
+ return onext, state, nil\
+end\
+local ropairs\
+ropairs = function(t, order_fn, only_strings)\
+ if only_strings == nil then\
+ only_strings = false\
+ end\
+ local index = sort(t, order_fn, only_strings)\
+ local state = {\
+ t = t,\
+ index = index,\
+ i = #index + 1,\
+ step = -1\
+ }\
+ return onext, state, nil\
+end\
+return {\
+ onext = onext,\
+ opairs = opairs,\
+ ropairs = ropairs\
+}\
+", "mmm/ordered.lua") end
+if not p["mmm.mmmfs.browser"] then p["mmm.mmmfs.browser"] = load("local require = relative(..., 1)\
+local Key\
+Key = require('.fileder').Key\
+local converts, get_conversions, apply_conversions\
+do\
+ local _obj_0 = require('.conversion')\
+ converts, get_conversions, apply_conversions = _obj_0.converts, _obj_0.get_conversions, _obj_0.apply_conversions\
+end\
+local ReactiveVar, get_or_create, text, elements\
+do\
+ local _obj_0 = require('mmm.component')\
+ ReactiveVar, get_or_create, text, elements = _obj_0.ReactiveVar, _obj_0.get_or_create, _obj_0.text, _obj_0.elements\
+end\
+local pre, div, nav, span, button, a, code, select, option\
+pre, div, nav, span, button, a, code, select, option = elements.pre, elements.div, elements.nav, elements.span, elements.button, elements.a, elements.code, elements.select, elements.option\
+local languages\
+languages = require('mmm.highlighting').languages\
+local keep\
+keep = function(var)\
+ local last = var:get()\
+ return var:map(function(val)\
+ last = val or last\
+ return last\
+ end)\
+end\
+local code_cast\
+code_cast = function(lang)\
+ return {\
+ inp = \"text/\" .. tostring(lang) .. \".*\",\
+ out = 'mmm/dom',\
+ transform = function(val)\
+ return languages[lang](val)\
+ end\
+ }\
+end\
+local casts = {\
+ code_cast('javascript'),\
+ code_cast('moonscript'),\
+ code_cast('lua'),\
+ code_cast('markdown'),\
+ code_cast('html'),\
+ {\
+ inp = 'text/plain',\
+ out = 'mmm/dom',\
+ transform = function(val)\
+ return text(val)\
+ end\
+ },\
+ {\
+ inp = 'URL.*',\
+ out = 'mmm/dom',\
+ transform = function(href)\
+ return span(a((code(href)), {\
+ href = href\
+ }))\
+ end\
+ }\
+}\
+for _index_0 = 1, #converts do\
+ local convert = converts[_index_0]\
+ table.insert(casts, convert)\
+end\
+local Browser\
+do\
+ local _class_0\
+ local err_and_trace, default_convert\
+ local _base_0 = {\
+ get_content = function(self, prop, err, convert)\
+ if err == nil then\
+ err = self.error\
+ end\
+ if convert == nil then\
+ convert = default_convert\
+ end\
+ local clear_error\
+ clear_error = function()\
+ if MODE == 'CLIENT' then\
+ return err:set()\
+ end\
+ end\
+ local disp_error\
+ disp_error = function(msg)\
+ if MODE == 'CLIENT' then\
+ err:set(pre(msg))\
+ end\
+ warn(\"ERROR rendering content: \" .. tostring(msg))\
+ return nil\
+ end\
+ local active = self.active:get()\
+ if not (active) then\
+ return disp_error(\"fileder not found!\")\
+ end\
+ if not (prop) then\
+ return disp_error(\"facet not found!\")\
+ end\
+ local ok, res = xpcall(convert, err_and_trace, active, prop)\
+ if MODE == 'CLIENT' then\
+ document.body.classList:remove('loading')\
+ end\
+ if ok and res then\
+ clear_error()\
+ return res\
+ elseif ok then\
+ return div(\"[no conversion path to \" .. tostring(prop.type) .. \"]\")\
+ elseif res and res.msg.match and res.msg:match('%[nossr%]$') then\
+ return div(\"[this page could not be pre-rendered on the server]\")\
+ else\
+ res = tostring(res.msg) .. \"\\n\" .. tostring(res.trace)\
+ return disp_error(res)\
+ end\
+ end,\
+ get_inspector = function(self)\
+ self.inspect_prop = self.facet:map(function(prop)\
+ local active = self.active:get()\
+ local key = active and active:find(prop)\
+ if key and key.original then\
+ key = key.original\
+ end\
+ return key\
+ end)\
+ self.inspect_err = ReactiveVar()\
+ do\
+ local _with_0 = div({\
+ class = 'view inspector'\
+ })\
+ _with_0:append(nav({\
+ span('inspector'),\
+ self.inspect_prop:map(function(current)\
+ current = current and current:tostring()\
+ local fileder = self.active:get()\
+ local onchange\
+ onchange = function(_, e)\
+ if e.target.value == '' then\
+ return \
+ end\
+ local name\
+ name = self.facet:get().name\
+ return self.inspect_prop:set(Key(e.target.value))\
+ end\
+ do\
+ local _with_1 = select({\
+ onchange = onchange\
+ })\
+ _with_1:append(option('(none)', {\
+ value = '',\
+ disabled = true,\
+ selected = not value\
+ }))\
+ if fileder then\
+ for key, _ in pairs(fileder.facets) do\
+ local value = key:tostring()\
+ _with_1:append(option(value, {\
+ value = value,\
+ selected = value == current\
+ }))\
+ end\
+ end\
+ return _with_1\
+ end\
+ end),\
+ self.inspect:map(function(enabled)\
+ if enabled then\
+ return button('close', {\
+ onclick = function(_, e)\
+ return self.inspect:set(false)\
+ end\
+ })\
+ end\
+ end)\
+ }))\
+ _with_0:append((function()\
+ do\
+ local _with_1 = div({\
+ class = self.inspect_err:map(function(e)\
+ if e then\
+ return 'error-wrap'\
+ else\
+ return 'error-wrap empty'\
+ end\
+ end)\
+ })\
+ _with_1:append(span(\"an error occured while rendering this view:\"))\
+ _with_1:append(self.inspect_err)\
+ return _with_1\
+ end\
+ end)())\
+ _with_0:append((function()\
+ do\
+ local _with_1 = pre({\
+ class = 'content'\
+ })\
+ _with_1:append(keep(self.inspect_prop:map(function(prop, old)\
+ return self:get_content(prop, self.inspect_err, function(self, prop)\
+ local value, key = self:get(prop)\
+ assert(key, \"couldn't @get \" .. tostring(prop))\
+ local conversions = get_conversions('mmm/dom', key.type, casts)\
+ assert(conversions, \"cannot cast '\" .. tostring(key.type) .. \"'\")\
+ return apply_conversions(conversions, value, self, prop)\
+ end)\
+ end)))\
+ return _with_1\
+ end\
+ end)())\
+ return _with_0\
+ end\
+ end,\
+ navigate = function(self, new)\
+ return self.path:set(new)\
+ end\
+ }\
+ _base_0.__index = _base_0\
+ _class_0 = setmetatable({\
+ __init = function(self, root, path, rehydrate)\
+ if rehydrate == nil then\
+ rehydrate = false\
+ end\
+ self.root = root\
+ assert(self.root, 'root fileder is nil')\
+ self.path = ReactiveVar(path or '')\
+ if MODE == 'CLIENT' then\
+ local logo = document:querySelector('header h1 > svg')\
+ local spin\
+ spin = function()\
+ logo.classList:add('spin')\
+ local _ = logo.parentElement.offsetWidth\
+ return logo.classList:remove('spin')\
+ end\
+ self.path:subscribe(function(path)\
+ document.body.classList:add('loading')\
+ spin()\
+ if self.skip then\
+ return \
+ end\
+ local vis_path = path .. ((function()\
+ if '/' == path:sub(-1) then\
+ return ''\
+ else\
+ return '/'\
+ end\
+ end)())\
+ return window.history:pushState(path, '', vis_path)\
+ end)\
+ window.onpopstate = function(_, event)\
+ if event.state and not event.state == js.null then\
+ self.skip = true\
+ self.path:set(event.state)\
+ self.skip = nil\
+ end\
+ end\
+ end\
+ self.active = self.path:map((function()\
+ local _base_1 = self.root\
+ local _fn_0 = _base_1.walk\
+ return function(...)\
+ return _fn_0(_base_1, ...)\
+ end\
+ end)())\
+ self.facet = self.active:map(function(fileder)\
+ if not (fileder) then\
+ return \
+ end\
+ local last = self.facet and self.facet:get()\
+ return Key((function()\
+ if last then\
+ return last.type\
+ else\
+ return 'mmm/dom'\
+ end\
+ end)())\
+ end)\
+ self.inspect = ReactiveVar((MODE == 'CLIENT' and window.location.search:match('[?&]inspect')))\
+ local main = get_or_create('div', 'browser-root', {\
+ class = 'main view'\
+ })\
+ if MODE == 'SERVER' then\
+ main:append(nav({\
+ id = 'browser-navbar',\
+ span('please stand by... interactivity loading :)')\
+ }))\
+ else\
+ main:prepend((function()\
+ do\
+ local _with_0 = get_or_create('nav', 'browser-navbar')\
+ _with_0.node.innerHTML = ''\
+ _with_0:append(span('path: ', self.path:map(function(path)\
+ do\
+ local _with_1 = div({\
+ class = 'path',\
+ style = {\
+ display = 'inline-block'\
+ }\
+ })\
+ local path_segment\
+ path_segment = function(name, href)\
+ return a(name, {\
+ href = href,\
+ onclick = function(_, e)\
+ e:preventDefault()\
+ return self:navigate(href)\
+ end\
+ })\
+ end\
+ local href = ''\
+ path = path:match('^/(.*)')\
+ _with_1:append(path_segment('root', ''))\
+ while path do\
+ local name, rest = path:match('^([%w%-_%.]+)/(.*)')\
+ if not name then\
+ name = path\
+ end\
+ path = rest\
+ href = tostring(href) .. \"/\" .. tostring(name)\
+ _with_1:append('/')\
+ _with_1:append(path_segment(name, href))\
+ end\
+ return _with_1\
+ end\
+ end)))\
+ _with_0:append(span('view facet:', {\
+ style = {\
+ ['margin-right'] = '0'\
+ }\
+ }))\
+ _with_0:append(self.active:map(function(fileder)\
+ local onchange\
+ onchange = function(_, e)\
+ local type\
+ type = self.facet:get().type\
+ return self.facet:set(Key({\
+ name = e.target.value,\
+ type = type\
+ }))\
+ end\
+ local current = self.facet:get()\
+ current = current and current.name\
+ do\
+ local _with_1 = select({\
+ onchange = onchange,\
+ disabled = not fileder\
+ })\
+ local has_main = fileder and fileder:find(current.name, '.*')\
+ _with_1:append(option('(main)', {\
+ value = '',\
+ disabled = not has_main,\
+ selected = current == ''\
+ }))\
+ if fileder then\
+ for i, value in ipairs(fileder:get_facets()) do\
+ local _continue_0 = false\
+ repeat\
+ if value == '' then\
+ _continue_0 = true\
+ break\
+ end\
+ _with_1:append(option(value, {\
+ value = value,\
+ selected = value == current\
+ }))\
+ _continue_0 = true\
+ until true\
+ if not _continue_0 then\
+ break\
+ end\
+ end\
+ end\
+ return _with_1\
+ end\
+ end))\
+ _with_0:append(self.inspect:map(function(enabled)\
+ if not enabled then\
+ return button('inspect', {\
+ onclick = function(_, e)\
+ return self.inspect:set(true)\
+ end\
+ })\
+ end\
+ end))\
+ return _with_0\
+ end\
+ end)())\
+ end\
+ self.error = ReactiveVar()\
+ main:append((function()\
+ do\
+ local _with_0 = get_or_create('div', 'browser-error', {\
+ class = self.error:map(function(e)\
+ if e then\
+ return 'error-wrap'\
+ else\
+ return 'error-wrap empty'\
+ end\
+ end)\
+ })\
+ _with_0:append((span(\"an error occured while rendering this view:\")), (rehydrate and _with_0.node.firstChild))\
+ _with_0:append(self.error)\
+ return _with_0\
+ end\
+ end)())\
+ main:append((function()\
+ do\
+ local _with_0 = get_or_create('div', 'browser-content', {\
+ class = 'content'\
+ })\
+ local content = ReactiveVar((function()\
+ if rehydrate then\
+ return _with_0.node.lastChild\
+ else\
+ return self:get_content(self.facet:get())\
+ end\
+ end)())\
+ _with_0:append(keep(content))\
+ if MODE == 'CLIENT' then\
+ self.facet:subscribe(function(p)\
+ return window:setTimeout((function()\
+ return content:set(self:get_content(p))\
+ end), 150)\
+ end)\
+ end\
+ return _with_0\
+ end\
+ end)())\
+ if rehydrate then\
+ self.facet:set(self.facet:get())\
+ end\
+ local inspector = self.inspect:map(function(enabled)\
+ if enabled then\
+ return self:get_inspector()\
+ end\
+ end)\
+ local wrapper = get_or_create('div', 'browser-wrapper', main, inspector, {\
+ class = 'browser'\
+ })\
+ self.node = wrapper.node\
+ do\
+ local _base_1 = wrapper\
+ local _fn_0 = _base_1.render\
+ self.render = function(...)\
+ return _fn_0(_base_1, ...)\
+ end\
+ end\
+ end,\
+ __base = _base_0,\
+ __name = \"Browser\"\
+ }, {\
+ __index = _base_0,\
+ __call = function(cls, ...)\
+ local _self_0 = setmetatable({}, _base_0)\
+ cls.__init(_self_0, ...)\
+ return _self_0\
+ end\
+ })\
+ _base_0.__class = _class_0\
+ local self = _class_0\
+ err_and_trace = function(msg)\
+ return {\
+ msg = msg,\
+ trace = debug.traceback()\
+ }\
+ end\
+ default_convert = function(self, key)\
+ return self:get(key.name, 'mmm/dom')\
+ end\
+ default_convert = function(self, key)\
+ return self:get(key.name, 'mmm/dom')\
+ end\
+ Browser = _class_0\
+end\
+return {\
+ Browser = Browser\
+}\
+", "mmm/mmmfs/browser.lua") end
+if not p["mmm.mmmfs.conversion"] then p["mmm.mmmfs.conversion"] = load("local require = relative(..., 1)\
+local converts = require('.converts')\
+local count\
+count = function(base, pattern)\
+ if pattern == nil then\
+ pattern = '->'\
+ end\
+ return select(2, base:gsub(pattern, ''))\
+end\
+local escape_pattern\
+escape_pattern = function(inp)\
+ return \"^\" .. tostring(inp:gsub('([^%w])', '%%%1')) .. \"$\"\
+end\
+local escape_inp\
+escape_inp = function(inp)\
+ return \"^\" .. tostring(inp:gsub('([-/])', '%%%1')) .. \"$\"\
+end\
+local get_conversions\
+get_conversions = function(want, have, _converts, limit)\
+ if _converts == nil then\
+ _converts = converts\
+ end\
+ if limit == nil then\
+ limit = 5\
+ end\
+ assert(have, 'need starting type(s)')\
+ if 'string' == type(have) then\
+ have = {\
+ have\
+ }\
+ end\
+ assert(#have > 0, 'need starting type(s) (list was empty)')\
+ want = escape_pattern(want)\
+ local iterations = limit + math.max(table.unpack((function()\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for _index_0 = 1, #have do\
+ local type = have[_index_0]\
+ _accum_0[_len_0] = count(type)\
+ _len_0 = _len_0 + 1\
+ end\
+ return _accum_0\
+ end)()))\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for _index_0 = 1, #have do\
+ local start = have[_index_0]\
+ _accum_0[_len_0] = {\
+ start = start,\
+ rest = start,\
+ conversions = { }\
+ }\
+ _len_0 = _len_0 + 1\
+ end\
+ have = _accum_0\
+ end\
+ for i = 1, iterations do\
+ local next_have, c = { }, 1\
+ for _index_0 = 1, #have do\
+ local _des_0 = have[_index_0]\
+ local start, rest, conversions\
+ start, rest, conversions = _des_0.start, _des_0.rest, _des_0.conversions\
+ if rest:match(want) then\
+ return conversions, start\
+ else\
+ for _index_1 = 1, #_converts do\
+ local _continue_0 = false\
+ repeat\
+ local convert = _converts[_index_1]\
+ local inp = escape_inp(convert.inp)\
+ if not (rest:match(inp)) then\
+ _continue_0 = true\
+ break\
+ end\
+ local result = rest:gsub(inp, convert.out)\
+ if result then\
+ next_have[c] = {\
+ start = start,\
+ rest = result,\
+ conversions = {\
+ {\
+ convert = convert,\
+ from = rest,\
+ to = result\
+ },\
+ table.unpack(conversions)\
+ }\
+ }\
+ c = c + 1\
+ end\
+ _continue_0 = true\
+ until true\
+ if not _continue_0 then\
+ break\
+ end\
+ end\
+ end\
+ end\
+ have = next_have\
+ if not (#have > 0) then\
+ return \
+ end\
+ end\
+end\
+local apply_conversions\
+apply_conversions = function(conversions, value, ...)\
+ for i = #conversions, 1, -1 do\
+ local step = conversions[i]\
+ value = step.convert.transform(step, value, ...)\
+ end\
+ return value\
+end\
+return {\
+ converts = converts,\
+ get_conversions = get_conversions,\
+ apply_conversions = apply_conversions\
+}\
+", "mmm/mmmfs/conversion.lua") end
+if not p["mmm.mmmfs.fileder"] then p["mmm.mmmfs.fileder"] = load("local require = relative(..., 1)\
+local get_conversions, apply_conversions\
+do\
+ local _obj_0 = require('.conversion')\
+ get_conversions, apply_conversions = _obj_0.get_conversions, _obj_0.apply_conversions\
+end\
+local Key\
+do\
+ local _class_0\
+ local _base_0 = {\
+ tostring = function(self)\
+ if self.name == '' then\
+ return self.type\
+ else\
+ return tostring(self.name) .. \": \" .. tostring(self.type)\
+ end\
+ end,\
+ __tostring = function(self)\
+ return self:tostring()\
+ end\
+ }\
+ _base_0.__index = _base_0\
+ _class_0 = setmetatable({\
+ __init = function(self, opts, second)\
+ if 'string' == type(second) then\
+ self.name, self.type = (opts or ''), second\
+ elseif 'string' == type(opts) then\
+ self.name, self.type = opts:match('^([%w-_]+): *(.+)$')\
+ if not self.name then\
+ self.name = ''\
+ self.type = opts\
+ end\
+ elseif 'table' == type(opts) then\
+ self.name = opts.name\
+ self.type = opts.type\
+ self.original = opts.original\
+ self.filename = opts.filename\
+ else\
+ return error(\"wrong argument type: \" .. tostring(type(opts)) .. \", \" .. tostring(type(second)))\
+ end\
+ end,\
+ __base = _base_0,\
+ __name = \"Key\"\
+ }, {\
+ __index = _base_0,\
+ __call = function(cls, ...)\
+ local _self_0 = setmetatable({}, _base_0)\
+ cls.__init(_self_0, ...)\
+ return _self_0\
+ end\
+ })\
+ _base_0.__class = _class_0\
+ Key = _class_0\
+end\
+local Fileder\
+do\
+ local _class_0\
+ local _base_0 = {\
+ walk = function(self, path)\
+ if path == '' then\
+ return self\
+ end\
+ if '/' ~= path:sub(1, 1) then\
+ path = tostring(self.path) .. \"/\" .. tostring(path)\
+ end\
+ if not (self.path == path:sub(1, #self.path)) then\
+ return \
+ end\
+ if #path == #self.path then\
+ return self\
+ end\
+ local _list_0 = self.children\
+ for _index_0 = 1, #_list_0 do\
+ local child = _list_0[_index_0]\
+ do\
+ local match = child:walk(path)\
+ if match then\
+ return match\
+ end\
+ end\
+ end\
+ end,\
+ mount = function(self, path, mount_as)\
+ if not mount_as then\
+ path = path .. self:gett('name: alpha')\
+ end\
+ assert(not self.path or self.path == path, \"mounted twice: \" .. tostring(self.path) .. \" and now \" .. tostring(path))\
+ self.path = path\
+ local _list_0 = self.children\
+ for _index_0 = 1, #_list_0 do\
+ local child = _list_0[_index_0]\
+ child:mount(self.path .. '/')\
+ end\
+ end,\
+ iterate = function(self, depth)\
+ if depth == nil then\
+ depth = 0\
+ end\
+ coroutine.yield(self)\
+ if depth == 1 then\
+ return \
+ end\
+ local _list_0 = self.children\
+ for _index_0 = 1, #_list_0 do\
+ local child = _list_0[_index_0]\
+ child:iterate(depth - 1)\
+ end\
+ end,\
+ get_facets = function(self)\
+ local names = { }\
+ for key in pairs(self.facets) do\
+ names[key.name] = true\
+ end\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for name in pairs(names) do\
+ _accum_0[_len_0] = name\
+ _len_0 = _len_0 + 1\
+ end\
+ return _accum_0\
+ end,\
+ has = function(self, ...)\
+ local want = Key(...)\
+ for key in pairs(self.facets) do\
+ local _continue_0 = false\
+ repeat\
+ if key.original then\
+ _continue_0 = true\
+ break\
+ end\
+ if key.name == want.name and key.type == want.type then\
+ return key\
+ end\
+ _continue_0 = true\
+ until true\
+ if not _continue_0 then\
+ break\
+ end\
+ end\
+ end,\
+ has_facet = function(self, want)\
+ for key in pairs(self.facets) do\
+ local _continue_0 = false\
+ repeat\
+ if key.original then\
+ _continue_0 = true\
+ break\
+ end\
+ if key.name == want then\
+ return key\
+ end\
+ _continue_0 = true\
+ until true\
+ if not _continue_0 then\
+ break\
+ end\
+ end\
+ end,\
+ find = function(self, ...)\
+ local want = Key(...)\
+ local matching\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for key in pairs(self.facets) do\
+ if key.name == want.name then\
+ _accum_0[_len_0] = key\
+ _len_0 = _len_0 + 1\
+ end\
+ end\
+ matching = _accum_0\
+ end\
+ if not (#matching > 0) then\
+ return \
+ end\
+ local shortest_path, start = get_conversions(want.type, (function()\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for _index_0 = 1, #matching do\
+ local key = matching[_index_0]\
+ _accum_0[_len_0] = key.type\
+ _len_0 = _len_0 + 1\
+ end\
+ return _accum_0\
+ end)())\
+ if start then\
+ for _index_0 = 1, #matching do\
+ local key = matching[_index_0]\
+ if key.type == start then\
+ return key, shortest_path\
+ end\
+ end\
+ return error(\"couldn't find key after resolution?\")\
+ end\
+ end,\
+ get = function(self, ...)\
+ local want = Key(...)\
+ local key, conversions = self:find(want)\
+ if key then\
+ local value = apply_conversions(conversions, self.facets[key], self, key)\
+ return value, key\
+ end\
+ end,\
+ gett = function(self, ...)\
+ local want = Key(...)\
+ local value, key = self:get(want)\
+ assert(value, tostring(self) .. \" doesn't have value for '\" .. tostring(want) .. \"'\")\
+ return value, key\
+ end,\
+ __tostring = function(self)\
+ return \"Fileder:\" .. tostring(self.path)\
+ end\
+ }\
+ _base_0.__index = _base_0\
+ _class_0 = setmetatable({\
+ __init = function(self, facets, children)\
+ if not children then\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for i, child in ipairs(facets) do\
+ facets[i] = nil\
+ local _value_0 = child\
+ _accum_0[_len_0] = _value_0\
+ _len_0 = _len_0 + 1\
+ end\
+ children = _accum_0\
+ end\
+ end\
+ self.children = setmetatable({ }, {\
+ __index = function(t, k)\
+ if not ('string' == type(k)) then\
+ return rawget(t, k)\
+ end\
+ return self:walk(tostring(self.path) .. \"/\" .. tostring(k))\
+ end,\
+ __newindex = function(t, k, child)\
+ rawset(t, k, child)\
+ if self.path == '/' then\
+ return child:mount('/')\
+ elseif self.path then\
+ return child:mount(self.path .. '/')\
+ end\
+ end\
+ })\
+ for i, child in ipairs(children) do\
+ self.children[i] = child\
+ end\
+ self.facets = setmetatable({ }, {\
+ __newindex = function(t, key, v)\
+ return rawset(t, (Key(key)), v)\
+ end\
+ })\
+ for k, v in pairs(facets) do\
+ self.facets[k] = v\
+ end\
+ end,\
+ __base = _base_0,\
+ __name = \"Fileder\"\
+ }, {\
+ __index = _base_0,\
+ __call = function(cls, ...)\
+ local _self_0 = setmetatable({}, _base_0)\
+ cls.__init(_self_0, ...)\
+ return _self_0\
+ end\
+ })\
+ _base_0.__class = _class_0\
+ Fileder = _class_0\
+end\
+local dir_base\
+dir_base = function(path)\
+ local dir, base = path:match('(.-)([^/]-)$')\
+ if dir and #dir > 0 then\
+ dir = dir:sub(1, #dir - 1)\
+ end\
+ return dir, base\
+end\
+local load_tree\
+load_tree = function(store, root)\
+ if root == nil then\
+ root = ''\
+ end\
+ local fileders = setmetatable({ }, {\
+ __index = function(self, path)\
+ do\
+ local val = Fileder({ })\
+ val.path = path\
+ rawset(self, path, val)\
+ return val\
+ end\
+ end\
+ })\
+ root = fileders[root]\
+ root.facets['name: alpha'] = ''\
+ for fn, ft in store:list_facets(root.path) do\
+ local val = store:load_facet(root.path, fn, ft)\
+ root.facets[Key(fn, ft)] = val\
+ end\
+ for path in store:list_all_fileders(root.path) do\
+ local fileder = fileders[path]\
+ local parent, name = dir_base(path)\
+ fileder.facets['name: alpha'] = name\
+ table.insert(fileders[parent].children, fileder)\
+ for fn, ft in store:list_facets(path) do\
+ local val = store:load_facet(path, fn, ft)\
+ fileder.facets[Key(fn, ft)] = val\
+ end\
+ end\
+ return root\
+end\
+return {\
+ Key = Key,\
+ Fileder = Fileder,\
+ dir_base = dir_base,\
+ load_tree = load_tree\
+}\
+", "mmm/mmmfs/fileder.lua") end
+if not p["mmm.mmmfs"] then p["mmm.mmmfs"] = load("local require = relative(...)\
+local Key, Fileder\
+do\
+ local _obj_0 = require('.fileder')\
+ Key, Fileder = _obj_0.Key, _obj_0.Fileder\
+end\
+local Browser\
+Browser = require('.browser').Browser\
+return {\
+ Key = Key,\
+ Fileder = Fileder,\
+ Browser = Browser\
+}\
+", "mmm/mmmfs/init.lua") end
+if not p["mmm.mmmfs.util"] then p["mmm.mmmfs.util"] = load("local merge\
+merge = function(orig, extra)\
+ if orig == nil then\
+ orig = { }\
+ end\
+ do\
+ local attr\
+ do\
+ local _tbl_0 = { }\
+ for k, v in pairs(orig) do\
+ _tbl_0[k] = v\
+ end\
+ attr = _tbl_0\
+ end\
+ for k, v in pairs(extra) do\
+ attr[k] = v\
+ end\
+ return attr\
+ end\
+end\
+local tourl\
+tourl = function(path)\
+ if STATIC then\
+ return path .. '/'\
+ else\
+ return path .. '/'\
+ end\
+end\
+return function(elements)\
+ local a, div, pre\
+ a, div, pre = elements.a, elements.div, elements.pre\
+ local find_fileder\
+ find_fileder = function(fileder, origin)\
+ if 'string' == type(fileder) then\
+ assert(origin, \"cannot resolve path '\" .. tostring(fileder) .. \"' without origin!\")\
+ return assert((origin:walk(fileder)), \"couldn't resolve path '\" .. tostring(fileder) .. \"' from \" .. tostring(origin))\
+ else\
+ return assert(fileder, \"no fileder passed.\")\
+ end\
+ end\
+ local navigate_to\
+ navigate_to = function(path, name, opts)\
+ if opts == nil then\
+ opts = { }\
+ end\
+ opts.href = tourl(path)\
+ if MODE == 'CLIENT' then\
+ opts.onclick = function(self, e)\
+ e:preventDefault()\
+ return BROWSER:navigate(path)\
+ end\
+ end\
+ return a(name, opts)\
+ end\
+ local link_to\
+ link_to = function(fileder, name, origin, attr)\
+ fileder = find_fileder(fileder, origin)\
+ name = name or fileder:get('title: mmm/dom')\
+ name = name or fileder:gett('name: alpha')\
+ do\
+ local href = fileder:get('link: URL.*')\
+ if href then\
+ return a(name, merge(attr, {\
+ href = href,\
+ target = '_blank'\
+ }))\
+ else\
+ return a(name, merge(attr, {\
+ href = tourl(fileder.path),\
+ onclick = (function()\
+ if MODE == 'CLIENT' then\
+ return function(self, e)\
+ e:preventDefault()\
+ return BROWSER:navigate(fileder.path)\
+ end\
+ end\
+ end)()\
+ }))\
+ end\
+ end\
+ end\
+ local embed\
+ embed = function(fileder, name, origin, opts)\
+ if name == nil then\
+ name = ''\
+ end\
+ if opts == nil then\
+ opts = { }\
+ end\
+ fileder = find_fileder(fileder, origin)\
+ local ok, node = pcall(fileder.gett, fileder, name, 'mmm/dom')\
+ if not ok then\
+ return div(\"couldn't embed \" .. tostring(fileder) .. \" \" .. tostring(name), (pre(node)), {\
+ style = {\
+ background = 'var(--gray-fail)',\
+ padding = '1em'\
+ }\
+ })\
+ end\
+ local klass = 'embed'\
+ if opts.desc then\
+ klass = klass .. ' desc'\
+ end\
+ if opts.inline then\
+ klass = klass .. ' inline'\
+ end\
+ node = div({\
+ class = klass,\
+ node,\
+ (function()\
+ if opts.desc then\
+ return div(opts.desc, {\
+ class = 'description'\
+ })\
+ end\
+ end)()\
+ })\
+ if opts.nolink then\
+ return node\
+ end\
+ return link_to(fileder, node, nil, opts.attr)\
+ end\
+ return {\
+ find_fileder = find_fileder,\
+ link_to = link_to,\
+ navigate_to = navigate_to,\
+ embed = embed\
+ }\
+end\
+", "mmm/mmmfs/util.lua") end
+if not p["mmm.mmmfs.converts"] then p["mmm.mmmfs.converts"] = load("local require = relative(..., 1)\
+local div, code, img, video, blockquote, a, span, source, iframe\
+do\
+ local _obj_0 = require('mmm.dom')\
+ div, code, img, video, blockquote, a, span, source, iframe = _obj_0.div, _obj_0.code, _obj_0.img, _obj_0.video, _obj_0.blockquote, _obj_0.a, _obj_0.span, _obj_0.source, _obj_0.iframe\
+end\
+local find_fileder, link_to, embed\
+do\
+ local _obj_0 = (require('mmm.mmmfs.util'))(require('mmm.dom'))\
+ find_fileder, link_to, embed = _obj_0.find_fileder, _obj_0.link_to, _obj_0.embed\
+end\
+local render\
+render = require('.layout').render\
+local tohtml\
+tohtml = require('mmm.component').tohtml\
+local js_fix\
+if MODE == 'CLIENT' then\
+ js_fix = function(arg)\
+ if arg == js.null then\
+ return \
+ end\
+ return arg\
+ end\
+end\
+local single\
+single = function(func)\
+ return function(self, val)\
+ return func(val)\
+ end\
+end\
+local loadwith\
+loadwith = function(_load)\
+ return function(self, val, fileder, key)\
+ local func = assert(_load(val, tostring(fileder) .. \"#\" .. tostring(key)))\
+ return func()\
+ end\
+end\
+local converts = {\
+ {\
+ inp = 'fn -> (.+)',\
+ out = '%1',\
+ transform = function(self, val, fileder)\
+ return val(fileder)\
+ end\
+ },\
+ {\
+ inp = 'mmm/component',\
+ out = 'mmm/dom',\
+ transform = single(tohtml)\
+ },\
+ {\
+ inp = 'mmm/dom',\
+ out = 'text/html+frag',\
+ transform = function(self, node)\
+ if MODE == 'SERVER' then\
+ return node\
+ else\
+ return node.outerHTML\
+ end\
+ end\
+ },\
+ {\
+ inp = 'mmm/dom',\
+ out = 'text/html',\
+ transform = function(self, html, fileder)\
+ return render(html, fileder)\
+ end\
+ },\
+ {\
+ inp = 'text/html%+frag',\
+ out = 'mmm/dom',\
+ transform = (function()\
+ if MODE == 'SERVER' then\
+ return function(self, html, fileder)\
+ html = html:gsub('(.-)', function(attrs, text)\
+ if #text == 0 then\
+ text = nil\
+ end\
+ local path = ''\
+ while attrs and attrs ~= '' do\
+ local key, val, _attrs = attrs:match('^(%w+)=\"([^\"]-)\"%s*(.*)')\
+ if not key then\
+ key, _attrs = attrs:match('^(%w+)%s*(.*)$')\
+ val = true\
+ end\
+ attrs = _attrs\
+ local _exp_0 = key\
+ if 'path' == _exp_0 then\
+ path = val\
+ else\
+ warn(\"unkown attribute '\" .. tostring(key) .. \"=\\\"\" .. tostring(val) .. \"\\\"' in \")\
+ end\
+ end\
+ return link_to(path, text, fileder)\
+ end)\
+ html = html:gsub('(.-)', function(attrs, desc)\
+ local path, facet = '', ''\
+ local opts = { }\
+ if #desc ~= 0 then\
+ opts.desc = desc\
+ end\
+ while attrs and attrs ~= '' do\
+ local key, val, _attrs = attrs:match('^(%w+)=\"([^\"]-)\"%s*(.*)')\
+ if not key then\
+ key, _attrs = attrs:match('^(%w+)%s*(.*)$')\
+ val = true\
+ end\
+ attrs = _attrs\
+ local _exp_0 = key\
+ if 'path' == _exp_0 then\
+ path = val\
+ elseif 'facet' == _exp_0 then\
+ facet = val\
+ elseif 'nolink' == _exp_0 then\
+ opts.nolink = true\
+ elseif 'inline' == _exp_0 then\
+ opts.inline = true\
+ else\
+ warn(\"unkown attribute '\" .. tostring(key) .. \"=\\\"\" .. tostring(val) .. \"\\\"' in \")\
+ end\
+ end\
+ return embed(path, facet, fileder, opts)\
+ end)\
+ return html\
+ end\
+ else\
+ return function(self, html, fileder)\
+ local parent\
+ do\
+ local _with_0 = document:createElement('div')\
+ _with_0.innerHTML = html\
+ local embeds = _with_0:getElementsByTagName('mmm-embed')\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for i = 0, embeds.length - 1 do\
+ _accum_0[_len_0] = embeds[i]\
+ _len_0 = _len_0 + 1\
+ end\
+ embeds = _accum_0\
+ end\
+ for _index_0 = 1, #embeds do\
+ local element = embeds[_index_0]\
+ local path = js_fix(element:getAttribute('path'))\
+ local facet = js_fix(element:getAttribute('facet'))\
+ local nolink = js_fix(element:getAttribute('nolink'))\
+ local inline = js_fix(element:getAttribute('inline'))\
+ local desc = js_fix(element.innerText)\
+ element:replaceWith(embed(path or '', facet or '', fileder, {\
+ nolink = nolink,\
+ inline = inline,\
+ desc = desc\
+ }))\
+ end\
+ embeds = _with_0:getElementsByTagName('mmm-link')\
+ do\
+ local _accum_0 = { }\
+ local _len_0 = 1\
+ for i = 0, embeds.length - 1 do\
+ _accum_0[_len_0] = embeds[i]\
+ _len_0 = _len_0 + 1\
+ end\
+ embeds = _accum_0\
+ end\
+ for _index_0 = 1, #embeds do\
+ local element = embeds[_index_0]\
+ local text = js_fix(element.innerText)\
+ local path = js_fix(element:getAttribute('path'))\
+ element:replaceWith(link_to(path or '', text, fileder))\
+ end\
+ parent = _with_0\
+ end\
+ assert(1 == parent.childElementCount, \"text/html with more than one child!\")\
+ return parent.firstElementChild\
+ end\
+ end\
+ end)()\
+ },\
+ {\
+ inp = 'text/lua -> (.+)',\
+ out = '%1',\
+ transform = loadwith(load or loadstring)\
+ },\
+ {\
+ inp = 'mmm/tpl -> (.+)',\
+ out = '%1',\
+ transform = function(self, source, fileder)\
+ return source:gsub('{{(.-)}}', function(expr)\
+ local path, facet = expr:match('^([%w%-_%./]*)%+(.*)')\
+ assert(path, \"couldn't match TPL expression '\" .. tostring(expr) .. \"'\")\
+ return (find_fileder(path, fileder)):gett(facet)\
+ end)\
+ end\
+ },\
+ {\
+ inp = 'time/iso8601-date',\
+ out = 'time/unix',\
+ transform = function(self, val)\
+ local year, _, month, day = val:match('^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$')\
+ assert(year, \"failed to parse ISO 8601 date: '\" .. tostring(val) .. \"'\")\
+ return os.time({\
+ year = year,\
+ month = month,\
+ day = day\
+ })\
+ end\
+ },\
+ {\
+ inp = 'URL -> twitter/tweet',\
+ out = 'mmm/dom',\
+ transform = function(self, href)\
+ local id = assert((href:match('twitter.com/[^/]-/status/(%d*)')), \"couldn't parse twitter/tweet URL: '\" .. tostring(href) .. \"'\")\
+ if MODE == 'CLIENT' then\
+ do\
+ local parent = div()\
+ window.twttr.widgets:createTweet(id, parent)\
+ return parent\
+ end\
+ else\
+ return div(blockquote({\
+ class = 'twitter-tweet',\
+ ['data-lang'] = 'en',\
+ a('(linked tweet)', {\
+ href = href\
+ })\
+ }))\
+ end\
+ end\
+ },\
+ {\
+ inp = 'URL -> youtube/video',\
+ out = 'mmm/dom',\
+ transform = function(self, link)\
+ local id = link:match('youtu%.be/([^/]+)')\
+ id = id or link:match('youtube.com/watch.*[?&]v=([^&]+)')\
+ id = id or link:match('youtube.com/[ev]/([^/]+)')\
+ id = id or link:match('youtube.com/embed/([^/]+)')\
+ assert(id, \"couldn't parse youtube URL: '\" .. tostring(link) .. \"'\")\
+ return iframe({\
+ width = 560,\
+ height = 315,\
+ frameborder = 0,\
+ allowfullscreen = true,\
+ frameBorder = 0,\
+ src = \"//www.youtube.com/embed/\" .. tostring(id)\
+ })\
+ end\
+ },\
+ {\
+ inp = 'URL -> image/.+',\
+ out = 'mmm/dom',\
+ transform = function(self, src, fileder)\
+ return img({\
+ src = src\
+ })\
+ end\
+ },\
+ {\
+ inp = 'URL -> video/.+',\
+ out = 'mmm/dom',\
+ transform = function(self, src)\
+ return video((source({\
+ src = src\
+ })), {\
+ controls = true,\
+ loop = true\
+ })\
+ end\
+ },\
+ {\
+ inp = 'text/plain',\
+ out = 'mmm/dom',\
+ transform = function(self, val)\
+ return span(val)\
+ end\
+ },\
+ {\
+ inp = 'alpha',\
+ out = 'mmm/dom',\
+ transform = single(code)\
+ },\
+ {\
+ inp = '(.+)',\
+ out = 'URL -> %1',\
+ transform = function(self, _, fileder, key)\
+ return tostring(fileder.path) .. \"/\" .. tostring(key.name) .. \":\" .. tostring(self.from)\
+ end\
+ }\
+}\
+if MODE == 'SERVER' then\
+ local ok, moon = pcall(require, 'moonscript.base')\
+ if ok then\
+ local _load = moon.load or moon.loadstring\
+ table.insert(converts, {\
+ inp = 'text/moonscript -> (.+)',\
+ out = '%1',\
+ transform = loadwith(moon.load or moon.loadstring)\
+ })\
+ table.insert(converts, {\
+ inp = 'text/moonscript -> (.+)',\
+ out = 'text/lua -> %1',\
+ transform = single(moon.to_lua)\
+ })\
+ end\
+else\
+ table.insert(converts, {\
+ inp = 'text/javascript -> (.+)',\
+ out = '%1',\
+ transform = function(self, source)\
+ local f = js.new(window.Function, source)\
+ return f()\
+ end\
+ })\
+end\
+do\
+ local markdown\
+ if MODE == 'SERVER' then\
+ local success, discount = pcall(require, 'discount')\
+ if not success then\
+ warn(\"NO MARKDOWN SUPPORT!\", discount)\
+ end\
+ markdown = success and function(md)\
+ local res = assert(discount.compile(md, 'githubtags'))\
+ return res.body\
+ end\
+ else\
+ markdown = window and window.marked and (function()\
+ local _base_0 = window\
+ local _fn_0 = _base_0.marked\
+ return function(...)\
+ return _fn_0(_base_0, ...)\
+ end\
+ end)()\
+ end\
+ if markdown then\
+ table.insert(converts, {\
+ inp = 'text/markdown',\
+ out = 'text/html+frag',\
+ transform = function(self, md)\
+ return \"
\" .. tostring(markdown(md)) .. \"
\"\
+ end\
+ })\
+ table.insert(converts, {\
+ inp = 'text/markdown%+span',\
+ out = 'mmm/dom',\
+ transform = (function()\
+ if MODE == 'SERVER' then\
+ return function(self, source)\
+ local html = markdown(source)\
+ html = html:gsub('^
$', '/span>')\
+ end\
+ else\
+ return function(self, source)\
+ local html = markdown(source)\
+ html = html:gsub('^%s*