[d24f17c] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | var find = require('property-information/find')
|
---|
| 4 | var normalize = require('property-information/normalize')
|
---|
| 5 | var parseSelector = require('hast-util-parse-selector')
|
---|
| 6 | var spaces = require('space-separated-tokens').parse
|
---|
| 7 | var commas = require('comma-separated-tokens').parse
|
---|
| 8 |
|
---|
| 9 | module.exports = factory
|
---|
| 10 |
|
---|
| 11 | var own = {}.hasOwnProperty
|
---|
| 12 |
|
---|
| 13 | function factory(schema, defaultTagName, caseSensitive) {
|
---|
| 14 | var adjust = caseSensitive ? createAdjustMap(caseSensitive) : null
|
---|
| 15 |
|
---|
| 16 | return h
|
---|
| 17 |
|
---|
| 18 | // Hyperscript compatible DSL for creating virtual hast trees.
|
---|
| 19 | function h(selector, properties) {
|
---|
| 20 | var node = parseSelector(selector, defaultTagName)
|
---|
| 21 | var children = Array.prototype.slice.call(arguments, 2)
|
---|
| 22 | var name = node.tagName.toLowerCase()
|
---|
| 23 | var property
|
---|
| 24 |
|
---|
| 25 | node.tagName = adjust && own.call(adjust, name) ? adjust[name] : name
|
---|
| 26 |
|
---|
| 27 | if (properties && isChildren(properties, node)) {
|
---|
| 28 | children.unshift(properties)
|
---|
| 29 | properties = null
|
---|
| 30 | }
|
---|
| 31 |
|
---|
| 32 | if (properties) {
|
---|
| 33 | for (property in properties) {
|
---|
| 34 | addProperty(node.properties, property, properties[property])
|
---|
| 35 | }
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | addChild(node.children, children)
|
---|
| 39 |
|
---|
| 40 | if (node.tagName === 'template') {
|
---|
| 41 | node.content = {type: 'root', children: node.children}
|
---|
| 42 | node.children = []
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | return node
|
---|
| 46 | }
|
---|
| 47 |
|
---|
| 48 | function addProperty(properties, key, value) {
|
---|
| 49 | var info
|
---|
| 50 | var property
|
---|
| 51 | var result
|
---|
| 52 |
|
---|
| 53 | // Ignore nullish and NaN values.
|
---|
| 54 | if (value === null || value === undefined || value !== value) {
|
---|
| 55 | return
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | info = find(schema, key)
|
---|
| 59 | property = info.property
|
---|
| 60 | result = value
|
---|
| 61 |
|
---|
| 62 | // Handle list values.
|
---|
| 63 | if (typeof result === 'string') {
|
---|
| 64 | if (info.spaceSeparated) {
|
---|
| 65 | result = spaces(result)
|
---|
| 66 | } else if (info.commaSeparated) {
|
---|
| 67 | result = commas(result)
|
---|
| 68 | } else if (info.commaOrSpaceSeparated) {
|
---|
| 69 | result = spaces(commas(result).join(' '))
|
---|
| 70 | }
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | // Accept `object` on style.
|
---|
| 74 | if (property === 'style' && typeof value !== 'string') {
|
---|
| 75 | result = style(result)
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | // Class-names (which can be added both on the `selector` and here).
|
---|
| 79 | if (property === 'className' && properties.className) {
|
---|
| 80 | result = properties.className.concat(result)
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | properties[property] = parsePrimitives(info, property, result)
|
---|
| 84 | }
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | function isChildren(value, node) {
|
---|
| 88 | return (
|
---|
| 89 | typeof value === 'string' ||
|
---|
| 90 | 'length' in value ||
|
---|
| 91 | isNode(node.tagName, value)
|
---|
| 92 | )
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | function isNode(tagName, value) {
|
---|
| 96 | var type = value.type
|
---|
| 97 |
|
---|
| 98 | if (tagName === 'input' || !type || typeof type !== 'string') {
|
---|
| 99 | return false
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | if (typeof value.children === 'object' && 'length' in value.children) {
|
---|
| 103 | return true
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | type = type.toLowerCase()
|
---|
| 107 |
|
---|
| 108 | if (tagName === 'button') {
|
---|
| 109 | return (
|
---|
| 110 | type !== 'menu' &&
|
---|
| 111 | type !== 'submit' &&
|
---|
| 112 | type !== 'reset' &&
|
---|
| 113 | type !== 'button'
|
---|
| 114 | )
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | return 'value' in value
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | function addChild(nodes, value) {
|
---|
| 121 | var index
|
---|
| 122 | var length
|
---|
| 123 |
|
---|
| 124 | if (typeof value === 'string' || typeof value === 'number') {
|
---|
| 125 | nodes.push({type: 'text', value: String(value)})
|
---|
| 126 | return
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | if (typeof value === 'object' && 'length' in value) {
|
---|
| 130 | index = -1
|
---|
| 131 | length = value.length
|
---|
| 132 |
|
---|
| 133 | while (++index < length) {
|
---|
| 134 | addChild(nodes, value[index])
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | return
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | if (typeof value !== 'object' || !('type' in value)) {
|
---|
| 141 | throw new Error('Expected node, nodes, or string, got `' + value + '`')
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | nodes.push(value)
|
---|
| 145 | }
|
---|
| 146 |
|
---|
| 147 | // Parse a (list of) primitives.
|
---|
| 148 | function parsePrimitives(info, name, value) {
|
---|
| 149 | var index
|
---|
| 150 | var length
|
---|
| 151 | var result
|
---|
| 152 |
|
---|
| 153 | if (typeof value !== 'object' || !('length' in value)) {
|
---|
| 154 | return parsePrimitive(info, name, value)
|
---|
| 155 | }
|
---|
| 156 |
|
---|
| 157 | length = value.length
|
---|
| 158 | index = -1
|
---|
| 159 | result = []
|
---|
| 160 |
|
---|
| 161 | while (++index < length) {
|
---|
| 162 | result[index] = parsePrimitive(info, name, value[index])
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | return result
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | // Parse a single primitives.
|
---|
| 169 | function parsePrimitive(info, name, value) {
|
---|
| 170 | var result = value
|
---|
| 171 |
|
---|
| 172 | if (info.number || info.positiveNumber) {
|
---|
| 173 | if (!isNaN(result) && result !== '') {
|
---|
| 174 | result = Number(result)
|
---|
| 175 | }
|
---|
| 176 | } else if (info.boolean || info.overloadedBoolean) {
|
---|
| 177 | // Accept `boolean` and `string`.
|
---|
| 178 | if (
|
---|
| 179 | typeof result === 'string' &&
|
---|
| 180 | (result === '' || normalize(value) === normalize(name))
|
---|
| 181 | ) {
|
---|
| 182 | result = true
|
---|
| 183 | }
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | return result
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | function style(value) {
|
---|
| 190 | var result = []
|
---|
| 191 | var key
|
---|
| 192 |
|
---|
| 193 | for (key in value) {
|
---|
| 194 | result.push([key, value[key]].join(': '))
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | return result.join('; ')
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | function createAdjustMap(values) {
|
---|
| 201 | var length = values.length
|
---|
| 202 | var index = -1
|
---|
| 203 | var result = {}
|
---|
| 204 | var value
|
---|
| 205 |
|
---|
| 206 | while (++index < length) {
|
---|
| 207 | value = values[index]
|
---|
| 208 | result[value.toLowerCase()] = value
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | return result
|
---|
| 212 | }
|
---|