main.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. var prefix = 'sd',
  2. Filters = require('./filters'),
  3. Directives = require('./directives'),
  4. selector = Object.keys(Directives).map(function (d) {
  5. return '[' + prefix + '-' + d + ']'
  6. }).join()
  7. function Seed (opts) {
  8. var self = this,
  9. root = this.el = document.getElementById(opts.id),
  10. els = root.querySelectorAll(selector)
  11. var bindings = self._bindings = {} // internal real data
  12. self.scope = {} // external interface
  13. // process nodes for directives
  14. ;[].forEach.call(els, processNode)
  15. processNode(root)
  16. // initialize all variables by invoking setters
  17. for (var key in bindings) {
  18. self.scope[key] = opts.scope[key]
  19. }
  20. function processNode (el) {
  21. cloneAttributes(el.attributes).forEach(function (attr) {
  22. var directive = parseDirective(attr)
  23. if (directive) {
  24. bindDirective(self, el, bindings, directive)
  25. }
  26. })
  27. }
  28. }
  29. Seed.prototype.dump = function () {
  30. var data = {}
  31. for (var key in this._bindings) {
  32. data[key] = this._bindings[key].value
  33. }
  34. return data
  35. }
  36. Seed.prototype.destroy = function () {
  37. for (var key in this._bindings) {
  38. this._bindings[key].directives.forEach(function (directive) {
  39. if (directive.definition.unbind) {
  40. directive.definition.unbind(
  41. directive.el,
  42. directive.argument,
  43. directive
  44. )
  45. }
  46. })
  47. }
  48. this.el.parentNode.remove(this.el)
  49. }
  50. // clone attributes so they don't change
  51. function cloneAttributes (attributes) {
  52. return [].map.call(attributes, function (attr) {
  53. return {
  54. name: attr.name,
  55. value: attr.value
  56. }
  57. })
  58. }
  59. function bindDirective (seed, el, bindings, directive) {
  60. directive.el = el
  61. el.removeAttribute(directive.attr.name)
  62. var key = directive.key,
  63. binding = bindings[key]
  64. if (!binding) {
  65. bindings[key] = binding = {
  66. value: undefined,
  67. directives: []
  68. }
  69. }
  70. binding.directives.push(directive)
  71. // invoke bind hook if exists
  72. if (directive.bind) {
  73. directive.bind(el, binding.value)
  74. }
  75. if (!seed.scope.hasOwnProperty(key)) {
  76. bindAccessors(seed, key, binding)
  77. }
  78. }
  79. function bindAccessors (seed, key, binding) {
  80. Object.defineProperty(seed.scope, key, {
  81. get: function () {
  82. return binding.value
  83. },
  84. set: function (value) {
  85. binding.value = value
  86. binding.directives.forEach(function (directive) {
  87. var filteredValue = value
  88. if (value && directive.filters) {
  89. filteredValue = applyFilters(value, directive)
  90. }
  91. directive.update(
  92. directive.el,
  93. filteredValue,
  94. directive.argument,
  95. directive,
  96. seed
  97. )
  98. })
  99. }
  100. })
  101. }
  102. function parseDirective (attr) {
  103. if (attr.name.indexOf(prefix) === -1) return
  104. // parse directive name and argument
  105. var noprefix = attr.name.slice(prefix.length + 1),
  106. argIndex = noprefix.indexOf('-'),
  107. dirname = argIndex === -1
  108. ? noprefix
  109. : noprefix.slice(0, argIndex),
  110. def = Directives[dirname],
  111. arg = argIndex === -1
  112. ? null
  113. : noprefix.slice(argIndex + 1)
  114. // parse scope variable key and pipe filters
  115. var exp = attr.value,
  116. pipeIndex = exp.indexOf('|'),
  117. key = pipeIndex === -1
  118. ? exp.trim()
  119. : exp.slice(0, pipeIndex).trim(),
  120. filters = pipeIndex === -1
  121. ? null
  122. : exp.slice(pipeIndex + 1).split('|').map(function (filter) {
  123. return filter.trim()
  124. })
  125. return def
  126. ? {
  127. attr: attr,
  128. key: key,
  129. filters: filters,
  130. definition: def,
  131. argument: arg,
  132. update: typeof def === 'function'
  133. ? def
  134. : def.update
  135. }
  136. : null
  137. }
  138. function applyFilters (value, directive) {
  139. if (directive.definition.customFilter) {
  140. return directive.definition.customFilter(value, directive.filters)
  141. } else {
  142. directive.filters.forEach(function (filter) {
  143. if (Filters[filter]) {
  144. value = Filters[filter](value)
  145. }
  146. })
  147. return value
  148. }
  149. }
  150. module.exports = {
  151. create: function (opts) {
  152. return new Seed(opts)
  153. },
  154. filters: Filters,
  155. directives: Directives
  156. }