EditorSim.icl 55.5 KB
Newer Older
1 2
module EditorSim
/**
3
* Simulation of messages passing between editors to figure out the design details
4 5
*/
import iTasks
6
import Data.Maybe, Data.Either, Data.Tuple, Data.Functor, Data.List, Data.Func, Data.Error
7
import qualified Data.Map as DM
8
import Text, Text.HTML
9 10 11
from StdFunc import seqList, :: St(..)
from Data.Foldable import maximumBy
import StdArray
12
import Data.Map.GenJSON
13

14
import qualified Graphics.Scalable.Image as GI
15 16 17
from Graphics.Scalable.Image import :: FillAttr(..), <@<,  :: Image, :: Host(..)
from Graphics.Scalable.Image import class tuneImage, instance tuneImage FillAttr
from Graphics.Scalable.Image import class margin, instance margin (!Span, !Span, !Span, !Span), instance margin Span
18
import iTasks.Extensions.SVG.SVGEditor
19
import StdMisc, StdDebug
20 21 22 23 24 25 26

//TODOS:
// - Model an example with input field validation for mandatory fields etc. such as 'empty','validated', etc

//PROBLEMS:
// - Link methods not complete enough yet... (impossible to react to refreshes on server...)

27
//Definition of editors
28 29 30 31 32 33

// p: The parameter set (same as in sds)
// r: Data read from an external source (sds) based on p. Data will arrive with some delay and can be updated later
// w: Data to be written back to the external source (sds)

// s: Server-side state
34
// c: Client-side state
35 36 37
// m: Messages exchanged to synchronize between client and server (and potentially between sub editors)

:: NxtEditor p r w s c m =
38
  { client :: NxtEditorClient c m
39
  , server :: NxtEditorServer p r w s c m
40 41 42
  }

:: NxtEditorClient c m =
43 44
  { init      :: (Maybe c) -> NxtDOM
  , state     :: NxtDOM -> c
45 46
  , onEvent   :: NxtDOMRef String NxtDOM -> ([NxtClientOutMessage m],NxtDOM)
  , onMessage :: (NxtClientInMessage m) NxtDOM -> ([NxtClientOutMessage m],NxtDOM)
47 48
  }

49
:: NxtEditorServer p r w s c m =
50
  { init      :: p NxtMajorVersion -> (s, NxtVersionTree, c)
51 52
  , parameter :: s -> p
  , value     :: s -> Maybe w
53 54
  , onRefresh :: r NxtMajorVersion s NxtVersionTree -> ([NxtServerOutMessage m], s, NxtVersionTree, NxtWrite) 
  , onMessage :: (NxtServerInMessage m) s NxtVersionTree -> ([NxtServerOutMessage m], s, NxtVersionTree, Bool)
55 56
  }

57
:: NxtWrite :== Bool //The Bool is a 'write' signal that indicates if something significant has changed
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
:: NxtVersion :== (!NxtMajorVersion,!NxtMinorVersion) //First: which read from sds, Second: which revision by edits
:: NxtMajorVersion :== Int
:: NxtMinorVersion :== Int

//Together with the served side state we need to track all versions explicitly
//(the client versions are encoded in the DOM)
:: NxtVersionTree
	= NVTBasic     !NxtVersion
	| NVTGlue      !NxtVersionTree !NxtVersionTree
	| NVTMultiple  !NxtVersion [NxtVersionTree]

//Messages commonly only affect part of a datastructure, so no complete version tree is communicated
:: NxtPartialVersionTree 
	= NVPVersion !(Maybe NxtVersion) ![(Int,NxtPartialVersionTree)]

:: NxtWithPartialVersion m = { message :: m, version :: NxtPartialVersionTree }
:: NxtWithPartialVersions m = { message :: m, oldVersion :: NxtPartialVersionTree, newVersion :: NxtPartialVersionTree}

:: NxtClientInMessage m
	= NxtClientInRemote m NxtPartialVersionTree NxtPartialVersionTree //(message, oldVersion, newVersion)
	| NxtClientInLocal m //local loopback on client

:: NxtClientOutMessage m = NxtClientOut m NxtPartialVersionTree //(message, version)

:: NxtServerInMessage m
	= NxtServerInRemote m NxtPartialVersionTree
	| NxtServerInLocal m NxtMajorVersion //When we feedback messages, we need to pass along the major version of message that created the feedback

:: NxtServerOutMessage m = NxtServerOut m NxtPartialVersionTree NxtPartialVersionTree

89 90 91 92 93 94 95 96 97
instance Functor NxtServerInMessage
where
	fmap f (NxtServerInRemote m v) = NxtServerInRemote (f m) v
	fmap f (NxtServerInLocal m v) = NxtServerInLocal (f m) v

instance Functor NxtClientInMessage
where
	fmap f (NxtClientInRemote m ov nv) = NxtClientInRemote (f m) ov nv
	fmap f (NxtClientInLocal m) = NxtClientInLocal (f m)
98 99 100 101 102 103 104 105 106 107 108 109

//TODO: this is not minimal enough: We create a partial version info structure, but it actually contains all versions!
toPartialVersion (NVTBasic v) = NVPVersion (Just v) []
toPartialVersion (NVTGlue v1 v2) = NVPVersion Nothing [(0,toPartialVersion v1),(1,toPartialVersion v2)]
toPartialVersion (NVTMultiple v vs) = NVPVersion (Just v) [(n,toPartialVersion cv) \\ cv <- vs & n <- [0..]]

emptyPartialVersion = NVPVersion Nothing []
selectPartialVersion pos (NVPVersion _ items) = case [v \\ (i,v) <- items | i == pos] of
	[v:_] = v
	_     = emptyPartialVersion

maxMajorVersion (NVPVersion mbv vs) = foldr max (maybe 0 fst mbv) (map (maxMajorVersion o snd) vs)
110

111 112 113 114
getVersion:: NxtVersionTree -> NxtVersion
getVersion (NVTBasic v) = v
getVersion (NVTMultiple v _) = v

115 116 117
//Simulated DOM/JSWorld
:: NxtDOMRef :== [Int]
:: NxtDOM :== NxtDOMNode
118

119 120 121
:: NxtDOMNode = 
	{ attributes :: Map String String
	, children   :: [NxtDOMNode]
122
	, history    :: [(NxtVersion,NxtDOMNode)] //Would normally be tracked in JS outside the DOM
123
	}
124

125 126
:: VersionedServerState s =
	{ state            :: s
127 128
	, readVersion      :: NxtMajorVersion //Increments each time the linked sds refreshes
	, stateVersion     :: NxtVersionTree //Holds the versions of all parts of an editor
129
	}
130 131 132

//Untyped clientside configuration
:: NxtUI :== Map String String
133
//:: NxtUI = { attributes  :: Map String String, children :: [NxtUI]}
134 135 136 137 138 139 140 141 142

//Untyped message for transfer and configuration
:: NxtChange
  = NxtNoChange
  | NxtReplace NxtUI
  | NxtChange [NxtAttrChange] [NxtStructureChange]

:: NxtAttrChange = NxtSetAttr String String | NxtDelAttr String
:: NxtStructureChange
143
  = NxtAddChild Int NxtUI
144 145 146
  | NxtRemChild Int
  | NxtUpdChild Int NxtChange

147 148 149 150
derive class iTask NxtDOMNode, VersionedServerState, NxtChange, NxtAttrChange, NxtStructureChange
derive class iTask NxtWithPartialVersion, NxtWithPartialVersions, NxtVersionTree, NxtPartialVersionTree
derive class iTask NxtServerInMessage, NxtServerOutMessage, NxtClientInMessage, NxtClientOutMessage

151 152
derive JSEncode NxtDOMNode, Map
derive JSDecode NxtDOMNode, Map
153

154 155 156 157 158 159 160 161 162 163
//Typed messages for dynamic editors that contain children
:: ContainerMsg c m
	= NxtInsertChild Position (Maybe c)
	| NxtRemoveChild Position
	| NxtUpdateChild Position m

:: Position :== Int

derive class iTask ContainerMsg

164 165 166 167 168
class EditMessage m
where
  encodeEditMessage :: m -> NxtChange
  decodeEditMessage :: NxtChange -> m

169 170 171 172 173
class EditUI c
where
  encodeEditUI :: c -> NxtUI
  decodeEditUI :: NxtUI -> c

174

175
nextRevision (version,revision) = (version, revision + 1)
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

checkRevision (NVPVersion (Just (v1,r1)) _) (v2,r2) = (v1 == v2) && (r1 == r2 + 1) //Only accept the next revision
checkRevision _ _ = False

getVersionFromDOM {NxtDOMNode|attributes}
	# major = maybe 0 toInt ('DM'.get "major-version" attributes)
	# minor = maybe 0 toInt ('DM'.get "minor-version" attributes)
	= (major,minor) 

setVersionInDOM (major,minor) dom=:{NxtDOMNode|attributes}
	# attributes = 'DM'.put "major-version" (toString major) attributes
	# attributes = 'DM'.put "minor-version" (toString minor) attributes
	= {NxtDOMNode|dom & attributes = attributes}

pushHistoryInDOM dom=:{attributes,children,history}
	# version = getVersionFromDOM dom
	= {NxtDOMNode|attributes = attributes, children = children, history = [(version,dom):history]}

resetHistoryInDOM dom = {NxtDOMNode|dom & history = []}

//Get the version of a child element in a compound structure
childversion pos mv = case [v \\ (n,v) <- mv | n == pos] of
	[] = NVPVersion Nothing []
	vs = last vs
200

201
//Definitions of a test editor
202
nxtNumberField :: NxtEditor () Int Int String String String
203
nxtNumberField = {client=client,server=server}
204
where
205
 client = {init=init,onEvent=onEvent,onMessage=onMessage,state=state}
206
 where
207
  init c
208
    = {NxtDOMNode|attributes = 'DM'.fromList [("type","numberfield"),("value",maybe "" fromString c)], children = [], history = []}
209

210
  state dom=:{NxtDOMNode|attributes} = fromMaybe "" ('DM'.get "value" attributes)
211

212
  onEvent [] e dom=:{NxtDOMNode|attributes}
213 214 215 216
	# version = nextRevision $ getVersionFromDOM dom
    # msg = [NxtClientOut e (NVPVersion (Just version) [])]
	# dom = pushHistoryInDOM dom
    # dom = setVersionInDOM version {NxtDOMNode|dom & attributes = 'DM'.put "value" (fromString e) attributes}
217 218
    = (msg,dom)
  onEvent _ _ dom = ([],dom)
219

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  onMessage (NxtClientInLocal message) dom=:{NxtDOMNode|attributes,history}
	# version = nextRevision $ getVersionFromDOM dom
    # msg = [NxtClientOut message (NVPVersion (Just version) [])]
	# dom = pushHistoryInDOM dom
    # dom = setVersionInDOM version {NxtDOMNode|dom & attributes = 'DM'.put "value" message attributes}
    = (msg,dom)

  onMessage (NxtClientInRemote message (NVPVersion (Just oldVersion) _) (NVPVersion (Just newVersion) _)) dom=:{NxtDOMNode|attributes,history}
	# version = getVersionFromDOM dom
	| version <> oldVersion //The server was not up-to date, mitigate potential conflict
		# curValue = fromJust ('DM'.get "value" attributes)
		# oldValue = firstJust ['DM'.get "value" attributes \\ (v,{NxtDOMNode|attributes}) <- history | v == oldVersion]
		//If we only extended the message, we can add the extension 
		| startsWith oldValue curValue
			# extension = subString (textSize oldValue) (textSize curValue) curValue
			# newValue = message +++ extension
			//Set message value as first step in history
			# dom = setVersionInDOM newVersion dom
			# dom = {NxtDOMNode|dom & attributes = 'DM'.put "value" message attributes}
			# dom = pushHistoryInDOM $ resetHistoryInDOM dom
			//Retore the extension and set as message
			# newVersion = nextRevision newVersion
			# dom = setVersionInDOM newVersion dom
			# dom = {NxtDOMNode|dom & attributes = 'DM'.put "value" newValue attributes}
    		# msg = [NxtClientOut newValue (NVPVersion (Just newVersion) [])]
			= (msg,dom)
		| otherwise //Too bad, we have lost our edits, best notify the user somehow by a visual or audible cue
			# dom = {NxtDOMNode|dom & attributes = 'DM'.put "value" message attributes}
			# dom = setVersionInDOM newVersion dom
			# dom = resetHistoryInDOM dom
			= ([],dom)
	| otherwise //Everything was as expected
		# dom = {NxtDOMNode|dom & attributes = 'DM'.put "value" message attributes}
		# dom = setVersionInDOM newVersion dom
		# dom = resetHistoryInDOM dom
		= ([],dom)
  where
    firstJust [Just x:_] = x
258

259
 server = {init=init,parameter=parameter,value=value,onRefresh=onRefresh,onMessage=onMessage}
260
 where
261
  init () v = ("",NVTBasic (v,0), "")
262 263 264
  parameter _ = ()
  value s = Just (toInt s)

265 266 267 268 269 270
  onRefresh r rv  _ (NVTBasic v)
	= ([NxtServerOut (toString r) (NVPVersion (Just v) []) (NVPVersion (Just (rv,0)) [])], toString r,NVTBasic (rv,0), False)

  onMessage (NxtServerInRemote message version) c (NVTBasic v )
	| checkRevision version v = ([], message, NVTBasic (nextRevision v), True)
	| otherwise               = ([], c, NVTBasic v, False)
271

272
nxtButton :: NxtEditor () Bool Bool Bool (String,Bool) Bool
273 274
nxtButton = {client=client,server=server}
where
275
 client = {init=init,state=state,onEvent=onEvent,onMessage=onMessage}
276
 where
277
  init c 
278
	# (label,clicked) = fromMaybe ("button",False) c
279
	= {NxtDOMNode|attributes = 'DM'.fromList [("type","button"),("label",label),("clicked",if clicked "true" "false")],children = [], history = []}
280

281 282
  state dom=:{NxtDOMNode|attributes}
	# clicked = case 'DM'.get "clicked" attributes of
283 284
		(Just "true") = True
		_             = False
285 286 287 288
	# label = fromJust ('DM'.get "label" attributes)
	= (label,clicked)

  onEvent [] "click" dom=:{NxtDOMNode|attributes}
289 290 291 292
	# major = maybe 0 toInt ('DM'.get "major-version" attributes)
	# minor = maybe 1 (inc o toInt) ('DM'.get "minor-version" attributes)
    # msg = [NxtClientOut True (NVPVersion (Just (major,minor)) [])]
    # dom = {NxtDOMNode|dom & attributes = 'DM'.put "minor-version" (toString minor) $'DM'.put "clicked" "true" attributes}
293 294 295 296
    = (msg,dom)
  onEvent _ _ dom
	= ([],dom)

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
  onMessage (NxtClientInLocal value) dom=:{NxtDOMNode|attributes} //Similar to onEvent, but can also set value to False
	# major = maybe 0 toInt ('DM'.get "major-version" attributes)
	# minor = maybe 1 (inc o toInt) ('DM'.get "minor-version" attributes)
    # msg = [NxtClientOut value (NVPVersion (Just (major,minor)) [])]
    # dom = {NxtDOMNode|dom & attributes = 'DM'.put "minor-version" (toString minor) $'DM'.put "clicked" "false" attributes}
    = (msg,dom)

  onMessage (NxtClientInRemote message oldVersion newVersion) dom=:{NxtDOMNode|attributes}
	# attributes = 'DM'.put "clicked" (if message "true" "false") attributes
	//Update version
	# attributes = case newVersion of
		(NVPVersion (Just (major,minor)) _)
			= 'DM'.put "minor-version" (toString minor) $ 'DM'.put "major-version" (toString major) attributes
		_ 
			= attributes
    = ([],{NxtDOMNode|dom & attributes = attributes})
313

314
 server = {init=init,parameter=parameter,value=value,onRefresh=onRefresh,onMessage=onMessage}
315
 where
316
  init () v = (False,NVTBasic (v,0),("Click me",False))
317 318 319
  parameter _ = ()
  value s = Just s

320
  onRefresh r rv _ (NVTBasic v) = ([NxtServerOut r (NVPVersion (Just v) []) (NVPVersion (Just (rv,0)) [])], r, NVTBasic (rv,0), False)
321

322 323 324
  onMessage (NxtServerInRemote message version) c (NVTBasic v)
	| checkRevision version v = ([], message, NVTBasic (nextRevision v), True)
	| otherwise               = ([], c, NVTBasic v, False)
325 326 327 328 329


toVersionAttr (x,y) = toString x +++ "-" +++ toString y

fromVersionAttr s = case split "-" s of
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
	[x,y:_] = (toInt x,toInt y)
	_       = (0,0)

addVersionAttr key (Just version) (NxtNoChange) = NxtChange [NxtSetAttr key (toVersionAttr version)] []
addVersionAttr key (Just version) (NxtChange attrChanges childChanges) = NxtChange (attrChanges ++ [NxtSetAttr key (toVersionAttr version)]) childChanges
addVersionAttr key (Just version) (NxtReplace attrs) = NxtReplace ('DM'.put key (toVersionAttr version) attrs)
addVersionAttr key Nothing message = message

getVersionAttr key (NxtChange attrChanges childChanges)
	= case [fromVersionAttr v \\ NxtSetAttr k v <- attrChanges | key == k] of
		[] =  Nothing
		versions = Just (last versions)
getVersionAttr key (NxtReplace attrs) 
	= fmap fromVersionAttr ('DM'.get key attrs)
getVersionAttr key enc = Nothing

overlayVersions key (NVPVersion Nothing []) (NxtNoChange) = NxtNoChange
overlayVersions key (NVPVersion mbv cvs) (NxtNoChange) = NxtChange attrChanges childChanges
where
	attrChanges = maybe [] (\version -> [NxtSetAttr key (toVersionAttr version)]) mbv
	childChanges = [NxtUpdChild n (overlayVersions key cv NxtNoChange) \\ (n,cv) <- cvs]
351

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
overlayVersions key (NVPVersion mbv cvs) (NxtChange attrChanges childChanges) = NxtChange attrChanges` childChanges`
where
	attrChanges` = attrChanges ++ maybe [] (\version -> [NxtSetAttr key (toVersionAttr version)]) mbv
	childChanges` = childChanges ++ [NxtUpdChild n (overlayVersions key cv NxtNoChange) \\ (n,cv) <- cvs]

overlayVersions key (NVPVersion mbv cvs) (NxtReplace attrs) = NxtReplace attrs` //TODO: Cannot handle nested versions yet
where 
	attrs` = maybe attrs (\version -> 'DM'.put key (toVersionAttr version) attrs) mbv

getOverlayedVersions key NxtNoChange = NVPVersion Nothing []
getOverlayedVersions key (NxtReplace attrs)
	= NVPVersion (fmap fromVersionAttr ('DM'.get key attrs)) []
getOverlayedVersions key (NxtChange attrChanges childChanges)
	# version = foldl setVersion Nothing attrChanges
	# childVersions = [(n, getOverlayedVersions key change) \\ (NxtUpdChild n change) <- childChanges]
	= NVPVersion version (filter (not o emptyChange o snd) childVersions)
where
	setVersion cur (NxtSetAttr k v) = if (k == key) (Just (fromVersionAttr v)) cur
	setVersion cur _ = cur
371

372 373
	emptyChange (NVPVersion Nothing []) = True
	emptyChange _ = False
374

375
instance EditMessage (NxtServerOutMessage m) | EditMessage m
376
where
377 378 379 380
	encodeEditMessage (NxtServerOut message oldVersion newVersion)
		= overlayVersions "old-version" oldVersion
		$ overlayVersions "new-version" newVersion
		$ encodeEditMessage message
381

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
	decodeEditMessage enc
		= let  message = decodeEditMessage enc
		       oldVersion = getOverlayedVersions "old-version" enc
		       newVersion = getOverlayedVersions "new-version" enc
		  in (NxtServerOut message oldVersion newVersion)

instance EditMessage (NxtClientOutMessage m) | EditMessage m
where
	encodeEditMessage (NxtClientOut message version)
		= overlayVersions "version" version
		$ encodeEditMessage message

	decodeEditMessage enc
		= let message = decodeEditMessage enc
		      version = getOverlayedVersions "version" enc
		  in (NxtClientOut message version)

instance EditMessage (NxtWithPartialVersion m) | EditMessage m
where
	encodeEditMessage {message,version}
		= overlayVersions "version" version
		$ encodeEditMessage message

	decodeEditMessage enc
		= {message=decodeEditMessage enc
		  ,version = getOverlayedVersions "version" enc
		  }

instance EditMessage (NxtWithPartialVersions m) | EditMessage m
411 412
where
	encodeEditMessage {message,oldVersion,newVersion}
413 414 415 416
		= overlayVersions "old-version" oldVersion
		$ overlayVersions "new-version" newVersion
		$ encodeEditMessage message

417
	decodeEditMessage enc
418 419 420 421
		= {message=decodeEditMessage enc
		  ,oldVersion = getOverlayedVersions "old-version" enc
		  ,newVersion = getOverlayedVersions "new-version" enc
		  }
422

423 424 425 426 427 428 429 430
instance EditMessage (Either a b) | EditMessage a & EditMessage b
where
  encodeEditMessage (Left value) = NxtChange [] [NxtUpdChild 0 (encodeEditMessage value)]
  encodeEditMessage (Right value) = NxtChange [] [NxtUpdChild 1 (encodeEditMessage value)]

  decodeEditMessage (NxtChange _ [NxtUpdChild 0 dec:_]) = Left (decodeEditMessage dec)
  decodeEditMessage (NxtChange _ [NxtUpdChild 1 dec:_]) = Right (decodeEditMessage dec)

431 432
instance EditMessage String //If strings are used as edit type, it's just the value attribute
where
433 434
  encodeEditMessage value = NxtChange [NxtSetAttr "value" value] []
  decodeEditMessage (NxtChange [NxtSetAttr "value" value:_] []) = value
435

436 437 438 439 440
instance EditUI String
where
  encodeEditUI v = 'DM'.fromList [("value",v)]
  decodeEditUI m = fromJust ('DM'.get "value" m)

441 442
instance EditMessage Bool //If strings are used as edit type, it's just the value attribute
where
443 444 445 446
  encodeEditMessage value = NxtChange [NxtSetAttr "value" (if value "true" "false")] []

  decodeEditMessage (NxtChange [NxtSetAttr "value" "true":_] []) = True
  decodeEditMessage (NxtChange [NxtSetAttr "value" "false":_] []) = False
447

448 449 450 451 452
instance EditUI Bool
where
  encodeEditUI v = 'DM'.fromList [("value",if v "true" "false")]
  decodeEditUI m = case ('DM'.get "value" m) of (Just "true") = True; _ = False

453 454 455 456 457 458
instance EditMessage (Maybe a,Maybe b) | EditMessage a & EditMessage b
where
  encodeEditMessage (mba, mbb)
	= NxtChange [] (maybe [] (\a -> [NxtUpdChild 0 (encodeEditMessage a)]) mba
		 ++ maybe [] (\b -> [NxtUpdChild 1 (encodeEditMessage b)]) mbb)

459 460 461 462
  decodeEditMessage (NxtChange _ [NxtUpdChild 0 enca,NxtUpdChild 1 encb:_])
		= (Just (decodeEditMessage enca), Just (decodeEditMessage encb))
  decodeEditMessage (NxtChange _ [NxtUpdChild 0 enca:_]) = (Just (decodeEditMessage enca),Nothing)
  decodeEditMessage (NxtChange _ [NxtUpdChild 1 encb:_]) = (Nothing,Just (decodeEditMessage encb))
463 464
  decodeEditMessage _ = (Nothing,Nothing)

465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
instance EditUI (Maybe a) | EditUI a
where
	encodeEditUI Nothing = 'DM'.newMap
	encodeEditUI (Just x) = encodeEditUI x

	decodeEditUI m = if ('DM'.null m) Nothing (Just (decodeEditUI m))

instance EditUI (a, b) | EditUI a & EditUI b
where
  encodeEditUI (a,b) = 'DM'.union (encodeEditUI a) (encodeEditUI b) //FIXME: This can't work with overlapping keys...
  decodeEditUI m = (decodeEditUI m,decodeEditUI m)

instance EditMessage (ContainerMsg c m) | EditUI c & EditMessage m
where
	encodeEditMessage (NxtInsertChild pos c) = NxtChange [] [NxtAddChild pos (encodeEditUI c)]
	encodeEditMessage (NxtRemoveChild pos) = NxtChange [] [NxtRemChild pos]
	encodeEditMessage (NxtUpdateChild pos m) = NxtChange [] [NxtUpdChild pos (encodeEditMessage m)]

483 484 485
	decodeEditMessage (NxtChange _ [NxtAddChild pos ui:_]) = NxtInsertChild pos (decodeEditUI ui)
	decodeEditMessage (NxtChange _ [NxtRemChild pos:_]) = NxtRemoveChild pos
	decodeEditMessage (NxtChange _ [NxtUpdChild pos m:_]) = NxtUpdateChild pos (decodeEditMessage m)
486 487 488 489 490 491 492 493 494 495 496 497 498

// ### Composition

//Combine two editors into one that can do both
//Based on the initial parameter, the appropriate one is selected
//The editor is biased left 
alternative :: 
	(NxtEditor p1 r1 w1 s1 c1 m1)
	(NxtEditor p2 r2 w2 s2 c2 m2)
    ->
	(NxtEditor (Either p1 p2) (Either r1 r2) (Either w1 w2) (Either s1 s2) (Either c1 c2) (Either m1 m2))
alternative e1 e2 = {NxtEditor|server=server,client=client} 
where
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	server = {init = init, parameter = parameter, value=value, onRefresh = onRefresh, onMessage = onMessage}
	where
		init (Left p) mv = let (s,vs,c) = (e1.server.NxtEditorServer.init p mv) in  (Left s,vs,Left c)
		init (Right p) mv = let (s,vs,c) = (e2.server.NxtEditorServer.init p mv) in (Right s,vs,Right c)

		parameter (Left s) = Left (e1.server.NxtEditorServer.parameter s)
		parameter (Right s) = Right (e2.server.NxtEditorServer.parameter s)

		value (Left s) = fmap Left (e1.server.NxtEditorServer.value s)
		value (Right s) = fmap Right (e2.server.NxtEditorServer.value s)

		onRefresh (Left r) rv (Left s) sv
			# (ms,s,sv,w) = e1.server.NxtEditorServer.onRefresh r rv s sv
			= ([NxtServerOut (Left message) oldVersion newVersion \\ (NxtServerOut message oldVersion newVersion) <- ms], Left s, sv, w)
		onRefresh (Right r) rv (Right s) sv
			# (ms,s,sv,w) = e2.server.NxtEditorServer.onRefresh r rv s sv
			= ([NxtServerOut (Right message) oldVersion newVersion \\ (NxtServerOut message oldVersion newVersion) <- ms], Right s, sv, w)

		onMessage (NxtServerInRemote (Left m) version) (Left s) sv 
			# (ms,s,sv,w) = e1.server.NxtEditorServer.onMessage (NxtServerInRemote m version) s sv
			= ([NxtServerOut (Left message) oldVersion newVersion \\ (NxtServerOut message oldVersion newVersion) <- ms], Left s, sv, w)
		onMessage (NxtServerInRemote (Right m) version) (Right s) sv
			# (ms,s,sv,w) = e2.server.NxtEditorServer.onMessage (NxtServerInRemote m version) s sv
			= ([NxtServerOut (Right message) oldVersion newVersion \\ (NxtServerOut message oldVersion newVersion) <- ms], Right s, sv, w)

524 525
    client = {init = init, onEvent = onEvent, onMessage = onMessage, state = state}
	where
526 527 528 529 530 531 532 533 534 535 536 537
		init Nothing
			# dom=:{NxtDOMNode|attributes} = e1.client.NxtEditorClient.init Nothing
			= {NxtDOMNode|dom & attributes = 'DM'.put "alternative" "left" attributes}
		init (Just (Left c))
			# dom=:{NxtDOMNode|attributes} = e1.client.NxtEditorClient.init (Just c)
			= {NxtDOMNode|dom & attributes = 'DM'.put "alternative" "left" attributes}
		init (Just (Right c))
			# dom=:{NxtDOMNode|attributes} = e2.client.NxtEditorClient.init (Just c)
			= {NxtDOMNode|dom & attributes = 'DM'.put "alternative" "right" attributes}

		state dom=:{NxtDOMNode|attributes}
    		# alt = fromJust ('DM'.get "alternative" attributes)
538
			| alt == "left"
539 540 541 542 543 544 545 546
				= Left (e1.client.NxtEditorClient.state dom)
			| otherwise
				= Right (e2.client.NxtEditorClient.state dom)
	
		onEvent ref event dom=:{NxtDOMNode|attributes}
    		# alt = fromJust ('DM'.get "alternative" attributes)
			| alt == "left"
				# (ms,dom) = e1.client.NxtEditorClient.onEvent ref event dom 
547
				= ([NxtClientOut (Left message) version \\ (NxtClientOut message version) <- ms], dom)
548
			| otherwise
549
				# (ms,dom) = e2.client.NxtEditorClient.onEvent ref event dom 
550
				= ([NxtClientOut (Right message) version \\ (NxtClientOut message version) <- ms], dom)
551

552 553 554 555 556 557
		onMessage (NxtClientInRemote (Left m) oldVersion newVersion) dom 
			# (ms,dom) = e1.client.NxtEditorClient.onMessage (NxtClientInRemote m oldVersion newVersion) dom 
			= ([NxtClientOut (Left message) version \\ (NxtClientOut message version) <- ms], dom)
		onMessage (NxtClientInRemote (Right m) oldVersion newVersion) dom 
			# (ms,dom) = e2.client.NxtEditorClient.onMessage (NxtClientInRemote m oldVersion newVersion) dom 
			= ([NxtClientOut (Right message) version \\ (NxtClientOut message version) <- ms], dom)
558

559 560 561
multiple :: (NxtEditor p r w s c m) -> (NxtEditor p [Maybe r] [Maybe w] (p,[s]) [c] (ContainerMsg c m))
multiple editor = {NxtEditor|server=server,client=client}
where
562 563
	server = {init = init, parameter = parameter, value=value, onRefresh = onRefresh, onMessage = onMessage}
	where
564 565 566
		init p v = ((p,[]),NVTMultiple (v,0) [], [])
		parameter (p,_) = p
		value (_,ss) = Just (map editor.server.NxtEditorServer.value ss)
567

568 569 570 571 572 573 574 575
		onRefresh mbrs rv (p,ss) (NVTMultiple stv vs)//A naive linear side by side diff to see what needs updating
			# (msgs,ss,vs,write) = compare 0 ss vs mbrs
			= (msgs, (p,ss), NVTMultiple stv vs, False)//By default we need to do a diff
		where
			//Compare first items side by side
			compare i [s:ss] [v:vs] [Nothing:mbrs]
				# (msgs, ss, vs, write) = compare (i + 1) ss vs mbrs
				= (msgs, [s:ss], [v:vs], write)
576

577 578 579 580 581
			compare i [s:ss] [vo:vs] [Just r:mbrs]
				# (ms, s, vn, writes) = editor.server.NxtEditorServer.onRefresh r rv s vo
				# (msgs, ss, vs, writess) = compare (i + 1) ss vs mbrs
				= ([NxtServerOut (NxtUpdateChild i message) (toPartialVersion vo) (toPartialVersion vn) \\ (NxtServerOut message oldVersion newVersion) <- ms] ++ msgs
				  , [s:ss], [vn:vs], writes || writess) //TODO: determine proper versions...
582

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
			//New read list has more items
			compare i [] _ mbrs
				# (msgs, ss, vs, ws) = unzip4 [create i` mbr \\ mbr <- mbrs & i` <- [i..]]
				= (flatten msgs, ss, vs, or ws)
			where
				create i mbr
					# (s,vi,c) = editor.server.NxtEditorServer.init p (fst stv)
					# (ms, s, v, write) = maybe ([],s,vi,False) (\r -> editor.server.NxtEditorServer.onRefresh r rv s vi) mbr 
					= ([NxtServerOut (NxtInsertChild i (Just c)) emptyPartialVersion (toPartialVersion vi)
					   :[NxtServerOut (NxtUpdateChild i message) (NVPVersion Nothing [(i,oldVersion)]) (NVPVersion Nothing [(i,newVersion)]) \\ (NxtServerOut message oldVersion newVersion) <- ms]], s, v, write)

			//New read list has less (remove existing)
			compare i ss _ [] = (repeatn (length ss) (NxtServerOut (NxtRemoveChild i) emptyPartialVersion emptyPartialVersion),[],[],False) //TODO: versions...

		onMessage (NxtServerInRemote (NxtUpdateChild pos m) (NVPVersion _ mv)) (p,ss) (NVTMultiple stv vs)//Route to the corresponding child
			| pos >= length ss || pos < 0 = ([],(p,ss),NVTMultiple stv vs,False) //Out of bounds, (maybe abort instead for the simulation)
			# (ms,s,v,write) = editor.server.NxtEditorServer.onMessage (NxtServerInRemote m (childversion pos mv)) (ss !! pos) (vs !! pos)
			= ([NxtServerOut (NxtUpdateChild pos message) (multiversion pos oldVersion) (multiversion pos newVersion)
			   \\ (NxtServerOut message oldVersion newVersion) <-ms ], (p, updateAt pos s ss), NVTMultiple stv (updateAt pos v vs), write)
			//TODO: create the right version structure
            where
				multiversion pos v = NVPVersion Nothing [(pos,v)]

		onMessage (NxtServerInRemote (NxtRemoveChild pos) (NVPVersion _ mv)) (p,ss) (NVTMultiple stv vs)
			| pos >= length ss || pos < 0 = ([],(p,ss),NVTMultiple stv vs, False) //Out of bounds, (maybe abort instead for the simulation)
			= ([], (p, removeAt pos ss), NVTMultiple stv (removeAt pos vs), True)

		onMessage (NxtServerInRemote (NxtInsertChild pos Nothing) (NVPVersion _ mv)) (p,ss) (NVTMultiple stv vs)
			| pos > length ss || pos < 0 = ([],(p,ss),NVTMultiple stv vs, False) //Out of bounds, (maybe abort instead for the simulation)
			# (s,v,_) = editor.server.NxtEditorServer.init p (fst stv) 
			= ([], (p, insertAt pos s ss), NVTMultiple stv (insertAt pos v vs), True)
614

615 616 617
		onMessage msg (p,ss) state
			= abort "OEPS.."

618 619
    client = {init = init, onEvent = onEvent, onMessage = onMessage, state = state}
	where
620
		init Nothing = {NxtDOMNode|attributes = attributes, children = [], history = []}
621
		init (Just cs)
622
			= {NxtDOMNode|attributes = attributes, children = [editor.client.NxtEditorClient.init (Just c) \\ c <- cs], history = []}
623 624 625 626 627
		attributes = 'DM'.fromList [("type","multiple")]

		onEvent [n:ref] event dom=:{NxtDOMNode|children}
			| n < 0 || n >= length children = ([],dom)
			# (ms,child) = editor.client.NxtEditorClient.onEvent ref event (children !! n)
628
			= ([NxtClientOut (NxtUpdateChild n message) (NVPVersion Nothing [(n,version)]) \\ (NxtClientOut message version) <- ms], {NxtDOMNode|dom & children = updateAt n child children})
629
		onEvent _ _ dom
630 631
			= ([],dom)

632
		onMessage (NxtClientInRemote (NxtInsertChild pos c) oldVersion newVersion) dom=:{NxtDOMNode|children} //TODO: Check structure versions...
633 634
			# child = editor.client.NxtEditorClient.init c
			= ([],{NxtDOMNode|dom & children = insertAt pos child children})
635

636 637 638 639
		onMessage (NxtClientInLocal (NxtInsertChild pos c)) dom=:{NxtDOMNode|children} //TODO: Revert and versioning
			# child = editor.client.NxtEditorClient.init c
			= ([NxtClientOut (NxtInsertChild pos c) emptyPartialVersion],{NxtDOMNode|dom & children = insertAt pos child children})

640
		onMessage (NxtClientInRemote (NxtRemoveChild pos) oldVersion newVersion) dom=:{NxtDOMNode|children} //TODO: Check structure versions...
641
			= ([],{NxtDOMNode|dom & children = removeAt pos children})
642

643 644 645
		onMessage (NxtClientInLocal (NxtRemoveChild pos)) dom=:{NxtDOMNode|children} //TODO: should be able to revert to older version and send versions...
			= ([NxtClientOut (NxtRemoveChild pos) emptyPartialVersion],{NxtDOMNode|dom & children = removeAt pos children})

646 647 648 649 650
		onMessage (NxtClientInRemote (NxtUpdateChild pos m) oldVersion newVersion) dom=:{NxtDOMNode|children}
			# (ms,child) = editor.client.NxtEditorClient.onMessage
				(NxtClientInRemote m (selectPartialVersion pos oldVersion) (selectPartialVersion pos newVersion)) (children !! pos)
			= ([(NxtClientOut (NxtUpdateChild pos message) emptyPartialVersion) \\ (NxtClientOut message version) <- ms] //TODO: Determine version
			  ,{NxtDOMNode|dom & children = updateAt pos child children})
651

652 653 654
		onMessage (NxtClientInLocal m) dom
			= trace_n "UNIMPLEMENTED" ([],dom)

655 656
		state dom=:{NxtDOMNode|children}
			= map editor.client.NxtEditorClient.state children
657

658
//Compose by juxtaposition, no need to specify interdependency
659 660
glue ::
		(NxtEditor p1 r1 w1 s1 c1 m1)
661
        (NxtEditor p2 r2 w2 s2 c2 m2)
662
        ->
663
        (NxtEditor (p1,p2) (Maybe r1,Maybe r2) (Maybe w1, Maybe w2) (s1,s2) (c1,c2) (Maybe m1, Maybe m2))
664 665
glue e1 e2 = {NxtEditor|server=server,client=client}
where
666
  server = {init=init,parameter=parameter,value=value,onRefresh=onRefresh,onMessage=onMessage}
667
  where
668 669 670 671
	init (p1,p2) mv
		# (s1,vs1,c1) = e1.server.NxtEditorServer.init p1 mv
		# (s2,vs2,c2) = e2.server.NxtEditorServer.init p2 mv
		= ((s1,s2),NVTGlue vs1 vs2, (c1,c2))
672

673
	parameter (s1,s2) = (e1.server.NxtEditorServer.parameter s1, e2.server.NxtEditorServer.parameter s2)
674

675 676
	value (s1,s2) = case (e1.server.NxtEditorServer.value s1, e2.server.NxtEditorServer.value s2) of
		(Nothing,Nothing) = Nothing
677
		(mb1,mb2) = Just (mb1,mb2)
678

679 680 681 682
    onRefresh (mbr1,mbr2) rv (s1,s2) (NVTGlue sv1 sv2)
       # (m1, s1, sv1, w1) = maybe ([],s1,sv1,False) (\r1 -> e1.server.NxtEditorServer.onRefresh r1 rv s1 sv1) mbr1
       # (m2, s2, sv2, w2) = maybe ([],s2,sv2,False) (\r2 -> e2.server.NxtEditorServer.onRefresh r2 rv s2 sv2) mbr2
       = (zipMessagesWithVersions m1 m2, (s1,s2), NVTGlue sv1 sv2, w1 || w2)
683

684 685 686 687
    onMessage (NxtServerInLocal (mb1,mb2) vm) (s1,s2) (NVTGlue sv1 sv2)
       # (ms1, s1, sv1, w1) = maybe ([],s1,sv1,False) (\m1 -> e1.server.NxtEditorServer.onMessage (NxtServerInLocal m1 vm) s1 sv1) mb1
       # (ms2, s2, sv2, w2) = maybe ([],s2,sv2,False) (\m2 -> e2.server.NxtEditorServer.onMessage (NxtServerInLocal m2 vm) s2 sv2) mb2
       = (zipMessagesWithVersions ms1 ms2, (s1,s2), NVTGlue sv1 sv2, w1 || w2)
688

689 690 691 692 693 694 695 696 697 698 699 700
    onMessage (NxtServerInRemote (mb1,mb2) (NVPVersion _ cvs)) (s1,s2) (NVTGlue sv1 sv2)
       # (ms1, s1, sv1, w1) = maybe ([],s1,sv1,False) (\m1 -> e1.server.NxtEditorServer.onMessage (NxtServerInRemote m1 (childversion 0 cvs)) s1 sv1) mb1
       # (ms2, s2, sv2, w2) = maybe ([],s2,sv2,False) (\m2 -> e2.server.NxtEditorServer.onMessage (NxtServerInRemote m2 (childversion 1 cvs)) s2 sv2) mb2
       = (zipMessagesWithVersions ms1 ms2, (s1,s2), NVTGlue sv1 sv2, w1 || w2)
	
  zipMessagesWithVersions [NxtServerOut x ovx nvx:xs] [NxtServerOut y ovy nvy:ys]
	= [NxtServerOut (Just x, Just y) (NVPVersion Nothing [(0,ovx),(1,ovy)]) (NVPVersion Nothing [(0,nvx),(1,nvy)]):zipMessagesWithVersions xs ys]
  zipMessagesWithVersions [] ys
	= [NxtServerOut (Nothing,Just y) (NVPVersion Nothing [(1,ovy)]) (NVPVersion Nothing [(1,nvy)]) \\ (NxtServerOut y ovy nvy) <- ys]
  zipMessagesWithVersions xs []
	= [NxtServerOut (Just x,Nothing) (NVPVersion Nothing [(0,ovx)]) (NVPVersion Nothing [(0,nvx)]) \\ (NxtServerOut x ovx nvx) <- xs]
 
701
  client = {init=init,onEvent=onEvent,onMessage=onMessage,state=state}
702
  where
703
    init c
704
		# (c1,c2) = maybe (Nothing,Nothing) (\(cx,cy) -> (Just cx,Just cy)) c
705
		= {NxtDOMNode|attributes=attributes,children = [e1.client.NxtEditorClient.init c1, e2.client.NxtEditorClient.init c2], history = []}
706 707 708 709
	attributes = 'DM'.fromList [("type","glue")]

    onEvent [0:ref] event dom=:{NxtDOMNode|children=[c1,c2]}
      # (m1,c1) = e1.client.NxtEditorClient.onEvent ref event c1
710
	  = ([NxtClientOut (Just message,Nothing) (NVPVersion Nothing [(0,version)]) \\ (NxtClientOut message version) <-m1],{NxtDOMNode|dom & children = [c1,c2]})
711 712
    onEvent [1:ref] event dom=:{NxtDOMNode|children=[c1,c2]}
      # (m2,c2) = e2.client.NxtEditorClient.onEvent ref event c2
713
	  = ([NxtClientOut (Nothing,Just message) (NVPVersion Nothing [(1,version)]) \\ (NxtClientOut message version) <-m2],{NxtDOMNode|dom & children = [c1,c2]})
714 715
	onEvent _ _ dom = ([],dom)

716 717 718 719 720 721 722 723 724
    onMessage (NxtClientInLocal (mb1,mb2)) dom=:{NxtDOMNode|children=[c1,c2]}
      # (m1,c1) = maybe ([],c1) (\m1 -> e1.client.NxtEditorClient.onMessage (NxtClientInLocal m1) c1) mb1
      # (m2,c2) = maybe ([],c2) (\m2 -> e2.client.NxtEditorClient.onMessage (NxtClientInLocal m2) c2) mb2
      = (zipClientOutMessages m1 m2, {NxtDOMNode|dom & children = [c1,c2]})

    onMessage (NxtClientInRemote (mb1,mb2) (NVPVersion _ omvs) (NVPVersion _ nmvs)) dom=:{NxtDOMNode|children=[c1,c2]}
      # (m1,c1) = maybe ([],c1) (\m1 -> e1.client.NxtEditorClient.onMessage (NxtClientInRemote m1 (childversion 0 omvs) (childversion 0 nmvs)) c1) mb1
      # (m2,c2) = maybe ([],c2) (\m2 -> e2.client.NxtEditorClient.onMessage (NxtClientInRemote m2 (childversion 1 omvs) (childversion 1 nmvs)) c2) mb2
      = (zipClientOutMessages m1 m2, {NxtDOMNode|dom & children = [c1,c2]})
725 726 727

    state dom=:{NxtDOMNode|children=[c1,c2]}
	  = (e1.client.NxtEditorClient.state c1, e2.client.NxtEditorClient.state c2)
728

729 730 731 732 733 734 735
  zipClientOutMessages [NxtClientOut x vx:xs] [NxtClientOut y vy:ys]
	= [NxtClientOut (Just x, Just y) (NVPVersion Nothing [(0,vx),(1,vy)]):zipClientOutMessages xs ys]
  zipClientOutMessages [] ys
	= [NxtClientOut (Nothing,Just y) (NVPVersion Nothing [(1,vy)]) \\ (NxtClientOut y vy) <- ys]
  zipClientOutMessages xs []
	= [NxtClientOut (Just x,Nothing) (NVPVersion Nothing [(0,vx)]) \\ (NxtClientOut x vx) <- xs]
 
736
linkm ::
737 738
	([s] (ContainerMsg c m) -> (Bool, [ContainerMsg c m]))
	([c] (ContainerMsg c m) -> (Bool, [ContainerMsg c m]))
739 740 741
	(NxtEditor p [Maybe r] [Maybe w] (p,[s]) [c] (ContainerMsg c m))
	->
	(NxtEditor p [Maybe r] [Maybe w] (p,[s]) [c] (ContainerMsg c m))
742
linkm mserver mclient editor = {NxtEditor|server=server,client=client}
743 744 745
where
	server = {init=init,parameter=parameter,value=value,onRefresh=onRefresh,onMessage=onMessage}
	where
746
		init = editor.server.NxtEditorServer.init
747 748 749
		parameter = editor.server.NxtEditorServer.parameter
		value = editor.server.NxtEditorServer.value

750 751 752 753
    	onRefresh rs rv (p,ss) (NVTMultiple v vs)
			# (msgs, (p,ss), NVTMultiple v vs, mbw)  = editor.server.NxtEditorServer.onRefresh rs rv (p,ss) (NVTMultiple v vs)
			# (msgs, p, ss, v, vs, mbwm) = foldl modifyMsg ([],p,ss,v,vs,False) msgs
			= (msgs, (p,ss), NVTMultiple v vs, mbw || mbwm)
754

755 756 757 758
		onMessage m (p,ss) (NVTMultiple v vs)
			# (msgs,(p,ss), NVTMultiple v vs, mbw) = editor.server.NxtEditorServer.onMessage m (p,ss) (NVTMultiple v vs)
			# (msgs,p,ss,v,vs,mbwm) = foldl modifyMsg ([],p,ss,v,vs,False) msgs
			= (msgs,(p,ss), NVTMultiple v vs, mbw || mbwm)
759

760
		modifyMsg (msgs,p,ss,v,vs,mbw) msg=:(NxtServerOut message oldVersion newVersion)
761
			//Modify the outgoing messages
762
			# (passOn,feedBack) = mserver ss message
763
			//Feedback messages
764 765
			# (feedbackOutput,p,ss,v,vs,mbwm) = foldl (feedBackMsg (maxMajorVersion newVersion)) ([],p,ss,v,vs,False) feedBack
			= (msgs ++ (if passOn [NxtServerOut message oldVersion newVersion] []) ++ feedbackOutput, p, ss, v, vs, mbw || mbwm)
766

767 768 769
		feedBackMsg vm (msgs,p,ss,v,vs,mbw) msg
			# (emsgs,(p,ss),NVTMultiple v vs,mbwm) = onMessage (NxtServerInLocal msg vm) (p,ss) (NVTMultiple v vs)
			= (msgs ++ emsgs,p,ss,v,vs,mbw || mbwm)
770 771 772 773 774 775

	client = {init=init,onEvent=onEvent,onMessage=onMessage,state=state}
	where
		init = editor.client.NxtEditorClient.init
		state = editor.client.NxtEditorClient.state

776 777 778
    	onEvent ref event dom
			# (msgs,dom) = editor.client.NxtEditorClient.onEvent ref event dom
			# (msgs,dom) = foldl modifyMsg ([],dom) msgs
779 780
			= (msgs,dom)

781 782 783
		onMessage msg dom
			# (msgs,dom) = editor.client.NxtEditorClient.onMessage msg dom
			# (msgs,dom) = foldl modifyMsg ([],dom) msgs
784 785
			= (msgs,dom)

786
		modifyMsg (msgs,dom) (NxtClientOut message version)
787
			# cs = state dom
788
			# (passOn,feedBack) = mclient cs message
789
			# (feedbackOutput,dom) = foldl feedBackMsg ([],dom) feedBack
790
			= (msgs ++ (if passOn [NxtClientOut message version] []) ++ feedbackOutput, dom)
791

792
		feedBackMsg (msgs,dom) msg
793
			# (emsgs,dom) = onMessage (NxtClientInLocal msg) dom
794
			= (msgs ++ emsgs,dom)
795

796
//Define the dependencies by defining feedback on messages
797
//NOTE: Only one the last 'writes' to the data source are be returned, is this ok?
798
linkg ::
799
        //Rewrite from server to client with feedback to server
800
        (s1 s2 (Maybe m1, Maybe m2) -> (Bool, [(Maybe m1, Maybe m2)]))
801
        //Rewrite from client to server with feedback to client
802 803
        (c1 c2 (Maybe m1, Maybe m2) -> (Bool, [(Maybe m1, Maybe m2)])) //Pass on, feedback message
        (NxtEditor p (Maybe r1,Maybe r2) (Maybe w1, Maybe w2) (s1,s2) (c1,c2) (Maybe m1, Maybe m2))
804
        ->
805
        (NxtEditor p (Maybe r1,Maybe r2) (Maybe w1, Maybe w2) (s1,s2) (c1,c2) (Maybe m1, Maybe m2))
806