each.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. var config = require('../config'),
  2. utils = require('../utils'),
  3. ViewModel // lazy def to avoid circular dependency
  4. /*
  5. * Mathods that perform precise DOM manipulation
  6. * based on mutator method triggered
  7. */
  8. var mutationHandlers = {
  9. push: function (m) {
  10. var i, l = m.args.length,
  11. baseIndex = this.collection.length - l
  12. for (i = 0; i < l; i++) {
  13. this.buildItem(this.ref, m.args[i], baseIndex + i)
  14. }
  15. },
  16. pop: function (m) {
  17. m.result.$destroy()
  18. },
  19. unshift: function (m) {
  20. var i, l = m.args.length, ref
  21. for (i = 0; i < l; i++) {
  22. ref = this.collection.length > l
  23. ? this.collection[l].$el
  24. : this.ref
  25. this.buildItem(ref, m.args[i], i)
  26. }
  27. this.updateIndexes()
  28. },
  29. shift: function (m) {
  30. m.result.$destroy()
  31. this.updateIndexes()
  32. },
  33. splice: function (m) {
  34. var i, pos, ref,
  35. l = m.args.length,
  36. k = m.result.length,
  37. index = m.args[0],
  38. removed = m.args[1],
  39. added = l - 2
  40. for (i = 0; i < k; i++) {
  41. m.result[i].$destroy()
  42. }
  43. if (added > 0) {
  44. for (i = 2; i < l; i++) {
  45. pos = index - removed + added + 1
  46. ref = this.collection[pos]
  47. ? this.collection[pos].$el
  48. : this.ref
  49. this.buildItem(ref, m.args[i], index + i)
  50. }
  51. }
  52. if (removed !== added) {
  53. this.updateIndexes()
  54. }
  55. },
  56. sort: function () {
  57. var i, l = this.collection.length, viewmodel
  58. for (i = 0; i < l; i++) {
  59. viewmodel = this.collection[i]
  60. viewmodel.$index = i
  61. this.container.insertBefore(viewmodel.$el, this.ref)
  62. }
  63. }
  64. }
  65. //mutationHandlers.reverse = mutationHandlers.sort
  66. module.exports = {
  67. bind: function () {
  68. this.el.removeAttribute(config.prefix + '-each')
  69. var ctn = this.container = this.el.parentNode
  70. // create a comment node as a reference node for DOM insertions
  71. this.ref = document.createComment('sd-each-' + this.arg)
  72. ctn.insertBefore(this.ref, this.el)
  73. ctn.removeChild(this.el)
  74. this.collection = null
  75. this.vms = null
  76. this.mutationListener = (function (mutation) {
  77. mutationHandlers[mutation.method].call(this, mutation)
  78. }).bind(this)
  79. },
  80. update: function (collection) {
  81. this.unbind(true)
  82. // attach an object to container to hold handlers
  83. this.container.sd_dHandlers = {}
  84. // if initiating with an empty collection, we need to
  85. // force a compile so that we get all the bindings for
  86. // dependency extraction.
  87. if (!this.collection && !collection.length) {
  88. this.buildItem(this.ref, null, null)
  89. }
  90. this.collection = collection
  91. this.vms = []
  92. // listen for collection mutation events
  93. // the collection has been augmented during Binding.set()
  94. collection.__observer__.on('mutate', this.mutationListener)
  95. // create child-seeds and append to DOM
  96. for (var i = 0, l = collection.length; i < l; i++) {
  97. this.buildItem(this.ref, collection[i], i)
  98. }
  99. },
  100. buildItem: function (ref, data, index) {
  101. var node = this.el.cloneNode(true)
  102. this.container.insertBefore(node, ref)
  103. ViewModel = ViewModel || require('../viewmodel')
  104. var vmID = node.getAttribute(config.prefix + '-viewmodel'),
  105. ChildVM = utils.getVM(vmID) || ViewModel
  106. var item = new ChildVM({
  107. el: node,
  108. each: true,
  109. eachPrefix: this.arg + '.',
  110. parentCompiler: this.compiler,
  111. index: index,
  112. delegator: this.container,
  113. data: {
  114. todo: data
  115. }
  116. })
  117. if (index) {
  118. this.vms[index] = item
  119. } else {
  120. item.$destroy()
  121. }
  122. },
  123. updateIndexes: function () {
  124. var i = this.collection.length
  125. while (i--) {
  126. this.collection[i].$index = i
  127. }
  128. },
  129. unbind: function () {
  130. if (this.collection) {
  131. this.collection.off('mutate', this.mutationListener)
  132. var i = this.collection.length
  133. while (i--) {
  134. this.collection[i].$destroy()
  135. }
  136. }
  137. var ctn = this.container,
  138. handlers = ctn.sd_dHandlers
  139. for (var key in handlers) {
  140. ctn.removeEventListener(handlers[key].event, handlers[key])
  141. }
  142. ctn.sd_dHandlers = null
  143. }
  144. }