saneObjectToDom.coffee 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. module.exports = self =
  2. convert: (obj) ->
  3. self._arrayToChildren obj
  4. _arrayToChildren: (a, parent = null) ->
  5. children = []
  6. prev = null
  7. for v in a
  8. if typeof v is 'string'
  9. node = self._getTextNodeFor v
  10. else
  11. node = self._objectToNode v, parent
  12. node.prev = null
  13. node.next = null
  14. node.parent = parent
  15. if prev?
  16. node.prev = prev
  17. prev.next = node
  18. prev = node
  19. children.push node
  20. children
  21. _objectToNode: (o) ->
  22. i = 0
  23. for own k, v of o
  24. if i > 0
  25. throw Error "_objectToNode() only accepts an object with one key/value"
  26. key = k
  27. val = v
  28. i++
  29. node = {}
  30. if typeof key isnt 'string'
  31. throw Error "_objectToNode()'s key must be a string of tag name and classes"
  32. if typeof val is 'string'
  33. children = [self._getTextNodeFor(val)]
  34. else if Array.isArray val
  35. children = self._arrayToChildren val, node
  36. else
  37. inspect o
  38. throw Error "_objectToNode()'s key's value must only be a string or an array"
  39. node.type = 'tag'
  40. {name, attribs} = self._parseTag key
  41. node.name = name
  42. node.attribs = attribs
  43. node.children = children
  44. node
  45. _getTextNodeFor: (s) ->
  46. {type: 'text', data: s}
  47. _nameRx: /^[a-zA-Z\-\_]{1}[a-zA-Z0-9\-\_]*$/
  48. _parseTag: (k) ->
  49. # validate
  50. if not k.match(/^[a-zA-Z0-9\#\-\_\.\[\]\"\'\=\,\s]+$/) or k.match(/^[0-9]+/)
  51. throw Error "cannot parse tag `#{k}`"
  52. attribs = {}
  53. parts =
  54. name: ''
  55. attribs: attribs
  56. # tag name
  57. if m = k.match /^([^\.#]+)/
  58. name = m[1]
  59. unless name.match self._nameRx
  60. throw Error "tag name `#{name}` is not valid"
  61. parts.name = name
  62. k = k.substr name.length, k.length
  63. # tag id
  64. if m = k.match /^#([a-zA-Z0-9\-]+)/
  65. id = m[1]
  66. unless id.match self._nameRx
  67. throw Error "tag id `#{id}` is not valid"
  68. attribs.id = id
  69. k = k.substr id.length + 1, k.length
  70. classes = []
  71. # the class attrib
  72. while m = k.match /\.([a-zA-Z0-9\-\_]+)/
  73. cls = m[1]
  74. unless cls.match self._nameRx
  75. throw Error "tag class `#{cls}` is not valid"
  76. classes.push cls
  77. k = k.replace '.' + cls, ''
  78. if classes.length
  79. attribs.class = classes.join " "
  80. # TODO: match attributes like [a=b]
  81. parts