Skip to content

Commit

Permalink
Add support for SVG
Browse files Browse the repository at this point in the history
Related to wooorm/property-information#6.
Related to GH-6.
Related to GH-4.
  • Loading branch information
wooorm committed Jul 17, 2018
1 parent 2c43757 commit 8c490a7
Show file tree
Hide file tree
Showing 7 changed files with 917 additions and 450 deletions.
194 changes: 194 additions & 0 deletions factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
'use strict'

var find = require('property-information/find')
var normalize = require('property-information/normalize')
var parseSelector = require('hast-util-parse-selector')
var spaces = require('space-separated-tokens').parse
var commas = require('comma-separated-tokens').parse

module.exports = factory

function factory(schema, defaultTagName) {
return h

/* Hyperscript compatible DSL for creating virtual HAST trees. */
function h(selector, properties, children) {
var node = parseSelector(selector, defaultTagName)
var property

if (!children && properties && isChildren(properties, node)) {
children = properties
properties = null
}

if (properties) {
for (property in properties) {
addProperty(node.properties, property, properties[property])
}
}

addChild(node.children, children)

if (node.tagName === 'template') {
node.content = {type: 'root', children: node.children}
node.children = []
}

return node
}

function addProperty(properties, key, value) {
var info
var property
var result

/* Ignore nully and NaN values. */
if (value === null || value === undefined || value !== value) {
return
}

info = find(schema, key)
property = info.property
result = value

/* Handle list values. */
if (typeof result === 'string') {
if (info.spaceSeparated) {
result = spaces(result)
} else if (info.commaSeparated) {
result = commas(result)
} else if (info.commaOrSpaceSeparated) {
result = spaces(commas(result).join(' '))
}
}

/* Accept `object` on style. */
if (property === 'style' && typeof value !== 'string') {
result = style(result)
}

/* Class-names (which can be added both on the `selector` and here). */
if (property === 'className' && properties.className) {
result = properties.className.concat(result)
}

properties[property] = parsePrimitives(info, property, result)
}
}

function isChildren(value, node) {
return (
typeof value === 'string' ||
'length' in value ||
isNode(node.tagName, value)
)
}

function isNode(tagName, value) {
var type = value.type

if (tagName === 'input' || !type || typeof type !== 'string') {
return false
}

if (typeof value.children === 'object' && 'length' in value.children) {
return true
}

type = type.toLowerCase()

if (tagName === 'button') {
return (
type !== 'menu' &&
type !== 'submit' &&
type !== 'reset' &&
type !== 'button'
)
}

return 'value' in value
}

function addChild(nodes, value) {
var index
var length

if (value === null || value === undefined) {
return
}

if (typeof value === 'string' || typeof value === 'number') {
nodes.push({type: 'text', value: String(value)})
return
}

if (typeof value === 'object' && 'length' in value) {
index = -1
length = value.length

while (++index < length) {
addChild(nodes, value[index])
}

return
}

if (typeof value !== 'object' || !('type' in value)) {
throw new Error('Expected node, nodes, or string, got `' + value + '`')
}

nodes.push(value)
}

/* Parse a (list of) primitives. */
function parsePrimitives(info, name, value) {
var index
var length
var result

if (typeof value !== 'object' || !('length' in value)) {
return parsePrimitive(info, name, value)
}

length = value.length
index = -1
result = []

while (++index < length) {
result[index] = parsePrimitive(info, name, value[index])
}

return result
}

/* Parse a single primitives. */
function parsePrimitive(info, name, value) {
var result = value

if (info.number || info.positiveNumber) {
if (!isNaN(result) && result !== '') {
result = Number(result)
}
} else if (info.boolean || info.overloadedBoolean) {
/* Accept `boolean` and `string`. */
if (
typeof result === 'string' &&
(result === '' || normalize(value) === normalize(name))
) {
result = true
}
}

return result
}

function style(value) {
var result = []
var key

for (key in value) {
result.push([key, value[key]].join(': '))
}

return result.join('; ')
}
9 changes: 9 additions & 0 deletions html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

var schema = require('property-information/html')
var factory = require('./factory')

var html = factory(schema, 'div')
html.displayName = 'html'

module.exports = html
179 changes: 1 addition & 178 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,180 +1,3 @@
'use strict'

var parseSelector = require('hast-util-parse-selector')
var camelcase = require('camelcase')
var propertyInformation = require('property-information')
var spaces = require('space-separated-tokens').parse
var commas = require('comma-separated-tokens').parse

module.exports = h

/* Hyperscript compatible DSL for creating virtual HAST
* trees. */
function h(selector, properties, children) {
var node = parseSelector(selector)
var property

if (
properties &&
!children &&
(typeof properties === 'string' ||
'length' in properties ||
isNode(node.tagName, properties))
) {
children = properties
properties = null
}

if (properties) {
for (property in properties) {
addProperty(node.properties, property, properties[property])
}
}

addChild(node.children, children)

if (node.tagName === 'template') {
node.content = {type: 'root', children: node.children}
node.children = []
}

return node
}

/* Check if `value` is a valid child node of `tagName`. */
function isNode(tagName, value) {
var type = value.type

if (typeof type === 'string') {
type = type.toLowerCase()
}

if (tagName === 'input' || !type || typeof type !== 'string') {
return false
}

if (typeof value.children === 'object' && 'length' in value.children) {
return true
}

if (tagName === 'button') {
return (
type !== 'menu' &&
type !== 'submit' &&
type !== 'reset' &&
type !== 'button'
)
}

return 'value' in value
}

/* Add `value` as a child to `nodes`. */
function addChild(nodes, value) {
var index
var length

if (value === null || value === undefined) {
return
}

if (typeof value === 'string' || typeof value === 'number') {
value = {type: 'text', value: String(value)}
}

if (typeof value === 'object' && 'length' in value) {
index = -1
length = value.length

while (++index < length) {
addChild(nodes, value[index])
}

return
}

if (typeof value !== 'object' || !('type' in value)) {
throw new Error('Expected node, nodes, or string, got `' + value + '`')
}

nodes.push(value)
}

/* Add `name` and its `value` to `properties`. `properties` can
* be prefilled by `parseSelector`: it can have `id` and `className`
* properties. */
function addProperty(properties, name, value) {
var info = propertyInformation(name) || {}
var result = value
var key

/* Ignore nully and NaN values. */
if (value === null || value === undefined || value !== value) {
return
}

/* Handle values. */
if (name === 'style') {
/* Accept `object`. */
if (typeof value !== 'string') {
result = []

for (key in value) {
result.push([key, value[key]].join(': '))
}

result = result.join('; ')
}
} else if (info.spaceSeparated) {
/* Accept both `string` and `Array`. */
result = typeof value === 'string' ? spaces(result) : result

/* Class-names (which can be added both on
* the `selector` and here). */
if (name === 'class' && properties.className) {
result = properties.className.concat(result)
}
} else if (info.commaSeparated) {
/* Accept both `string` and `Array`. */
result = typeof value === 'string' ? commas(result) : result
}

result = parsePrimitive(info, name, result)

properties[info.propertyName || camelcase(name)] = result
}

/* Parse a (list of) primitives. */
function parsePrimitive(info, name, value) {
var result = value
var index
var length

if (typeof value === 'object' && 'length' in value) {
length = value.length
index = -1
result = []

while (++index < length) {
result[index] = parsePrimitive(info, name, value[index])
}

return result
}

if (info.numeric || info.positiveNumeric) {
if (!isNaN(result) && result !== '') {
result = Number(result)
}
} else if (info.boolean || info.overloadedBoolean) {
/* Accept `boolean` and `string`. */
if (
typeof result === 'string' &&
(result === '' || value.toLowerCase() === name)
) {
result = true
}
}

return result
}
module.exports = require('./html')
Loading

0 comments on commit 8c490a7

Please sign in to comment.