Mercurial > repos > rhope
comparison framework.rhope @ 147:f3686f60985d
Sort of working port of framework. Transaction bug seems to be getting in the way. Going to work around, but want the old version in the repo so I can test later.
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 22 Nov 2010 01:15:02 -0500 |
parents | 6202b866d72c |
children | f582fd6c75ee |
comparison
equal
deleted
inserted
replaced
146:1f39e69446f9 | 147:f3686f60985d |
---|---|
1 Import webserver.rhope | 1 Import webserver.rhope |
2 | 2 |
3 _Key Value Map[list,index,newlist,worker:out] | |
4 { | |
5 newval,newkey <- [worker]Call[[list]Index[index], index] | |
6 | |
7 next <- [newlist]Set[newkey, newval] | |
8 | |
9 [list]Next[index] | |
10 { | |
11 out <- _Key Value Map[list, ~, next, worker] | |
12 }{ | |
13 out <- Val[next] | |
14 } | |
15 } | |
16 | |
17 Key Value Map[list,worker:out] | |
18 { | |
19 [list]First | |
20 { | |
21 out <- _Key Value Map[list, ~, New Like[list], worker] | |
22 }{ | |
23 out <- New Like[list] | |
24 } | |
25 } | |
26 | |
3 Framework Handler[con,path,request type,queryvars,headers,handler,title,use session] | 27 Framework Handler[con,path,request type,queryvars,headers,handler,title,use session] |
4 { | 28 { |
5 page <- Page[title, path, use session, queryvars, headers] | 29 page <- Page[title, path, use session, queryvars, headers] |
6 out list <- [handler]Do[ [[List[]]Append[page]]Append[path] ] | 30 handler page <- [handler]Call[page,path] |
7 handler page <- [out list]Index[0] | |
8 If[[request type] = ["POST"]] | 31 If[[request type] = ["POST"]] |
9 { | 32 { |
10 final page <- Process POST[handler page, con, headers] | 33 final page,ncon <- Process POST[handler page, con, headers] |
11 }{ | 34 }{ |
12 final page <- Val[handler page] | 35 final page <- Val[handler page] |
36 ncon <- Val[con] | |
13 } | 37 } |
14 string,out headers <- [final page]Render | 38 string,out headers <- [final page]Render |
15 | 39 |
16 [HTTP OK[con, Get Content Type[".html"], [string]Length, out headers] | 40 after headers <- HTTP OK[ncon, Get Content Type[".html"], [string]Byte Length, out headers] |
17 ]Put String[string] | 41 { Print["Sent status"] |
42 { [[string]Write to File[after headers]]Close | |
43 { Print["Wrote data"] }}} | |
18 } | 44 } |
19 | 45 |
20 Handler Fixer[handler:out] | 46 Handler Fixer[handler:out] |
21 { | 47 { |
22 If[[Type Of[handler]] = ["List"]] | 48 [(List(),List Leaf())]Find[=[?,Blueprint Of[handler]]] |
23 { | 49 { |
24 out <- [[["Framework Handler"]Set Input[5, [handler]Index[0]]]Set Input[6, [handler]Index[1]]]Set Input[7, [handler]Index[2]] | 50 out <- Val[Framework Handler[?, ?, ?, ?, ?, [handler]Index[0], [handler]Index[1], [handler]Index[2]]] |
25 }{ | 51 }{ |
26 out <- handler | 52 out <- handler |
27 } | 53 } |
28 } | 54 } |
29 | 55 |
30 Start Web[handlers] | 56 Start Web[handlers,port] |
31 { | 57 { |
32 Print["Starting Rhope Web Server"] | 58 Print["Starting Rhope Web Server"] |
33 Init Sessions[] | 59 workaround <- Init Sessions[] |
34 { Listen on Port[80,["Connection Start"]Set Input[1, Map[handlers,"Handler Fixer"]]] } | 60 Val[workaround] |
35 Wait Forever[] | 61 { Listen on Port[port,Connection Start[?, ?, Map[handlers,Handler Fixer[?]]]] |
62 { Wait Forever[] }} | |
36 } | 63 } |
37 | 64 |
38 Get Class[container:class] | 65 Get Class[container:class] |
39 { | 66 { |
40 If[[[[container]Class >>]Length] > [0]] | 67 If[[[[container]Class >>]Length] > [0]] |
52 Data | 79 Data |
53 } | 80 } |
54 | 81 |
55 Web Event[name,origin,data:out] | 82 Web Event[name,origin,data:out] |
56 { | 83 { |
57 out <- [[[Build["Web Event"]]Event Name <<[name]]Origin <<[origin]]Data <<[data] | 84 out <- [[[Build[Web Event()]]Event Name <<[name]]Origin <<[origin]]Data <<[data] |
58 } | 85 } |
59 | 86 |
60 Blueprint Web Container | 87 Blueprint Web Container |
61 { | 88 { |
62 Tag Name | 89 Tag Name |
70 Preformatted | 97 Preformatted |
71 } | 98 } |
72 | 99 |
73 Web Container[class:out] | 100 Web Container[class:out] |
74 { | 101 { |
75 out <- [[[[[[[[Build["Web Container"] | 102 out <- [[[[[[[[Build[Web Container()] |
76 ]Tag Name <<["div"] | 103 ]Tag Name <<["div"] |
77 ]Class <<[class] | 104 ]Class <<[class] |
78 ]Propagate Events <<[No] | 105 ]Propagate Events <<[No] |
79 ]Children <<[List[]] | 106 ]Children <<[List[]] |
80 ]Named Children <<[Dictionary[]] | 107 ]Named Children <<[Dictionary[]] |
93 out <- [start]Append[[container]Render] | 120 out <- [start]Append[[container]Render] |
94 } | 121 } |
95 | 122 |
96 _Preformatted[child,val:out] | 123 _Preformatted[child,val:out] |
97 { | 124 { |
98 If[[Type Of[child]] = ["Web Text"]] | 125 If[[Blueprint Of[child]] = [Web Text()]] |
99 { | 126 { |
100 out <- [child]Preformatted <<[val] | 127 out <- [child]Preformatted <<[val] |
101 }{ | 128 }{ |
102 If[[Type Of[child]] = ["Web Container"]] | 129 If[[Blueprint Of[child]] = [Web Container()]] |
103 { | 130 { |
104 out <- [child]Preformatted[val] | 131 out <- [child]Preformatted[val] |
105 }{ | 132 }{ |
106 out <- child | 133 out <- child |
107 } | 134 } |
108 } | 135 } |
109 } | 136 } |
110 | 137 |
111 Preformatted@Web Container[cont,preformatted?:out] | 138 Preformatted@Web Container[cont,preformatted?:out] |
112 { | 139 { |
113 out <- [[cont]Children <<[ Map[[cont]Children >>, ["_Preformatted"]Set Input[1, preformatted?]] ] | 140 out <- [[cont]Children <<[ Map[[cont]Children >>, _Preformatted[?, preformatted?]] ] |
114 ]Preformatted <<[preformatted?] | 141 ]Preformatted <<[preformatted?] |
115 } | 142 } |
116 | 143 |
117 Set Session@Web Container[container,session:out] | 144 Set Session@Web Container[container,session:out] |
118 { | 145 { |
119 out <- [ | 146 out <- [ |
120 [ | 147 [ |
121 [container]Use Session <<[Yes] | 148 [container]Use Session <<[Yes] |
122 ]Session <<[session] | 149 ]Session <<[session] |
123 ]Children <<[ Map[ [container]Children >>, ["Set Session"]Set Input[1, session] ] ] | 150 ]Children <<[ Map[ [container]Children >>, Set Session[?, session] ] ] |
124 } | 151 } |
125 | 152 |
126 Set Handler@Web Container[container,event name,handler:out] | 153 Set Handler@Web Container[container,event name,handler:out] |
127 { | 154 { |
128 out <- [container]Handlers <<[ [[container]Handlers >> ]Set[event name, handler] ] | 155 out <- [container]Handlers <<[ [[container]Handlers >> ]Set[event name, handler] ] |
139 tab <- "\t" | 166 tab <- "\t" |
140 } | 167 } |
141 out <- [[[[[[["<"]Append[ [container]Tag Name >> ] | 168 out <- [[[[[[["<"]Append[ [container]Tag Name >> ] |
142 ]Append[Get Class[container]] | 169 ]Append[Get Class[container]] |
143 ]Append[[[">"]Append[newline]]Append[tab]] | 170 ]Append[[[">"]Append[newline]]Append[tab]] |
144 ]Append[Fold[["Render Child"]<String@Worker, "", [container]Children >>]] | 171 ]Append[Fold[Render Child[?], "", [container]Children >>]] |
145 ]Append[[newline]Append["</"]] | 172 ]Append[[newline]Append["</"]] |
146 ]Append[ [container]Tag Name >> ] | 173 ]Append[ [container]Tag Name >> ] |
147 ]Append[[">"]Append[newline]] | 174 ]Append[[">"]Append[newline]] |
148 } | 175 } |
149 | 176 |
150 Container Event Handler[container,events,index:cont,out events] | 177 Container Event Handler[container,events,index:cont,out events] |
151 { | 178 { |
152 event <- [events]Index[index] | 179 event <- [events]Index[index] |
153 [[container]Handlers >>]Index[ [event]Event Name >>] | 180 [[container]Handlers >>]Index[ [event]Event Name >>] |
154 { | 181 { |
155 result list <- [~]Do[ | 182 //The original version, you had to populate the container output |
156 [[List[]]Append[container]]Append[event] | 183 //and optionally populate the new event output, but that won't work |
157 ] | 184 //now. None of my existing code really needs to populate both so I've |
158 new container <- [result list]Index[0] | 185 //made them mutually exclusive. |
159 [result list]Index[1] | 186 new container <- [~]Call[container,event] |
160 { | 187 { |
188 out events <- Val[result events] | |
189 }{ | |
161 out events <- [result events]Append[~] | 190 out events <- [result events]Append[~] |
162 }{ | 191 new container <- Val[container] |
163 out events <- Val[result events] | 192 } |
164 } | 193 }{ |
165 }{ | 194 new container <- Val[container] |
166 new container <- container | |
167 out events <- Val[result events] | 195 out events <- Val[result events] |
168 } | 196 } |
169 | 197 |
170 [events]Next[index] | 198 [events]Next[index] |
171 { | 199 { |
259 out <- [container]Handlers <<[ [[container]Handlers >> ]Set[event name, handler] ] | 287 out <- [container]Handlers <<[ [[container]Handlers >> ]Set[event name, handler] ] |
260 } | 288 } |
261 | 289 |
262 Page[title,url,use session,queryvars,headers:out] | 290 Page[title,url,use session,queryvars,headers:out] |
263 { | 291 { |
264 page <- [[[[[[[Build["Page"] | 292 page <- [[[[[[[Build[Page()] |
265 ]Title <<[title] | 293 ]Title <<[title] |
266 ]URL <<[url] | 294 ]URL <<[url] |
267 ]CSS <<[[List[]]Append["/default.css"]] | 295 ]CSS <<[[List[]]Append["/default.css"]] |
268 ]Children <<[List[]] | 296 ]Children <<[List[]] |
269 ]Named Children <<[Dictionary[]] | 297 ]Named Children <<[Dictionary[]] |
270 ]Handlers <<[Dictionary[]] | 298 ]Handlers <<[Dictionary[]] |
271 ]Use Session <<[use session] | 299 ]Use Session <<[use session] |
272 If[use session] | 300 If[use session] |
273 { | 301 { |
274 Load@Session[queryvars, headers] | 302 Load Session[queryvars, headers] |
275 { | 303 { |
276 out <- [[page]Session <<[~]]Session ID <<[ [~]Session ID>>] | 304 out <- [[page]Session <<[~]]Session ID <<[ [~]Session ID>>] |
277 } | 305 } |
278 }{ | 306 }{ |
279 out <- Val[page] | 307 out <- Val[page] |
300 out <- [[[[[[["<html>\n\t<head>\n\t\t<title>"]Append[[page]Title >>] | 328 out <- [[[[[[["<html>\n\t<head>\n\t\t<title>"]Append[[page]Title >>] |
301 ]Append["</title>\n\t\t<link rel=\"stylesheet\" href=\""] | 329 ]Append["</title>\n\t\t<link rel=\"stylesheet\" href=\""] |
302 ]Append[[[page]CSS >>]Join["\">\n\t\t<link rel=\"stylesheet\" href=\""]] | 330 ]Append[[[page]CSS >>]Join["\">\n\t\t<link rel=\"stylesheet\" href=\""]] |
303 ]Append["\">\n\t</head>\n\t<body>\n\t<form method=\"POST\" action=\""] | 331 ]Append["\">\n\t</head>\n\t<body>\n\t<form method=\"POST\" action=\""] |
304 ]Append[[[page]Get Action]Append["\">\n"]] | 332 ]Append[[[page]Get Action]Append["\">\n"]] |
305 ]Append[Fold[["Render Child"]<String@Worker, "", [page]Children >>]] | 333 ]Append[Fold[Render Child[?], "", [page]Children >>]] |
306 ]Append["\t</form>\n\t</body>\n</html>"] | 334 ]Append["\t</form>\n\t</body>\n</html>"] |
307 If[[page]Use Session>>] | 335 If[[page]Use Session>>] |
308 { | 336 { |
309 headers <- [[page]Session >>]Finalize[Dictionary[]] | 337 headers <- [[page]Session >>]Finalize[Dictionary[]] |
310 }{ | 338 }{ |
348 } | 376 } |
349 | 377 |
350 Decode Helper Decode[list,destlist,index:out] | 378 Decode Helper Decode[list,destlist,index:out] |
351 { | 379 { |
352 code,rest <- [[list]Index[index]]Slice[2] | 380 code,rest <- [[list]Index[index]]Slice[2] |
353 newlist <- [destlist]Set[index, [[""]Put Byte[From Hex@Whole Number[code]]]Append[rest]] | 381 decoded <- String[[Array[]]Append[Trunc UInt8[Abs UInt[Hex Int32[code]]]]] |
382 newlist <- [destlist]Set[index, [decoded]Append[rest]] | |
354 [list]Next[index] | 383 [list]Next[index] |
355 { | 384 { |
356 out <- Decode Helper Decode[list, newlist, ~] | 385 out <- Decode Helper Decode[list, newlist, ~] |
357 }{ | 386 }{ |
358 out <- Val[newlist] | 387 out <- Val[newlist] |
390 { | 419 { |
391 oval <- URL Decode[val] | 420 oval <- URL Decode[val] |
392 okey <- URL Decode[key] | 421 okey <- URL Decode[key] |
393 } | 422 } |
394 | 423 |
395 Process POST[page,con,headers:out] | 424 Process POST[page,con,headers:out,ncon] |
396 { | 425 { |
397 [con]Get FString[[headers]Index["Content-Length"]] {} | 426 ,ncon <- [con]Read[[headers]Index["Content-Length"]] |
398 { | 427 { |
399 post string <- [~]Replace["+"," "] | 428 post string <- [String[~]]Replace["+"," "] |
400 } | 429 } |
401 post data <- Key Value Map[Dict Split[post string, "=", "&"], ["Decode Pair"]<String@Worker] | 430 post data <- Key Value Map[Dict Split[post string, "=", "&"], Decode Pair[?]] |
402 out <- [page]Postback[post data] | 431 out <- [page]Postback[post data] |
403 } | 432 } |
404 | 433 |
405 Postback@Page[page,post data:out,events] | 434 Postback@Page[page,post data:out,events] |
406 { | 435 { |
420 Preformatted | 449 Preformatted |
421 } | 450 } |
422 | 451 |
423 Web Text[text,tag:out] | 452 Web Text[text,tag:out] |
424 { | 453 { |
425 out <- [[[Build["Web Text"]]Text <<[text]]Enclosing Tag <<[tag]]Preformatted <<[No] | 454 out <- [[[Build[Web Text()]]Text <<[text]]Enclosing Tag <<[tag]]Preformatted <<[No] |
426 } | 455 } |
427 | 456 |
428 Name@Web Text[text:out,none] | 457 Name@Web Text[text:out,none] |
429 { | 458 { |
430 none <- text | 459 none <- text |
497 name <- [field]Name >> | 526 name <- [field]Name >> |
498 } | 527 } |
499 | 528 |
500 Web Field[name,value,type:out] | 529 Web Field[name,value,type:out] |
501 { | 530 { |
502 out <- [[[[Build["Web Field"]]Name <<[name]]Value <<[value]]Type <<[type]]Class <<[""] | 531 out <- [[[[Build[Web Field()]]Name <<[name]]Value <<[value]]Type <<[type]]Class <<[""] |
503 } | 532 } |
504 | 533 |
505 Set Session@Web Field[in,session:out] | 534 Set Session@Web Field[in,session:out] |
506 { | 535 { |
507 out <- in | 536 out <- in |
543 Class | 572 Class |
544 } | 573 } |
545 | 574 |
546 Web Button[name,label:out] | 575 Web Button[name,label:out] |
547 { | 576 { |
548 out <- [[[Build["Web Button"]]Name <<[name]]Label <<[label]]Class <<[""] | 577 out <- [[[Build[Web Button()]]Name <<[name]]Label <<[label]]Class <<[""] |
549 } | 578 } |
550 | 579 |
551 Name@Web Button[button:name,none] | 580 Name@Web Button[button:name,none] |
552 { | 581 { |
553 name <- [button]Name >> | 582 name <- [button]Name >> |
583 Dirty | 612 Dirty |
584 } | 613 } |
585 | 614 |
586 Get Unique ID[:out] uses Session | 615 Get Unique ID[:out] uses Session |
587 { | 616 { |
588 out <- [[[::ID]<Whole Number@String]Append["_"]]Append[Random[]] | 617 Print["Get Unique ID"] |
589 ::ID <- [::ID]+[1] | 618 out <- [[String[Session::ID]]Append["_"]]Append[String[Random[]]] |
619 { Session::ID <- [Session::ID]+[1] } | |
590 } | 620 } |
591 | 621 |
592 Session[:out] | 622 Session[:out] |
593 { | 623 { |
594 out <- [[[[Build["Session"]]Session ID <<[Get Unique ID[]]]Use Cookies <<[No]]Data <<[Dictionary[]]]Dirty <<[No] | 624 out <- [[[[Build[Session()]]Session ID <<[Get Unique ID[]]]Use Cookies <<[No]]Data <<[Dictionary[]]]Dirty <<[No] |
595 } | 625 { Print["Built session"] } |
596 | 626 } |
597 Load@Session[queryvars,headers:out] uses Session | 627 |
628 Load Session[queryvars,headers:out] uses Session | |
598 { | 629 { |
599 ,checkquery <- [headers]Index["Cookie"] | 630 ,checkquery <- [headers]Index["Cookie"] |
600 { | 631 { |
601 parts <- Dict Split[~, "=", "; "] | 632 parts <- Dict Split[~, "=", "; "] |
602 ,checkquery <- [parts]Index["session_id"] | 633 ,checkquery <- [parts]Index["session_id"] |
603 { | 634 { |
604 ,checkquery <- [::Sessions]Index[~] | 635 ,checkquery <- [Session::Sessions]Index[~] |
605 { | 636 { |
606 out <- [~]Use Cookies <<[Yes] | 637 out <- [~]Use Cookies <<[Yes] |
607 } | 638 } |
608 } | 639 } |
609 } | 640 } |
611 | 642 |
612 Val[checkquery] | 643 Val[checkquery] |
613 { | 644 { |
614 ,makenew <- [queryvars]Index["session_id"] | 645 ,makenew <- [queryvars]Index["session_id"] |
615 { | 646 { |
616 out, makenew <- [::Sessions]Index[~] | 647 out, makenew <- [Session::Sessions]Index[~] |
617 } | 648 } |
618 } | 649 } |
619 | 650 |
620 Val[makenew] | 651 Val[makenew] |
621 { | 652 { |
653 next,not found <- [[session]Data >>]Next[last] | 684 next,not found <- [[session]Data >>]Next[last] |
654 } | 685 } |
655 | 686 |
656 Init Sessions[:out] uses Session | 687 Init Sessions[:out] uses Session |
657 { | 688 { |
658 ::ID <- 1 | 689 Session::Sessions <- Dictionary[] |
659 ::Sessions <- Dictionary[] | 690 out <- Yes |
691 } | |
692 | |
693 Globals Session | |
694 { | |
695 ID <- 1 | |
696 Sessions <- No | |
660 out <- 0 | 697 out <- 0 |
661 } | 698 } |
662 | 699 |
663 Save@Session[session:out] uses Session | 700 Save@Session[session:out] uses Session |
664 { | 701 { |
665 ::Sessions <- [::Sessions]Set[[session]Session ID >>, session] | 702 Session::Sessions <- [Session::Sessions]Set[[session]Session ID >>, session] |
666 } | 703 } |
667 | 704 |
668 Finalize@Session[session,headers:out headers] | 705 Finalize@Session[session,headers:out headers] |
669 { | 706 { |
670 If[[session]Dirty >>] | 707 If[[session]Dirty >>] |
682 Query Params | 719 Query Params |
683 } | 720 } |
684 | 721 |
685 Web Link[text,target:out] | 722 Web Link[text,target:out] |
686 { | 723 { |
687 out <- [[[[Build["Web Link"]]Text <<[text]]Target <<[target]]Class <<[""]]Query Params <<[Dictionary[]] | 724 out <- [[[[Build[Web Link()]]Text <<[text]]Target <<[target]]Class <<[""]]Query Params <<[Dictionary[]] |
688 } | 725 } |
689 | 726 |
690 | 727 |
691 With Session@Web Link[text,target,session:out] | 728 With Session@Web Link[text,target,session:out] |
692 { | 729 { |
740 Data | 777 Data |
741 } | 778 } |
742 | 779 |
743 Web Table[headers,data:out] | 780 Web Table[headers,data:out] |
744 { | 781 { |
745 out <- [[Build["Web Table"]]Headers <<[headers]]Data <<[data] | 782 out <- [[Build[Web Table()]]Headers <<[headers]]Data <<[data] |
746 } | 783 } |
747 | 784 |
748 Name@Web Table[link:name,none] | 785 Name@Web Table[link:name,none] |
749 { | 786 { |
750 none <- link | 787 none <- link |
768 | 805 |
769 Get Header Row@Web Table[table:out] | 806 Get Header Row@Web Table[table:out] |
770 { | 807 { |
771 If[[[[table]Headers >>]Length] > [0]] | 808 If[[[[table]Headers >>]Length] > [0]] |
772 { | 809 { |
773 out <- [Fold[["Make Header Row"]<String@Worker, "\t\t<tr>\n", [table]Headers >>]]Append["\t\t</tr>\n"] | 810 out <- [Fold[Make Header Row[?], "\t\t<tr>\n", [table]Headers >>]]Append["\t\t</tr>\n"] |
774 }{ | 811 }{ |
775 out <- "" | 812 out <- "" |
776 } | 813 } |
777 } | 814 } |
778 | 815 |
781 out <- [[[string]Append["\t\t\t<td>"]]Append[[cell]Render]]Append["</td>\n"] | 818 out <- [[[string]Append["\t\t\t<td>"]]Append[[cell]Render]]Append["</td>\n"] |
782 } | 819 } |
783 | 820 |
784 Make Table Row[string,row:out] | 821 Make Table Row[string,row:out] |
785 { | 822 { |
786 out <- [Fold[["Make Table Cell"]<String@Worker, [string]Append["\t\t<tr>\n"], row]]Append["\t\t</tr>"] | 823 out <- [Fold[Make Table Cell[?], [string]Append["\t\t<tr>\n"], row]]Append["\t\t</tr>"] |
787 } | 824 } |
788 | 825 |
789 Render@Web Table[table:out,headers] | 826 Render@Web Table[table:out,headers] |
790 { | 827 { |
791 out <- [ | 828 out <- [ |
792 [ | 829 [ |
793 ["\t<table>\n"]Append[[table]Get Header Row] | 830 ["\t<table>\n"]Append[[table]Get Header Row] |
794 ]Append[ Fold[["Make Table Row"]<String@Worker, "", [table]Data >>] ] | 831 ]Append[ Fold[Make Table Row[?], "", [table]Data >>] ] |
795 ]Append["\t</table>\n"] | 832 ]Append["\t</table>\n"] |
796 } | 833 } |
797 | 834 |
798 Blueprint Web Image | 835 Blueprint Web Image |
799 { | 836 { |
800 Source | 837 Source |
801 Alt | 838 Alt |
802 } | 839 } |
803 | 840 |
804 New@Web Image[source,alt:out] | 841 Web Image[source,alt:out] |
805 { | 842 { |
806 out <- [[Build["Web Image"]]Source <<[source]]Alt <<[alt] | 843 out <- [[Build[Web Image()]]Source <<[source]]Alt <<[alt] |
807 } | 844 } |
808 | 845 |
809 Name@Web Image[image:name,none] | 846 Name@Web Image[image:name,none] |
810 { | 847 { |
811 name <- [image]Source >> | 848 name <- [image]Source >> |