prettydiff2
Version:
Latest version of Pretty Diff for use in Atom Beautify to field test it in the wild before moving on to Pretty Diff 3.
941 lines (936 loc) • 260 kB
JavaScript
/*prettydiff.com api.topcoms:true,api.insize:4,api.inchar:" ",api.vertical:true */
/*jshint laxbreak: true*/
/*global __dirname, ace, define, global, module, process, require*/
/***********************************************************************
markuppretty is written by Austin Cheney on 20 Jun 2015.
Please see the license.txt file associated with the Pretty Diff
application for license information.
**********************************************************************/
/* A simple parser for XML, HTML, and a variety of template schemes. It
beautifies, minifies, and peforms a series of analysis*/
(function markuppretty_init() {
"use strict";
var markuppretty = function markuppretty_(options) {
var safeSort = global.prettydiff.safeSort,
output = "",
stats = {
cdata : [
0, 0
],
comment : [
0, 0
],
conditional: [
0, 0
],
content : [
0, 0
],
end : [
0, 0
],
ignore : [
0, 0
],
script : [
0, 0
],
sgml : [
0, 0
],
singleton : [
0, 0
],
space : 0,
start : [
0, 0
],
style : [
0, 0
],
template : [
0, 0
],
text : [
0, 0
],
xml : [0, 0]
},
//parallel arrays
// * attrs is a list of arrays, each of which contains (if any) parsed
// attributes
// * begin stores the index of the current token's parent element
// * daddy stores the tag name of the parent element
// * jscom stores true/false if the current token is a JS comment from JSX
// format
// * level describes the indentation of a given token level is only used in
// beautify and diff modes
// * linen stores the input line number on which the token occurs
// * lines describes the preceeding space using: 2, 1, or 0 lines is populated
// in markuppretty__tokenize_spacer
// * presv whether a given token should be preserved as provided
// * token stores parsed tokens
// * types segments tokens into named groups
// * value attribute value if current type is attribute and
// options.attributetoken is true
attrs = [],
jscom = [],
level = [],
linen = [],
lines = [],
token = [],
types = [],
presv = [],
daddy = [],
begin = [],
value = [],
reqs = [],
ids = [],
parseError = [],
parent = [
["none", -1]
],
line = 1,
wrap = options.wrap,
objsortop = false,
globalerror = "",
lf = (options.crlf === true || options.crlf === "true")
? "\r\n"
: "\n",
sourceSize = options.source.length,
extlib = function markuppretty__extlib(type) {
var result = "",
newline = options.newline;
if (type === "script" && typeof global.prettydiff.jspretty !== "function") {
return options.source;
}
if (type === "style" && typeof global.prettydiff.csspretty !== "function") {
return options.source;
}
options.newline = false;
result = (type === "script")
? global
.prettydiff
.jspretty(options)
: global
.prettydiff
.csspretty(options);
options.newline = newline;
if (options.nodeasync === true) {
if (globalerror === "") {
globalerror = result[1];
}
if (options.mode === "parse") {
if (options.parseFormat === "htmltable") {
if (type === "script") {
return result[0]
.data
.replace(
"<thead>",
"<thead><tr><th colspan=\"6\" class=\"nested\">JavaScript tokens</th></tr>"
);
}
return result[0]
.data
.replace(
"<thead>",
"<thead><tr><th colspan=\"4\" class=\"nested\">CSS tokens</th></tr>"
);
}
return result[0].data;
}
return result[0];
}
if (options.mode === "parse") {
if (options.parseFormat === "htmltable") {
if (type === "script") {
return result
.data
.replace(
"<thead>",
"<thead><tr><th colspan=\"6\" class=\"nested\">JavaScript tokens</th></tr>"
);
}
return result
.data
.replace(
"<thead>",
"<thead><tr><th colspan=\"4\" class=\"nested\">CSS tokens</th></tr>"
);
}
return result.data;
}
return result;
},
//What is the lowercase tag name of the provided token?
tagName = function markuppretty__tagName(el) {
var space = el
.replace(/^(\{((%-?)|\{-?)\s*)/, "%")
.replace(/\s+/, " ")
.indexOf(" "),
name = (space < 0)
? el
.replace(/^(\{((%-?)|\{-?)\s*)/, " ")
.slice(1, el.length - 1)
.toLowerCase()
: el
.replace(/^(\{((%-?)|\{-?)\s*)/, " ")
.slice(1, space)
.toLowerCase();
name = name.replace(/(\}\})$/, "");
if (name.indexOf("(") > 0) {
name = name.slice(0, name.indexOf("("));
}
return name;
};
(function markuppretty__options() {
objsortop = (
options.objsort === true || options.objsort === "true" || options.objsort === "all" || options.objsort === "markup"
);
options.source = (
typeof options.source === "string" && options.source.length > 0
)
? options
.source
.replace(/\r\n?/g, "\n")
: "Error: no source code supplied to markuppretty!";
if (options.mode === "analysis") {
options.accessibility = true;
}
}());
//type definitions:
// * start end type
// * <![CDATA[ ]]> cdata
// * <!-- --> comment
// * <#-- --> comment
// * <%-- --%> comment
// * {! !} comment
// * <!--[if --> conditional
// * text text content
// * </ > end
// * <pre </pre> ignore (html only)
// * text text script
// * <! > sgml
// * < /> singleton
// * < > start
// * text text style
// * <!--# --> template
// * <% %> template
// * {{{ }}} template
// * {{ }} template
// * {% %} template
// * [% %] template
// * {@ @} template
// * {# #} template
// * {# /} template
// * {? /} template
// * {^ /} template
// * {@ /} template
// * {< /} template
// * {+ /} template
// * {~ } template
// * <? ?> template
// * {:else} template_else
// * <#else > template_else
// * {@}else{@} template_else
// * <%}else{%> template_else
// * {{ }} template_end
// * <%\s*} %> template_end
// * [%\s*} %] template_end
// * {@\s*} @} template_end
// * { } template_end
// * {{# }} template_start
// * <% {\s*%> template_start
// * [% {\s*%] template_start
// * {@ {\s*@} template_start
// * {# } template_start
// * {? } template_start
// * {^ } template_start
// * {@ } template_start
// * {< } template_start
// * {+ } template_start
// * <?xml ?> xml
if (options.mode !== "diff") {
options.content = false;
}
if (options.jsx === true) {
options.dustjs = false;
}
(function markuppretty__tokenize() {
var a = 0,
b = options
.source
.split(""),
c = b.length,
minspace = "",
space = "",
list = 0,
litag = 0,
linepreserve = 0,
cftransaction = false,
sgmlflag = 0,
ext = false,
//cftags is a list of supported coldfusion tags
//* required - means must have a separate matching end tag
// * optional - means the tag could have a separate end tag, but is probably a
// singleton
//* prohibited - means there is not corresponding end tag
cftags = {
cfabort : "prohibited",
cfajaximport : "optional",
cfajaxproxy : "optional",
cfapplet : "prohibited",
cfapplication : "prohibited",
cfargument : "prohibited",
cfassociate : "prohibited",
cfauthenticate : "prohibited",
cfbreak : "prohibited",
cfcache : "optional",
cfcalendar : "optional",
cfcase : "required",
cfcatch : "required",
cfchart : "optional",
cfchartdata : "prohibited",
cfchartseries : "optional",
cfclient : "required",
cfclientsettings : "optional",
cfcol : "prohibited",
cfcollection : "prohibited",
cfcomponent : "required",
cfcontent : "optional",
cfcontinue : "prohibited",
cfcookie : "prohibited",
cfdbinfo : "prohibited",
cfdefaultcase : "required",
cfdirectory : "prohibited",
cfdiv : "optional",
cfdocument : "optional",
cfdocumentitem : "optional",
cfdocumentsection : "optional",
cfdump : "optional",
cfelse : "prohibited",
cfelseif : "prohibited",
cferror : "prohibited",
cfexchangecalendar : "optional",
cfexchangeconnection : "optional",
cfexchangecontact : "optional",
cfexchangeconversation: "optional",
cfexchangefilter : "optional",
cfexchangefolder : "optional",
cfexchangemail : "optional",
cfexchangetask : "optional",
cfexecute : "required",
cfexit : "prohibited",
cffeed : "prohibited",
cffile : "optional",
cffileupload : "optional",
cffinally : "required",
cfflush : "prohibited",
cfform : "required",
cfformgroup : "required",
cfformitem : "optional",
cfforward : "prohibited",
cfftp : "prohibited",
cffunction : "required",
cfgraph : "required",
cfgraphdata : "prohibited",
cfgrid : "required",
cfgridcolumn : "optional",
cfgridrow : "optional",
cfgridupdate : "optional",
cfheader : "prohibited",
cfhtmlbody : "optional",
cfhtmlhead : "optional",
cfhtmltopdf : "optional",
cfhtmltopdfitem : "optional",
cfhttp : "optional",
cfhttpparam : "prohibited",
cfif : "required",
cfimage : "prohibited",
cfimap : "prohibited",
cfimapfilter : "optional",
cfimport : "prohibited",
cfinclude : "prohibited",
cfindex : "prohibited",
cfinput : "prohibited",
cfinsert : "prohibited",
cfinterface : "required",
cfinvoke : "optional",
cfinvokeargument : "prohibited",
cflayout : "optional",
cflayoutarea : "optional",
cfldap : "prohibited",
cflocation : "prohibited",
cflock : "required",
cflog : "prohibited",
cflogic : "required",
cfloginuser : "prohibited",
cflogout : "prohibited",
cfloop : "required",
cfmail : "required",
cfmailparam : "prohibited",
cfmailpart : "required",
cfmap : "optional",
cfmapitem : "optional",
cfmediaplayer : "optional",
cfmenu : "required",
cfmenuitem : "optional",
cfmessagebox : "optional",
cfmodule : "optional",
cfNTauthenticate : "optional",
cfoauth : "optional",
cfobject : "prohibited",
cfobjectcache : "prohibited",
cfoutput : "required",
cfpageencoding : "optional",
cfparam : "prohibited",
cfpdf : "optional",
cfpdfform : "optional",
cfpdfformparam : "optional",
cfpdfparam : "prohibited",
cfpdfsubform : "required",
cfpod : "optional",
cfpop : "prohibited",
cfpresentation : "required",
cfpresentationslide : "optional",
cfpresenter : "optional",
cfprint : "optional",
cfprocessingdirective : "optional",
cfprocparam : "prohibited",
cfprocresult : "prohibited",
cfprogressbar : "optional",
cfproperty : "prohibited",
cfquery : "required",
cfqueryparam : "prohibited",
cfregistry : "prohibited",
cfreport : "optional",
cfreportparam : "optional",
cfrethrow : "prohibited",
cfretry : "prohibited",
cfreturn : "prohibited",
cfsavecontent : "required",
cfschedule : "prohibited",
cfscript : "required",
cfsearch : "prohibited",
cfselect : "required",
cfservlet : "prohibited",
cfservletparam : "prohibited",
cfset : "prohibited",
cfsetting : "optional",
cfsharepoint : "optional",
cfsilent : "required",
cfsleep : "prohibited",
cfslider : "prohibited",
cfspreadsheet : "optional",
cfsprydataset : "optional",
cfstatic : "required",
cfstopwatch : "required",
cfstoredproc : "optional",
cfswitch : "required",
cftable : "required",
cftextarea : "optional",
cfthread : "optional",
cfthrow : "prohibited",
cftimer : "required",
cftooltip : "required",
cftrace : "optional",
cftransaction : "required",
cftree : "required",
cftreeitem : "optional",
cftry : "required",
cfupdate : "prohibited",
cfvideo : "prohibited",
cfvideoplayer : "optional",
cfwddx : "prohibited",
cfwebsocket : "optional",
cfwhile : "required",
cfwindow : "optional",
cfx_ : "prohibited",
cfxml : "required",
cfzip : "optional",
cfzipparam : "prohibited"
},
// determine if spaces between nodes are absent, multiline, or merely there 2 -
// multiline 1 - space present 0 - no space present
spacer = function markuppretty__tokenize_spacer() {
var linea = 0;
if (space.length > 0) {
stats.space = stats.space + space.length;
linea = space
.split("\n")
.length - 1;
if (options.preserve > 0 && linea > 1) {
if (linea > options.preserve + 1) {
lines.push(options.preserve + 1);
} else {
lines.push(linea);
}
} else {
lines.push(1);
}
} else {
lines.push(0);
}
minspace = space;
space = "";
},
//parses tags, attributes, and template elements
tag = function markuppretty__tokenize_tag(end) {
var lexer = [],
bcount = 0,
e = 0,
f = 0,
igcount = 0,
jsxcount = 0,
braccount = 0,
parncount = 0,
quote = "",
element = "",
lastchar = "",
jsxquote = "",
tname = "",
comment = false,
cheat = false,
endtag = false,
nopush = false,
nosort = false,
simple = false,
preserve = false,
stest = false,
liend = false,
ignoreme = false,
quotetest = false,
parseFail = false,
singleton = false,
earlyexit = false,
attribute = [],
attstore = [],
presend = {
cfquery: true
},
arname = function markuppretty__tokenize_tag_name(x) {
var eq = x.indexOf("=");
if (eq > 0 && ((eq < x.indexOf("\"") && x.indexOf("\"") > 0) || (eq < x.indexOf("'") && x.indexOf("'") > 0))) {
return x.slice(0, eq);
}
return x;
},
slashy = function markuppretty__tokenize_tag_slashy() {
var x = a;
do {
x = x - 1;
} while (b[x] === "\\");
x = a - x;
if (x % 2 === 1) {
return false;
}
return true;
},
attrpush = function markuppretty__tokenize_tag_attrpush(quotes) {
var atty = "",
name = "",
aa = 0,
bb = 0;
if (quotes === true) {
if (quote === "\"" && options.quoteConvert === "single") {
atty = attribute
.slice(0, attribute.length - 1)
.join("")
.replace(/'/g, "\"")
.replace(/"/, "'") + "'";
} else if (quote === "'" && options.quoteConvert === "double") {
atty = attribute
.slice(0, attribute.length - 1)
.join("")
.replace(/"/g, "'")
.replace(/'/, "\"") + "\"";
} else {
atty = attribute.join("");
}
name = arname(atty);
if (name === "data-prettydiff-ignore") {
ignoreme = true;
} else if (name === "id") {
ids.push(atty.slice(name.length + 2, atty.length - 1));
} else if (name === "schemaLocation") {
reqs.push(atty.slice(name.length + 2, atty.length - 1));
}
quote = "";
} else {
atty = attribute
.join("")
.replace(/\s+/g, " ");
name = arname(atty);
if (name === "data-prettydiff-ignore") {
ignoreme = true;
} else if (name === "id") {
ids.push(element.slice(name.length + 1, atty.length));
}
if (options.jsx === true && attribute[0] === "{" && attribute[attribute.length - 1] === "}") {
jsxcount = 0;
}
}
if (atty.slice(0, 3) === "<%=" || atty.slice(0, 2) === "{%") {
nosort = true;
}
atty = atty
.replace(/^\u0020/, "")
.replace(/\u0020$/, "");
attribute = atty
.replace(/\r\n/g, "\n")
.split("\n");
bb = attribute.length;
for (aa = 0; aa < bb; aa = aa + 1) {
attribute[aa] = attribute[aa].replace(/(\s+)$/, "");
}
atty = attribute.join(lf);
if (atty === "=") {
attstore[attstore.length - 1] = attstore[attstore.length - 1] + "=";
} else if (atty.charAt(0) === "=" && attstore.length > 0 && attstore[attstore.length - 1].indexOf("=") < 0) {
//if an attribute starts with a `=` then adjoin it to the last attribute
attstore[attstore.length - 1] = attstore[attstore.length - 1] + atty;
} else if (atty.charAt(0) !== "=" && attstore.length > 0 && attstore[attstore.length - 1].indexOf("=") === attstore[attstore.length - 1].length - 1) {
// if an attribute follows an attribute ending with `=` then adjoin it to the
// last attribute
attstore[attstore.length - 1] = attstore[attstore.length - 1] + atty;
} else if (atty !== "" && atty !== " ") {
attstore.push(atty);
}
attribute = [];
};
spacer();
jscom.push(false);
linen.push(line);
value.push("");
ext = false;
// this complex series of conditions determines an elements delimiters look to
// the types being pushed to quickly reason about the logic no type is pushed
// for start tags or singleton tags just yet some types set the `preserve` flag,
// which means to preserve internal white space The `nopush` flag is set when
// parsed tags are to be ignored and forgotten
(function markuppretty__tokenize_types() {
if (end === "]>") {
end = ">";
sgmlflag = sgmlflag - 1;
types.push("template_end");
} else if (end === "---") {
preserve = true;
types.push("comment");
} else if (b[a] === "<") {
if (b[a + 1] === "/") {
if (b[a + 2] === "#") {
types.push("template_end");
} else {
types.push("end");
}
end = ">";
} else if (b[a + 1] === "!") {
if (b[a + 2] === "-" && b[a + 3] === "-") {
if (b[a + 4] === "#") {
end = "-->";
types.push("template");
} else if (b[a + 4] === "[" && b[a + 5] === "i" && b[a + 6] === "f" && options.conditional === true) {
end = "-->";
types.push("conditional");
} else if (b[a + 4] === "-" && (/<cf[a-z]/i).test(options.source) === true) {
preserve = true;
comment = true;
end = "--->";
types.push("comment");
} else {
end = "-->";
if (options.mode === "minify" || options.comments === "nocomment") {
nopush = true;
comment = true;
} else {
if (options.preserveComment === true) {
preserve = true;
}
comment = true;
if (options.commline === true) {
lines[lines.length - 1] = 2;
}
types.push("comment");
}
}
} else if (b[a + 2] === "[" && b[a + 3] === "C" && b[a + 4] === "D" && b[a + 5] === "A" && b[a + 6] === "T" && b[a + 7] === "A" && b[a + 8] === "[") {
end = "]]>";
preserve = true;
comment = true;
types.push("cdata");
} else {
end = ">";
sgmlflag = sgmlflag + 1;
types.push("sgml");
}
} else if (b[a + 1] === "?") {
end = "?>";
if (b[a + 2] === "x" && b[a + 3] === "m" && b[a + 4] === "l") {
types.push("xml");
} else {
preserve = true;
types.push("template");
}
} else if (b[a + 1] === "%") {
if (b[a + 2] !== "=") {
preserve = true;
}
if (b[a + 2] === "-" && b[a + 3] === "-") {
end = "--%>";
comment = true;
if (options.commline === true) {
line[line.length - 1] = 2;
}
types.push("comment");
} else if (b[a + 2] === "#") {
end = "%>";
comment = true;
if (options.commline === true) {
line[line.length - 1] = 2;
}
types.push("comment");
} else {
end = "%>";
types.push("template");
}
} else if (b[a + 4] !== undefined && b[a + 1].toLowerCase() === "p" && b[a + 2].toLowerCase() === "r" && b[a + 3].toLowerCase() === "e" && (b[a + 4] === ">" || (/\s/).test(b[a + 4]) === true)) {
end = "</pre>";
preserve = true;
types.push("ignore");
} else if (b[a + 4] !== undefined && b[a + 1].toLowerCase() === "x" && b[a + 2].toLowerCase() === "s" && b[a + 3].toLowerCase() === "l" && b[a + 4].toLowerCase() === ":" && b[a + 5].toLowerCase() === "t" && b[a + 6].toLowerCase() === "e" && b[a + 7].toLowerCase() === "x" && b[a + 8].toLowerCase() === "t" && (b[a + 9] === ">" || (/\s/).test(b[a + 9]) === true)) {
end = "</xsl:text>";
preserve = true;
types.push("ignore");
} else if (b[a + 8] !== undefined && b[a + 1].toLowerCase() === "c" && b[a + 2].toLowerCase() === "f" && b[a + 3].toLowerCase() === "q" && b[a + 4].toLowerCase() === "u" && b[a + 5].toLowerCase() === "e" && b[a + 6].toLowerCase() === "r" && b[a + 7].toLowerCase() === "y" && (b[a + 8] === ">" || (/\s/).test(b[a + 8]))) {
end = ">";
linepreserve = linepreserve + 1;
types.push("linepreserve");
} else if (b[a + 1] === "<") {
if (b[a + 2] === "<") {
end = ">>>";
} else {
end = ">>";
}
types.push("template");
} else if (b[a + 1] === "#") {
if (b[a + 2] === "e" && b[a + 3] === "l" && b[a + 4] === "s" && b[a + 5] === "e") {
end = ">";
types.push("template_else");
} else if (b[a + 2] === "-" && b[a + 3] === "-") {
end = "-->";
types.push("comment");
preserve = true;
} else {
end = ">";
types.push("template_start");
}
} else {
simple = true;
end = ">";
}
} else if (b[a] === "{") {
preserve = true;
if (options.jsx === true) {
end = "}";
types.push("script");
} else if (options.dustjs === true) {
if (b[a + 1] === ":" && b[a + 2] === "e" && b[a + 3] === "l" && b[a + 4] === "s" && b[a + 5] === "e" && b[a + 6] === "}") {
a = a + 6;
token.push("{:else}");
presv.push(true);
daddy.push(parent[parent.length - 1][0]);
begin.push(parent[parent.length - 1][1]);
attrs.push({});
stats.template[0] = stats.template[0] + 1;
stats.template[1] = stats.template[1] + 7;
earlyexit = true;
return types.push("template_else");
}
if (b[a + 1] === "!") {
end = "!}";
comment = true;
types.push("comment");
} else if (b[a + 1] === "/") {
end = "}";
types.push("template_end");
} else if (b[a + 1] === "~") {
end = "}";
types.push("singleton");
} else if (b[a + 1] === ">") {
end = "/}";
types.push("singleton");
} else if (b[a + 1] === "#" || b[a + 1] === "?" || b[a + 1] === "^" || b[a + 1] === "@" || b[a + 1] === "<" || b[a + 1] === "+") {
end = "}";
types.push("template_start");
} else {
end = "}";
types.push("template");
}
} else if (b[a + 1] === "{") {
if (b[a + 2] === "{") {
end = "}}}";
types.push("template");
} else if (b[a + 2] === "#") {
end = "}}";
types.push("template_start");
} else if (b[a + 2] === "/") {
end = "}}";
types.push("template_end");
} else if (b[a + 2] === "e" && b[a + 3] === "n" && b[a + 4] === "d") {
end = "}}";
types.push("template_end");
} else if (b[a + 2] === "e" && b[a + 3] === "l" && b[a + 4] === "s" && b[a + 5] === "e") {
end = "}}";
types.push("template_else");
} else {
end = "}}";
types.push("template");
}
} else if (b[a + 1] === "%") {
end = "%}";
types.push("template");
} else if (b[a + 1] === "#") {
end = "#}";
types.push("comment");
preserve = true;
comment = true;
} else {
end = b[a + 1] + "}";
types.push("template");
}
if (b[a + 1] === "@" && b[a + 2] === "}" && b[a + 3] === "e" && b[a + 4] === "l" && b[a + 5] === "s" && b[a + 6] === "e" && b[a + 7] === "{" && b[a + 8] === "@" && b[a + 9] === "}") {
a = a + 9;
types[types.length - 1] = "template_else";
presv.push(true);
daddy.push(parent[parent.length - 1][0]);
begin.push(parent[parent.length - 1][1]);
attrs.push({});
stats.template[0] = stats.template[0] + 1;
stats.template[1] = stats.template[1] + 10;
earlyexit = true;
return token.push("{@}else{@}");
}
} else if (b[a] === "[" && b[a + 1] === "%") {
end = "%]";
types.push("template");
} else if (b[a] === "#" && options.apacheVelocity === true) {
if (b[a + 1] === "*") {
preserve = true;
comment = true;
end = "*#";
types.push("comment");
} else if (b[a + 1] === "[" && b[a + 2] === "[") {
preserve = true;
comment = true;
end = "]]#";
types.push("comment");
} else if (b[a + 1] === "#") {
preserve = true;
comment = true;
end = "\n";
types.push("comment");
} else if (b[a + 1] === "e" && b[a + 2] === "l" && b[a + 3] === "s" && b[a + 4] === "e" && (/\s/).test(b[a + 5]) === true) {
end = "\n";
types.push("template_else");
} else if (b[a + 1] === "i" && b[a + 2] === "f") {
end = "\n";
types.push("template_start");
} else if (b[a + 1] === "f" && b[a + 2] === "o" && b[a + 3] === "r" && b[a + 4] === "e" && b[a + 5] === "a" && b[a + 6] === "c" && b[a + 7] === "h") {
end = "\n";
types.push("template_start");
} else if (b[a + 1] === "e" && b[a + 2] === "n" && b[a + 3] === "d") {
end = "\n";
types.push("template_end");
} else {
end = "\n";
types.push("template");
}
} else if (b[a] === "$" && options.apacheVelocity === true) {
end = "\n";
types.push("template");
}
if (options.unformatted === true) {
preserve = true;
}
}());
if (earlyexit === true) {
return;
}
// This loop is the logic that parses tags and attributes If the attribute
// data-prettydiff-ignore is present the `ignore` flag is set The ignore flag is
// identical to the preserve flag
lastchar = end.charAt(end.length - 1);
for (a = a; a < c; a = a + 1) {
if (b[a] === "\n") {
line = line + 1;
}
if (preserve === true || (/\s/).test(b[a]) === false) {
lexer.push(b[a]);
} else if (lexer[lexer.length - 1] !== " ") {
lexer.push(" ");
}
if (comment === true) {
quote = "";
//comments must ignore fancy encapsulations and attribute parsing
if (b[a] === lastchar && lexer.length > end.length + 1) {
//if current character matches the last character of the tag ending sequence
f = lexer.length;
for (e = end.length - 1; e > -1; e = e - 1) {
f = f - 1;
if (lexer[f] !== end.charAt(e)) {
break;
}
}
if (e < 0) {
if (end === "endcomment") {
f = f - 1;
if ((/\s/).test(lexer[f]) === true) {
do {
f = f - 1;
} while ((/\s/).test(lexer[f]) === true);
}
if (lexer[f - 1] === "{" && lexer[f] === "%") {
end = "%}";
lastchar = "}";
}
} else {
break;
}
}
}
} else {
if (quote === "") {
if (options.jsx === true) {
if (b[a] === "{") {
jsxcount = jsxcount + 1;
} else if (b[a] === "}") {
jsxcount = jsxcount - 1;
}
}
if (types[types.length - 1] === "sgml" && b[a] === "[" && lexer.length > 4) {
types[types.length - 1] = "template_start";
break;
}
if (b[a] === "<" && preserve === false && lexer.length > 1 && end !== ">>" && end !== ">>>" && simple === true) {
parseError.push("Parse error on line " + line + " on element: ");
parseFail = true;
}
if (stest === true && (/\s/).test(b[a]) === false && b[a] !== lastchar) {
//attribute start
stest = false;
quote = jsxquote;
igcount = 0;
lexer.pop();
for (a = a; a < c; a = a + 1) {
if (b[a] === "\n") {
line = line + 1;
}
if (options.unformatted === true) {
lexer.push(b[a]);
}
attribute.push(b[a]);
if ((b[a] === "<" || b[a] === ">") && (quote === "" || quote === ">") && options.jsx === false) {
if (quote === "" && b[a] === "<") {
quote = ">";
braccount = 1;
} else if (quote === ">") {