var Scanner = HTMLTools.Scanner; var getComment = HTMLTools.Parse.getComment; var getDoctype = HTMLTools.Parse.getDoctype; var getHTMLToken = HTMLTools.Parse.getHTMLToken; // "tokenize" is not really a great operation for real use, because // it ignores the special content rules for tags like "style" and // "script". var tokenize = function (input) { var scanner = new Scanner(input); var tokens = []; while (! scanner.isEOF()) { var token = getHTMLToken(scanner); if (token) tokens.push(token); } return tokens; }; Tinytest.add("html-tools - comments", function (test) { var succeed = function (input, content) { var scanner = new Scanner(input); var result = getComment(scanner); test.isTrue(result); test.equal(scanner.pos, content.length + 7); test.equal(result, { t: 'Comment', v: content }); }; var ignore = function (input) { var scanner = new Scanner(input); var result = getComment(scanner);; test.isFalse(result); test.equal(scanner.pos, 0); }; var fatal = function (input, messageContains) { var scanner = new Scanner(input); var error; try { getComment(scanner); } catch (e) { error = e; } test.isTrue(error); if (error) test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); }; test.equal(getComment(new Scanner("")), { t: 'Comment', v: ' hello ' }); ignore(""); ignore("', 'Unclosed'); fatal('', 'cannot contain'); fatal('', 'must end at first'); fatal('', 'cannot contain'); fatal('', 'cannot contain'); succeed('', ''); succeed('', '-x'); succeed('', 'x'); succeed('', ' hello - - world '); }); Tinytest.add("html-tools - doctype", function (test) { var succeed = function (input, expectedProps) { var scanner = new Scanner(input); var result = getDoctype(scanner); test.isTrue(result); test.equal(scanner.pos, result.v.length); test.equal(input.slice(0, result.v.length), result.v); var actualProps = _.extend({}, result); delete actualProps.t; delete actualProps.v; test.equal(actualProps, expectedProps); }; var fatal = function (input, messageContains) { var scanner = new Scanner(input); var error; try { getDoctype(scanner); } catch (e) { error = e; } test.isTrue(error); if (messageContains) test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); }; test.equal(getDoctype(new Scanner("x")), { t: 'Doctype', v: '', name: 'html' }); test.equal(getDoctype(new Scanner("x")), { t: 'Doctype', v: "", name: 'html', systemId: 'about:legacy-compat' }); test.equal(getDoctype(new Scanner("x")), { t: 'Doctype', v: "", name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN' }); test.equal(getDoctype(new Scanner("x")), { t: 'Doctype', v: "", name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/html4/strict.dtd' }); succeed('', {name: 'html'}); succeed('', {name: 'html'}); succeed('', {name: 'html'}); succeed('', {name: 'html'}); succeed('', {name: 'html'}); succeed('', {name: 'html'}); fatal('', 'Expected space'); fatal('', 'Malformed DOCTYPE'); fatal('', 'Malformed DOCTYPE'); fatal('', {name: 'html', systemId: 'about:legacy-compat'}); succeed('', {name: 'html', systemId: 'about:legacy-compat'}); succeed("", {name: 'html', systemId: 'about:legacy-compat'}); succeed("", {name: 'html', systemId: 'about:legacy-compat'}); succeed('', {name: 'html', systemId: 'about:legacy-compat'}); fatal('', 'Expected PUBLIC or SYSTEM'); fatal('', 'Expected space'); fatal(''); fatal(''); fatal('">'); fatal(''); fatal(''); fatal(''); fatal(''); fatal(''); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN'}); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN'}); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); fatal(''); fatal(''); }); Tinytest.add("html-tools - tokenize", function (test) { var fatal = function (input, messageContains) { var error; try { tokenize(input); } catch (e) { error = e; } test.isTrue(error); if (messageContains) test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); }; test.equal(tokenize(''), []); test.equal(tokenize('abc'), [{t: 'Chars', v: 'abc'}]); test.equal(tokenize('&'), [{t: 'Chars', v: '&'}]); test.equal(tokenize('&'), [{t: 'CharRef', v: '&', cp: [38]}]); test.equal(tokenize('ok fine'), [{t: 'Chars', v: 'ok'}, {t: 'CharRef', v: ' ', cp: [32]}, {t: 'Chars', v: 'fine'}]); test.equal(tokenize('ac'), [{t: 'Chars', v: 'a'}, {t: 'Comment', v: 'b'}, {t: 'Chars', v: 'c'}]); test.equal(tokenize(''), [{t: 'Tag', n: 'a'}]); fatal('<'); fatal(''), [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: 'c'}], d: [{t: 'Chars', v: 'e'}], f: [{t: 'Chars', v: 'g'}], h: [] }}]); fatal(''); fatal(''); fatal(''); test.equal(tokenize(''), [{t: 'Tag', n: 'a', isSelfClosing: true}]); fatal(''); fatal(''); fatal(''); fatal(''); test.equal(tokenize(''), [{t: 'Tag', n: 'a#', attrs: { b0: [{t: 'Chars', v: 'c@'}], d1: [{t: 'Chars', v: 'e2'}], 'f#': [{t: 'Chars', v: 'g '}], h: [] }}]); test.equal(tokenize('
'), [{t: 'Tag', n: 'div', attrs: { 'class': [] }}, {t: 'Tag', n: 'div', isEnd: true}]); test.equal(tokenize('
'), [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); test.equal(tokenize('
'), [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); test.equal(tokenize('