Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added the option to choose casing for loose mode. #207

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
.*.swp
node_modules/*
nyc_output/
Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,16 @@ Pass the following arguments to the parser function. All are optional.

`strict` - Boolean. Whether or not to be a jerk. Default: `false`.

`opt` - Object bag of settings regarding string formatting. All default to `false`.
`opt` - Object bag of settings regarding string formatting. All default to `false`, `looseCasing` default to `upper`.

Settings supported:

* `trim` - Boolean. Whether or not to trim text and comment nodes.
* `normalize` - Boolean. If true, then turn any whitespace into a single
space.
* `lowercase` - Boolean. If true, then lowercase tag names and attribute names
in loose mode, rather than uppercasing them.
* `looseCasing` - String (`'lower'`/`'upper'`/`null`). In loose mode, the casing of tag names and
attributes will be lower/upper cased or unchanged.
The deprecated `lowercase` and `lowercasetags` overrides this property if set.
* `xmlns` - Boolean. If true, then namespaces are supported.
* `position` - Boolean. If false, then don't track line/col/position.
* `strictEntities` - Boolean. If true, only parse [predefined XML
Expand Down Expand Up @@ -174,8 +175,9 @@ but before any attributes are encountered. Argument: object with a
same object that will later be emitted in the `opentag` event.

`opentag` - An opening tag. Argument: object with `name` and `attributes`.
In non-strict mode, tag names are uppercased, unless the `lowercase`
option is set. If the `xmlns` option is set, then it will contain
In non-strict mode, tag names will be lower/upper cased or unchanged if `looseCasing`
option is set to `lower`, `upper` or `null` (defaults to `upper`).
If the `xmlns` option is set, then it will contain
namespace binding information on the `ns` member, and will have a
`local`, `prefix`, and `uri` member.

Expand All @@ -185,8 +187,9 @@ self-closing tags will have `closeTag` emitted immediately after `openTag`.
Argument: tag name.

`attribute` - An attribute node. Argument: object with `name` and `value`.
In non-strict mode, attribute names are uppercased, unless the `lowercase`
option is set. If the `xmlns` option is set, it will also contains namespace
In non-strict mode, attribute names will be lower/upper cased or unchanged if `looseCasing`
option is set to `lower`, `upper` or `null` (defaults to `upper`).
If the `xmlns` option is set, it will also contains namespace
information.

`comment` - A comment node. Argument: the string of the comment.
Expand Down
26 changes: 21 additions & 5 deletions lib/sax.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
'closenamespace'
]

var looseCaseMapping = {
lower: 'toLowerCase',
upper: 'toUpperCase'
}

function SAXParser (strict, opt) {
if (!(this instanceof SAXParser)) {
return new SAXParser(strict, opt)
Expand All @@ -52,8 +57,17 @@
parser.q = parser.c = ''
parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH
parser.opt = opt || {}
parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags
parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase'

if (!parser.opt.hasOwnProperty('looseCasing')) {
parser.opt.looseCasing = 'upper'
}
parser.looseCase = looseCaseMapping[parser.opt.looseCasing]

// backwards support for deprecated "lowercase" and "lowercasetags" options
if (parser.opt.hasOwnProperty('lowercase') || parser.opt.hasOwnProperty('lowercasetags')) {
parser.looseCase = (parser.opt.lowercase || parser.opt.lowercasetags) ? 'toLowerCase' : 'toUpperCase'
}

parser.tags = []
parser.closed = parser.closedRoot = parser.sawRoot = false
parser.tag = parser.error = null
Expand Down Expand Up @@ -695,7 +709,9 @@
}

function newTag (parser) {
if (!parser.strict) parser.tagName = parser.tagName[parser.looseCase]()
if (!parser.strict && parser.looseCase) {
parser.tagName = parser.tagName[parser.looseCase]()
}
var parent = parser.tags[parser.tags.length - 1] || parser
var tag = parser.tag = { name: parser.tagName, attributes: {} }

Expand Down Expand Up @@ -723,7 +739,7 @@
}

function attrib (parser) {
if (!parser.strict) {
if (!parser.strict && parser.looseCase) {
parser.attribName = parser.attribName[parser.looseCase]()
}

Expand Down Expand Up @@ -876,7 +892,7 @@
// <a><b></c></b></a> will close everything, otherwise.
var t = parser.tags.length
var tagName = parser.tagName
if (!parser.strict) {
if (!parser.strict && parser.looseCase) {
tagName = tagName[parser.looseCase]()
}
var closeTo = tagName
Expand Down
125 changes: 125 additions & 0 deletions test/casing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// test lower looseCasing
require(__dirname).test({
xml: '<span class="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'span',
attributes: {}
} ],
[ 'attribute', { name: 'class', value: 'test' } ],
[ 'attribute', { name: 'hello', value: 'world' } ],
[ 'opentag', {
name: 'span',
attributes: { class: 'test', hello: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'span' ]
],
strict: false,
opt: {looseCasing: 'lower'}
})

// test upper looseCasing
require(__dirname).test({
xml: '<span class="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'SPAN',
attributes: {}
} ],
[ 'attribute', { name: 'CLASS', value: 'test' } ],
[ 'attribute', { name: 'HELLO', value: 'world' } ],
[ 'opentag', {
name: 'SPAN',
attributes: { CLASS: 'test', HELLO: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'SPAN' ]
],
strict: false,
opt: {looseCasing: 'upper'}
})

// test no looseCasing
require(__dirname).test({
xml: '<span className="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'span',
attributes: {}
} ],
[ 'attribute', { name: 'className', value: 'test' } ],
[ 'attribute', { name: 'hello', value: 'world' } ],
[ 'opentag', {
name: 'span',
attributes: { className: 'test', hello: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'span' ]
],
strict: false,
opt: {looseCasing: null}
})

// make sure deprecated lowercase overrides the looseCasing null option
require(__dirname).test({
xml: '<span className="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'span',
attributes: {}
} ],
[ 'attribute', { name: 'classname', value: 'test' } ],
[ 'attribute', { name: 'hello', value: 'world' } ],
[ 'opentag', {
name: 'span',
attributes: { classname: 'test', hello: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'span' ]
],
strict: false,
opt: {lowercase: true, looseCasing: null}
})

// make sure deprecated lowercase overrides the looseCasing upper option
require(__dirname).test({
xml: '<span className="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'span',
attributes: {}
} ],
[ 'attribute', { name: 'classname', value: 'test' } ],
[ 'attribute', { name: 'hello', value: 'world' } ],
[ 'opentag', {
name: 'span',
attributes: { classname: 'test', hello: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'span' ]
],
strict: false,
opt: {lowercase: true, looseCasing: 'upper'}
})

// make sure deprecated lowercasetags overrides the looseCasing upper option
require(__dirname).test({
xml: '<span className="test" hello="world"></span>',
expect: [
[ 'opentagstart', {
name: 'span',
attributes: {}
} ],
[ 'attribute', { name: 'classname', value: 'test' } ],
[ 'attribute', { name: 'hello', value: 'world' } ],
[ 'opentag', {
name: 'span',
attributes: { classname: 'test', hello: 'world' },
isSelfClosing: false
} ],
[ 'closetag', 'span' ]
],
strict: false,
opt: {lowercasetags: true, looseCasing: 'upper'}
})