Skip to content

Commit

Permalink
Add support for form elements
Browse files Browse the repository at this point in the history
Closes GH-3.
Closes GH-50.
  • Loading branch information
wooorm authored May 26, 2019
1 parent 785b144 commit dadad8f
Show file tree
Hide file tree
Showing 29 changed files with 1,181 additions and 16 deletions.
14 changes: 14 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@
module.exports = toMdast

var minify = require('rehype-minify-whitespace')
var visit = require('unist-util-visit')
var xtend = require('xtend')
var one = require('./lib/one')
var handlers = require('./lib/handlers')

function toMdast(tree, options) {
var settings = options || {}
var opts = {newlines: settings.newlines === true}
var byId = {}

h.nodeById = byId
h.baseFound = false
h.frozenBaseURL = null

h.handlers = xtend(handlers, settings.handlers || {})
h.augment = augment
h.document = settings.document

visit(tree, onvisit)

return one(h, minify(opts)(tree), null)

function h(node, type, props, children) {
Expand Down Expand Up @@ -51,4 +56,13 @@ function toMdast(tree, options) {

return right
}

function onvisit(node) {
var props = node.properties || {}
var id = props.id

if (id && !(id in node)) {
byId[id] = node
}
}
}
File renamed without changes.
17 changes: 13 additions & 4 deletions lib/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ var br = require('./break')
var cell = require('./table-cell')
var code = require('./code')
var comment = require('./comment')
var dataList = require('./data-list')
var dl = require('./dl')
var del = require('./delete')
var emphasis = require('./emphasis')
var heading = require('./heading')
var iframe = require('./iframe')
var image = require('./image')
var inlineCode = require('./inline-code')
var input = require('./input')
var link = require('./link')
var list = require('./list')
var listItem = require('./list-item')
Expand All @@ -23,9 +24,11 @@ var paragraph = require('./paragraph')
var quote = require('./q')
var root = require('./root')
var row = require('./table-row')
var select = require('./select')
var strong = require('./strong')
var table = require('./table')
var text = require('./text')
var textarea = require('./textarea')
var thematicBreak = require('./thematic-break')
var wbr = require('./wbr')

Expand All @@ -49,7 +52,6 @@ exports.element = ignore
exports.embed = ignore
exports.frame = ignore
exports.frameset = ignore
exports.input = ignore
exports.isindex = ignore
exports.keygen = ignore
exports.link = ignore
Expand All @@ -66,7 +68,6 @@ exports.optgroup = ignore
exports.option = ignore
exports.param = ignore
exports.script = ignore
exports.select = ignore
exports.shadow = ignore
exports.source = ignore
exports.spacer = ignore
Expand All @@ -82,13 +83,15 @@ exports.bdi = all
exports.bdo = all
exports.big = all
exports.blink = all
exports.button = all
exports.canvas = all
exports.cite = all
exports.data = all
exports.details = all
exports.dfn = all
exports.font = all
exports.ins = all
exports.label = all
exports.marquee = all
exports.meter = all
exports.nobr = all
Expand Down Expand Up @@ -117,12 +120,15 @@ exports.aside = wrapped
exports.body = wrapped
exports.center = wrapped
exports.div = wrapped
exports.fieldset = wrapped
exports.figcaption = wrapped
exports.figure = wrapped
exports.form = wrapped
exports.footer = wrapped
exports.header = wrapped
exports.hgroup = wrapped
exports.html = wrapped
exports.legend = wrapped
exports.main = wrapped
exports.multicol = wrapped
exports.nav = wrapped
Expand All @@ -137,7 +143,7 @@ exports.blockquote = blockquote
exports.br = br
exports.code = inlineCode
exports.dir = list
exports.dl = dataList
exports.dl = dl
exports.dt = listItem
exports.dd = listItem
exports.del = del
Expand All @@ -153,6 +159,7 @@ exports.i = emphasis
exports.iframe = iframe
exports.img = image
exports.image = image
exports.input = input
exports.kbd = inlineCode
exports.li = listItem
exports.listing = code
Expand All @@ -164,11 +171,13 @@ exports.pre = code
exports.q = quote
exports.s = del
exports.samp = inlineCode
exports.select = select
exports.strike = del
exports.strong = strong
exports.summary = paragraph
exports.table = table
exports.td = cell
exports.textarea = textarea
exports.th = cell
exports.tr = row
exports.tt = inlineCode
Expand Down
97 changes: 97 additions & 0 deletions lib/handlers/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use strict'

var repeat = require('repeat-string')
var is = require('hast-util-is-element')
var resolve = require('../util/resolve')
var findSelectedOptions = require('../util/find-selected-options')

module.exports = input

// eslint-disable-next-line complexity
function input(h, node) {
var byId = h.nodeById
var props = node.properties
var value = props.value || props.placeholder
var list = props.list
var type = props.type
var values = []
var length
var index
var results
var url
var text

if (props.disabled || props.type === 'hidden' || props.type === 'file') {
return
}

if (type === 'checkbox' || type === 'radio') {
return {type: 'text', value: '[' + (props.checked ? 'x' : ' ') + ']'}
}

if (type === 'image' && props.alt) {
values = [[props.alt]]
} else if (value) {
values = [[value]]
} else if (
list &&
type !== 'password' && // `list` is not supported on `password`
type !== 'file' && // …or `file`
type !== 'submit' && // …or `submit`
type !== 'reset' && // …or `reset`
type !== 'button' && // …or `button`
list in byId &&
is(byId[list], 'datalist')
) {
values = findSelectedOptions(byId[list], props)
}

if (values.length === 0) {
return
}

// Passwords don’t support `list`.
if (type === 'password') {
values[0] = [repeat('•', values[0][0].length)]
}

// Images don’t support `list`.
if (type === 'image') {
return h(node, 'image', {
url: resolve(h, props.src),
title: props.title || null,
alt: values[0][0]
})
}

length = values.length
index = -1
results = []

if (type !== 'url' && type !== 'email') {
while (++index < length) {
value = values[index]
results.push(value[1] ? value[1] + ' (' + value[0] + ')' : value[0])
}

return h.augment(node, {type: 'text', value: results.join(', ')})
}

while (++index < length) {
value = values[index]
text = resolve(h, value[0])
url = type === 'email' ? 'mailto:' + text : text

results.push(
h(node, 'link', {title: null, url: url}, [
{type: 'text', value: value[1] || text}
])
)

if (index !== length - 1) {
results.push({type: 'text', value: ', '})
}
}

return results
}
20 changes: 18 additions & 2 deletions lib/handlers/list-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = listItem
var is = require('hast-util-is-element')
var wrapChildren = require('../util/wrap-children')

// eslint-disable-next-line complexity
function listItem(h, node) {
var children = node.children
var head = children[0]
Expand All @@ -21,17 +22,32 @@ function listItem(h, node) {
if (
checkbox &&
is(checkbox, 'input') &&
checkbox.properties.type === 'checkbox'
(checkbox.properties.type === 'checkbox' ||
checkbox.properties.type === 'radio')
) {
checked = Boolean(checkbox.properties.checked)
}
}

content = wrapChildren(h, node)

// Remove initial spacing if we previously found a checkbox.
if (checked !== null) {
grandchildren = content[0] && content[0].children

// Remove text checkbox (enabled inputs are mapped to textual checkboxes).
head = grandchildren && grandchildren[0]

if (
head &&
head.type === 'text' &&
head.value.length === 3 &&
head.value.charAt(0) === '[' &&
head.value.charAt(2) === ']'
) {
grandchildren.shift()
}

// Remove initial spacing if we previously found a checkbox.
head = grandchildren && grandchildren[0]

if (head && head.type === 'text' && head.value.charAt(0) === ' ') {
Expand Down
25 changes: 25 additions & 0 deletions lib/handlers/select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict'

var findSelectedOptions = require('../util/find-selected-options')

module.exports = select

function select(h, node) {
var values = findSelectedOptions(node)
var length = values.length
var index = -1
var results = []
var value

while (++index < length) {
value = values[index]
results.push(value[1] ? value[1] + ' (' + value[0] + ')' : value[0])
}

if (results.length !== 0) {
return h.augment(node, {
type: 'text',
value: results.join(', ')
})
}
}
9 changes: 9 additions & 0 deletions lib/handlers/textarea.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

var toText = require('hast-util-to-text')

module.exports = textarea

function textarea(h, node) {
return h.augment(node, {type: 'text', value: toText(node)})
}
69 changes: 69 additions & 0 deletions lib/util/find-selected-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict'

var is = require('hast-util-is-element')
var has = require('hast-util-has-property')
var toText = require('hast-util-to-text')

module.exports = findSelectedOptions

function findSelectedOptions(node, properties) {
var props = properties || node.properties
var multiple = props.multiple
var size = Math.min(parseInt(props.size, 10), 0) || (multiple ? 4 : 1)
var options = findOptions(node)
var length = options.length
var index = -1
var selectedOptions = []
var values = []
var option
var list
var content
var label
var value

while (++index < length) {
option = options[index]

if (option.properties.selected) {
selectedOptions.push(option)
}
}

list = selectedOptions.length === 0 ? options : selectedOptions
options = list.slice(0, size)
length = options.length
index = -1

while (++index < length) {
option = options[index]
content = toText(option)
label = content || option.properties.label
value = option.properties.value || content

values.push([value, label === value ? null : label])
}

return values
}

function findOptions(node) {
var children = node.children
var length = children.length
var index = -1
var results = []
var child

while (++index < length) {
child = children[index]

if (is(child, 'option')) {
if (!has(child, 'disabled')) {
results.push(child)
}
} else if ('children' in child) {
results = results.concat(findOptions(child))
}
}

return results
}
Loading

0 comments on commit dadad8f

Please sign in to comment.