diff --git a/.gitignore b/.gitignore index 8428194c..6522e6f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea .*.swp node_modules/* nyc_output/ diff --git a/README.md b/README.md index afcd3f3d..5d55d816 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. @@ -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. diff --git a/lib/sax.js b/lib/sax.js index db0d4c31..0183fee1 100644 --- a/lib/sax.js +++ b/lib/sax.js @@ -42,6 +42,11 @@ 'closenamespace' ] + var looseCaseMapping = { + lower: 'toLowerCase', + upper: 'toUpperCase' + } + function SAXParser (strict, opt) { if (!(this instanceof SAXParser)) { return new SAXParser(strict, opt) @@ -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 @@ -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: {} } @@ -723,7 +739,7 @@ } function attrib (parser) { - if (!parser.strict) { + if (!parser.strict && parser.looseCase) { parser.attribName = parser.attribName[parser.looseCase]() } @@ -876,7 +892,7 @@ // 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 diff --git a/test/casing.js b/test/casing.js new file mode 100644 index 00000000..223ed386 --- /dev/null +++ b/test/casing.js @@ -0,0 +1,125 @@ +// test lower looseCasing +require(__dirname).test({ + xml: '', + 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: '', + 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: '', + 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: '', + 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: '', + 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: '', + 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'} +})