Advertisement
Guest User

Untitled

a guest
Jul 21st, 2017
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.47 KB | None | 0 0
  1. _sync "VDOM : The Virtual DOM implementation in Ironscript"
  2.  
  3. (_include "stdlib")
  4.  
  5. [defun h (type props :children) {
  6. [.props = props]
  7.  
  8. [parse-type-string = @{
  9. var type = args[0];
  10. var idDelim = args[1];
  11. var classDelim = args[2];
  12. if (!idDelim) idDelim = '#';
  13. if (!classDelim) classDelim = '.';
  14.  
  15. function parseClass (str) {
  16. var tarr = str.split(classDelim);
  17. var type = "div";
  18. if (classDelim !== str[0]) type = tarr[0];
  19. return [type, tarr.slice(1).join(' ')];
  20. }
  21.  
  22. function parseId (str) {
  23. var tarr = str.split(idDelim);
  24. if (idDelim === str[0]) return ["div", "", tarr[1]];
  25. else if (tarr.length > 1) {
  26. let ret = parseClass(tarr[0]);
  27. ret.push(tarr[1]);
  28. return ret;
  29. }
  30. else return parseClass(tarr[0]);
  31. }
  32. $return (parseId(type));
  33. }@ ]
  34.  
  35. [t = (parse-type-string type) ]
  36. [if ["object" === (typeof t)]
  37. then (_begin
  38. [.type = t.0]
  39. [props.class = t.1]
  40. [props.id = t.2]
  41. )
  42. else [.type = t]
  43. ]
  44. [if children [.children = (_seq :children)] ]
  45. } ]
  46.  
  47. [_rho (H @tag) (h @tag {}) ]
  48. [_rho (H @tag @props) (h @tag @props) ]
  49. [_rho (H @tag @props :@children) (h @tag @props :@children) ]
  50. [_rho (T @tag :@children) (h @tag {} :@children) ]
  51. [_rho (T @tag) (h @tag {}) ]
  52.  
  53.  
  54. [COMPONENTS = {}]
  55. [NOOP = (_fn () NIL) ]
  56. [PORT = (_fn () (_stream NOOP NIL) ) ]
  57.  
  58.  
  59. [defun vnode (node id) (_begin
  60. [type = node.type]
  61. [if type
  62. then (_begin
  63. [componentfn = (_dot COMPONENTS type)]
  64. [if componentfn
  65. then (_begin
  66. [nd = (componentfn node.props)]
  67. [if node.props.id then (vnode nd node.props.id) else (vnode nd ".")]
  68. )
  69. else {
  70. [.type = type]
  71. [.props = node.props]
  72. [if .props.id then [.id = .props.id] else [.id = "."] ]
  73. [if node.children
  74. [.children = (_map node.children (_fn (child index)
  75. (vnode child (concat .id "." index) )
  76. ) ) ]
  77. ]
  78. }
  79. ]
  80. )
  81. else node
  82. ]
  83. )]
  84.  
  85.  
  86. [EVENTS = {
  87. [init = @{
  88. var capt = true;
  89. if (args[0] === "false") capt = false;
  90. ("keypress keydown keyup click dbclick scroll focus blur change search select submit input invalid".split(" "))
  91. .forEach (function (e) {
  92. document.body.addEventListener(e, function (ev) {$yield(ev); ev.stopPropagation();}, capt) ;
  93. });
  94. }@ ]
  95.  
  96. [.event-stream = (_stream init "true")]
  97.  
  98. [listeners = {}]
  99. [defun .listen (id event port)
  100. [(_dot listeners (concat id ":" event) ) = port]
  101. ]
  102. [defun .mute (id event) [(concat id ":" event) = NIL] ]
  103. [defun .remove (id) ( @{
  104. var obj = args[0];
  105. var id = args[1];
  106. Object.keys(obj).forEach(function (name) {
  107. if (name.startsWith(id+'.') || name.startsWith(id+':')) obj[name] = undefined;
  108. });
  109. }@ listeners id ) ]
  110.  
  111. (_do (_on .event-stream
  112. (_begin
  113. [e = (_pull .event-stream)]
  114. [port = (_dot listeners (concat e.target.id ":" e.type ) ) ]
  115. [if port (_push port e)]
  116. )
  117. ) )
  118. } ]
  119.  
  120.  
  121. [update-fn-internal = (_fx (
  122. @{
  123.  
  124. function isEventProp (name) { return /^on/.test(name); }
  125. function extractEventName (name) { return name.slice(2).toLowerCase(); }
  126.  
  127. function isCustomProp (name) {
  128. if (name === "id") return true;
  129. if (isEventProp(name)) return true;
  130. return false;
  131. }
  132.  
  133. function setBooleanProp ($target, name, value) {
  134. if (value) $target.setAttribute(name, value);
  135. $target[name] = value;
  136. }
  137.  
  138. function setProp ($target, name, value) {
  139. if (isCustomProp(name)) return;
  140. else if (typeof value === 'boolean') setBooleanProp ($target, name, value);
  141. else $target.setAttribute (name, value);
  142. }
  143.  
  144. function setProps ($target, props) {
  145. if (typeof props === 'object') Object.keys(props).forEach(function (name) {
  146. setProp($target, name, props[name]);
  147. });
  148. }
  149.  
  150. function removeBooleanProp ($target, name) {
  151. $target.removeAttribute (name);
  152. $target[name] = false;
  153. }
  154.  
  155. function removeProp ($target, name, value) {
  156. if (isCustomProp(name)) return;
  157. else if (typeof value === 'boolean') removeBooleanProp ($target, name);
  158. else $target.removeAttribute (name);
  159. }
  160.  
  161. function updateProp ($target, name, newVal, oldVal) {
  162. if (!newVal) removeProp ($target, name, oldVal);
  163. else if (!oldval || newVal !== oldVal) setProp ($target, name, newVal);
  164. else return;
  165. }
  166.  
  167. function updateProps ($target, newProps, oldProps) {
  168. if (!oldProps) oldProps = {};
  169. var props = Object.assign ({}, newProps, oldProps);
  170. Object.keys(props).forEach (function (name) {
  171. updateProp ($target, name, newProps[name], oldProps[name]);
  172. });
  173. }
  174.  
  175. function addEventListeners (id, props) {
  176. if (typeof props === 'object') Object.keys(props).forEach(function (name) {
  177. if (isEventProp(name)) addPatch.push({id: id, type: extractEventName(name), port: props[name]});
  178. });
  179. }
  180.  
  181. function createElement (node) {
  182. if (typeof node === 'string') return document.createTextNode(node);
  183. var $el = document.createElement (node.type);
  184. $el.setAttribute("id", node.id);
  185. setProps ($el, node.props);
  186. addEventListeners(node.id, node.props);
  187. if (node.children) node.children.arr.map(createElement).forEach($el.appendChild.bind($el));
  188. return $el;
  189. }
  190.  
  191. function changed (node1, node2) {
  192. return typeof node1 !== typeof node2
  193. || typeof node1 === 'string' && node1 !== node2
  194. || node1.type !== node2.type
  195. || node1.id !== node2.id;
  196. }
  197.  
  198. function Patch () {
  199. this.arr = [];
  200. this.push = function (x) {this.arr.push(x); }.bind(this);
  201. }
  202.  
  203. var addPatch = [];
  204. var removePatch = [];
  205.  
  206. function updateElement ($par, newNode, oldNode, index=0) {
  207. if (!oldNode) {
  208. $par.appendChild (createElement (newNode));
  209. }
  210. else if (!newNode) {
  211. removePatch.push(oldNode.id);
  212. $par.removeChild($par.childNodes[index]);
  213. }
  214. else if (changed(newNode, oldNode)) {
  215. removePatch.push(oldNode.id);
  216. $par.replaceChild(createElement(newNode), $par.childNodes[index]);
  217. }
  218. else if (newNode.type) {
  219. updateProps($par.childNodes[index], newNode.props, oldNode.props);
  220. var newLen = newNode.children.arr.length;
  221. var oldLen = oldNode.children.arr.length;
  222.  
  223. for (var i=0; i<newLen || i<oldLen; i++) {
  224. updateElement ($par.childNodes[index], newNode.children.arr[i], oldNode.children.arr[i], i);
  225. }
  226. }
  227. }
  228.  
  229. function update (id, newNode, oldNode) {
  230. var $par = document.getElementById (id);
  231. addPatch = [];
  232. removePatch = [];
  233. updateElement ($par, newNode, oldNode);
  234. return {add:addPatch, remove:removePatch};
  235. }
  236. $return (update);
  237. }@ ) ) ]
  238.  
  239.  
  240. [defun update (id newNode oldNode)
  241. (_begin
  242. [vOldNode = [if oldNode then (vnode oldNode) else NIL]]
  243. [vNewNode = [if newNode then (vnode newNode) else NIL]]
  244. [patches = (update-fn-internal id vNewNode vOldNode ) ]
  245. (_map patches.remove (_fn (item) (EVENTS.remove item) ) )
  246. (_map patches.add (_fn (item) (EVENTS.listen item.id item.type item.port) ) )
  247. )
  248. ]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement