Advertisement
Guest User

Untitled

a guest
Jul 7th, 2015
221
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.13 KB | None | 0 0
  1. function View(template, model, parentNode) {
  2. this.model = model
  3.  
  4. this.parentNode = null
  5. this.bindings = {}
  6.  
  7. this.node = document.importNode(template.content, true)
  8. this.childNodesSlice = Array.prototype.slice.apply(this.node.childNodes)
  9. this.discoverNodes(this.node.childNodes)
  10. this.setupObserve()
  11.  
  12. if (parentNode) {
  13. this.mount(parentNode)
  14. }
  15. }
  16.  
  17. View.prototype.discoverNodes = function(nodes) {
  18. for (var i = 0; i < nodes.length; i++) {
  19. this.discoverNode(nodes[i])
  20. }
  21. }
  22.  
  23. View.prototype.discoverNode = function(node) {
  24. switch (node.nodeType) {
  25. case Node.ELEMENT_NODE:
  26. this.discoverElementNode(node)
  27. break
  28. case Node.ATTRIBUTE_NODE:
  29. this.discoverAttributeNode(node)
  30. break
  31. case Node.TEXT_NODE:
  32. this.discoverTextNode(node)
  33. break
  34. default:
  35. throw new Error("incompatible node came into View.discoverNode")
  36. }
  37. }
  38.  
  39. View.prototype.discoverElementNode = function(node) {
  40. if (node.tagName === "TEMPLATE") {
  41. this.discoverTemplateNode(node)
  42. } else {
  43. this.discoverNodes(node.attributes)
  44. this.discoverNodes(node.childNodes)
  45. }
  46. }
  47.  
  48. View.prototype.discoverAttributeNode = function(node) {
  49. var textFragments = node.value.split(/(\{\{.+?\}\})/g).filter(function(s){
  50. return s !== ""
  51. })
  52.  
  53. var hasExpression = false
  54.  
  55. var expression = textFragments.map(function(s){
  56. m = s.match(/^\{\{(.+)\}\}$/)
  57. if (m) {
  58. hasExpression = true
  59. return m[1]
  60. } else {
  61. return JSON.stringify(s)
  62. }
  63. }).join(" + ")
  64.  
  65. if (hasExpression) {
  66. this.bindNode(node, expression)
  67. }
  68. }
  69.  
  70. View.prototype.discoverTextNode = function(node) {
  71. var textFragments = node.data.split(/(\{\{.+?\}\})/g).filter(function(s){
  72. return s !== ""
  73. })
  74. if (textFragments.length > 1) {
  75. for (var i in textFragments) {
  76. var s = textFragments[i]
  77. var textNode = document.createTextNode(s)
  78. m = s.match(/^\{\{(.+)\}\}$/)
  79. if (m) this.bindNode(textNode, m[1])
  80. node.parentNode.insertBefore(textNode, node)
  81. }
  82. node.parentNode.removeChild(node)
  83. } else if (textFragments.length === 1) {
  84. var s = textFragments[0]
  85. m = s.match(/^\{\{(.+)\}\}$/)
  86. if (m) this.bindNode(node, m[1])
  87. }
  88. }
  89.  
  90. View.prototype.discoverTemplateNode = function(node) {
  91. var mountPoint = document.createTextNode("")
  92. node.parentNode.insertBefore(mountPoint, node)
  93. node.remove()
  94. node.mountPoint = mountPoint
  95. this.discoverNodes(node.attributes)
  96. }
  97.  
  98. View.prototype.applyNodeValue = function(node, value) {
  99. switch (node.nodeType) {
  100. case Node.ATTRIBUTE_NODE:
  101. if (node.ownerElement.tagName === "TEMPLATE") {
  102. var template = node.ownerElement
  103. switch (node.name) {
  104. case "if":
  105. if (!!value) {
  106. template.mountPoint.renderedView = new View(
  107. template, this.model, template.mountPoint)
  108. } else {
  109. if (template.mountPoint.renderedView) {
  110. template.mountPoint.renderedView.destroy()
  111. }
  112. }
  113. break;
  114. case "loop":
  115. if (!!value && value instanceof Array && value.length > 0) {
  116. template.mountPoint.renderedViews = value.map(function(v) {
  117. return new View(template, v, template.mountPoint)
  118. })
  119. } else {
  120. if (template.mountPoint.renderedViews) {
  121. template.mountPoint.renderedViews.forEach(function(view){
  122. view.destroy()
  123. })
  124. }
  125. }
  126. break
  127. default:
  128. throw new Error("don't know how to apply value on " + node.name + " attribute of template")
  129. }
  130. } else {
  131. node.value = value
  132. }
  133. break
  134. case Node.TEXT_NODE:
  135. node.data = value
  136. break
  137. default:
  138. throw new Error("incompatible node came into View.applyNodeValue")
  139. }
  140. }
  141.  
  142. View.prototype.bindNode = function(node, expression) {
  143. if (!this.bindings[expression]) {
  144. this.bindings[expression] = []
  145. }
  146. this.bindings[expression].push(node)
  147. this.applyNodeValue(node, evalInContext(expression, this.model))
  148. }
  149.  
  150. View.prototype.setupObserve = function() {
  151. if (!this.model) {
  152. return
  153. }
  154. var model = this.model
  155. var bindings = this.bindings
  156. var applyNodeValue = this.applyNodeValue.bind(this)
  157. // to be written
  158. Object.observe(model, function(changes){
  159. changes.forEach(function(change){
  160. if (change.name in bindings) {
  161. bindings[change.name].forEach(function(node){
  162. applyNodeValue(node, evalInContext(change.name, model))
  163. })
  164. }
  165. })
  166. })
  167. }
  168.  
  169. View.prototype.unmount = function() {
  170. if (!this.parentNode) {
  171. return
  172. }
  173. for (var i = 0; i < this.childNodesSlice.length; i++) {
  174. this.childNodesSlice[i].remove()
  175. }
  176. }
  177.  
  178. View.prototype.destroy = function() {
  179. this.unmount()
  180. // to be written
  181. }
  182.  
  183. View.prototype.mount = function(parentNode) {
  184. if (parentNode.nodeType === Node.TEXT_NODE) {
  185. parentNode.parentNode.insertBefore(this.node, parentNode)
  186. this.parentNode = parentNode.parentNode
  187. } else {
  188. parentNode.appendChild(this.node)
  189. this.parentNode = parentNode
  190. }
  191. }
  192.  
  193. View.queryTemplate = function(templateName, rootDocument) {
  194. rootDocument = rootDocument || document
  195. var template = rootDocument.querySelector("template[name='" + templateName + "']")
  196. if (template) {
  197. return template
  198. }
  199. imports = rootDocument.querySelectorAll('link[rel="import"]')
  200. for (var i = 0; i < imports.length; i++) {
  201. template = View.queryTemplate(templateName, imports[i].import)
  202. if (template) {
  203. return template
  204. }
  205. }
  206. throw new Error("cannot find template " + templateName)
  207. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement