@depup/mammoth 1.12.0-depup.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +75 -0
- package/.github/ISSUE_TEMPLATE.md +12 -0
- package/.github/pull_request_template.md +3 -0
- package/.github/workflows/tests.yml +26 -0
- package/LICENSE +22 -0
- package/NEWS +530 -0
- package/README.md +37 -0
- package/bin/mammoth +38 -0
- package/browser/docx/files.js +14 -0
- package/browser/unzip.js +12 -0
- package/changes.json +34 -0
- package/lib/document-to-html.js +469 -0
- package/lib/documents.js +254 -0
- package/lib/docx/body-reader.js +798 -0
- package/lib/docx/comments-reader.js +31 -0
- package/lib/docx/content-types-reader.js +58 -0
- package/lib/docx/document-xml-reader.js +30 -0
- package/lib/docx/docx-reader.js +226 -0
- package/lib/docx/files.js +80 -0
- package/lib/docx/notes-reader.js +28 -0
- package/lib/docx/numbering-xml.js +111 -0
- package/lib/docx/office-xml-reader.js +73 -0
- package/lib/docx/relationships-reader.js +43 -0
- package/lib/docx/style-map.js +75 -0
- package/lib/docx/styles-reader.js +90 -0
- package/lib/docx/uris.js +21 -0
- package/lib/html/ast.js +51 -0
- package/lib/html/index.js +41 -0
- package/lib/html/simplify.js +88 -0
- package/lib/images.js +31 -0
- package/lib/index.d.ts +86 -0
- package/lib/index.js +111 -0
- package/lib/main.js +63 -0
- package/lib/options-reader.js +107 -0
- package/lib/promises.js +42 -0
- package/lib/raw-text.js +14 -0
- package/lib/results.js +72 -0
- package/lib/style-reader.js +365 -0
- package/lib/styles/document-matchers.js +100 -0
- package/lib/styles/html-paths.js +75 -0
- package/lib/styles/parser/tokeniser.js +30 -0
- package/lib/transforms.js +62 -0
- package/lib/underline.js +11 -0
- package/lib/unzip.js +20 -0
- package/lib/writers/html-writer.js +159 -0
- package/lib/writers/index.js +14 -0
- package/lib/writers/markdown-writer.js +163 -0
- package/lib/xml/index.js +8 -0
- package/lib/xml/nodes.js +70 -0
- package/lib/xml/reader.js +69 -0
- package/lib/xml/writer.js +61 -0
- package/lib/xml/xmldom.js +23 -0
- package/lib/zipfile.js +72 -0
- package/mammoth.browser.js +22096 -0
- package/mammoth.browser.min.js +20 -0
- package/package.json +106 -0
- package/test/.eslintrc.json +7 -0
- package/test/document-to-html.tests.js +892 -0
- package/test/docx/body-reader.tests.js +2037 -0
- package/test/docx/comments-reader.tests.js +53 -0
- package/test/docx/content-types-reader.tests.js +45 -0
- package/test/docx/document-matchers.js +42 -0
- package/test/docx/document-xml-reader.tests.js +41 -0
- package/test/docx/docx-reader.tests.js +179 -0
- package/test/docx/files.tests.js +107 -0
- package/test/docx/notes-reader.tests.js +36 -0
- package/test/docx/numbering-xml.tests.js +183 -0
- package/test/docx/office-xml-reader.tests.js +41 -0
- package/test/docx/relationships-reader.tests.js +65 -0
- package/test/docx/style-map.tests.js +113 -0
- package/test/docx/styles-reader.tests.js +143 -0
- package/test/docx/testing.js +12 -0
- package/test/docx/uris.tests.js +22 -0
- package/test/html/simplify.tests.js +141 -0
- package/test/html/write.tests.js +42 -0
- package/test/images.tests.js +112 -0
- package/test/main.tests.js +89 -0
- package/test/mammoth.tests.js +523 -0
- package/test/mocha.opts +1 -0
- package/test/options-reader.tests.js +63 -0
- package/test/raw-text.tests.js +61 -0
- package/test/results.tests.js +15 -0
- package/test/style-reader.tests.js +291 -0
- package/test/styles/document-matchers.tests.js +91 -0
- package/test/styles/html-paths.tests.js +20 -0
- package/test/styles/parser/tokeniser.tests.js +104 -0
- package/test/test-data/comments.docx +0 -0
- package/test/test-data/embedded-style-map.docx +0 -0
- package/test/test-data/empty.docx +0 -0
- package/test/test-data/empty.zip +0 -0
- package/test/test-data/endnotes.docx +0 -0
- package/test/test-data/external-picture.docx +0 -0
- package/test/test-data/footnote-hyperlink.docx +0 -0
- package/test/test-data/footnotes.docx +0 -0
- package/test/test-data/hello.zip +0 -0
- package/test/test-data/hyperlinks/word/_rels/document.xml.rels +10 -0
- package/test/test-data/hyperlinks/word/document.xml +18 -0
- package/test/test-data/simple/word/document.xml +18 -0
- package/test/test-data/simple-list.docx +0 -0
- package/test/test-data/single-paragraph.docx +0 -0
- package/test/test-data/strict-format.docx +0 -0
- package/test/test-data/strikethrough.docx +0 -0
- package/test/test-data/tables.docx +0 -0
- package/test/test-data/text-box.docx +0 -0
- package/test/test-data/tiny-picture-target-base-relative.docx +0 -0
- package/test/test-data/tiny-picture.docx +0 -0
- package/test/test-data/tiny-picture.png +0 -0
- package/test/test-data/underline.docx +0 -0
- package/test/test-data/utf8-bom.docx +0 -0
- package/test/test.js +11 -0
- package/test/testing.js +55 -0
- package/test/transforms.tests.js +125 -0
- package/test/unzip.tests.js +38 -0
- package/test/writers/html-writer.tests.js +133 -0
- package/test/writers/markdown-writer.tests.js +304 -0
- package/test/xml/reader.tests.js +85 -0
- package/test/xml/writer.tests.js +81 -0
- package/test/zipfile.tests.js +61 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
var html = require("../html");
|
|
4
|
+
|
|
5
|
+
exports.topLevelElement = topLevelElement;
|
|
6
|
+
exports.elements = elements;
|
|
7
|
+
exports.element = element;
|
|
8
|
+
|
|
9
|
+
function topLevelElement(tagName, attributes) {
|
|
10
|
+
return elements([element(tagName, attributes, {fresh: true})]);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function elements(elementStyles) {
|
|
14
|
+
return new HtmlPath(elementStyles.map(function(elementStyle) {
|
|
15
|
+
if (_.isString(elementStyle)) {
|
|
16
|
+
return element(elementStyle);
|
|
17
|
+
} else {
|
|
18
|
+
return elementStyle;
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function HtmlPath(elements) {
|
|
24
|
+
this._elements = elements;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
HtmlPath.prototype.wrap = function wrap(children) {
|
|
28
|
+
var result = children();
|
|
29
|
+
for (var index = this._elements.length - 1; index >= 0; index--) {
|
|
30
|
+
result = this._elements[index].wrapNodes(result);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function element(tagName, attributes, options) {
|
|
36
|
+
options = options || {};
|
|
37
|
+
return new Element(tagName, attributes, options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Element(tagName, attributes, options) {
|
|
41
|
+
var tagNames = {};
|
|
42
|
+
if (_.isArray(tagName)) {
|
|
43
|
+
tagName.forEach(function(tagName) {
|
|
44
|
+
tagNames[tagName] = true;
|
|
45
|
+
});
|
|
46
|
+
tagName = tagName[0];
|
|
47
|
+
} else {
|
|
48
|
+
tagNames[tagName] = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.tagName = tagName;
|
|
52
|
+
this.tagNames = tagNames;
|
|
53
|
+
this.attributes = attributes || {};
|
|
54
|
+
this.fresh = options.fresh;
|
|
55
|
+
this.separator = options.separator;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Element.prototype.matchesElement = function(element) {
|
|
59
|
+
return this.tagNames[element.tagName] && _.isEqual(this.attributes || {}, element.attributes || {});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
Element.prototype.wrap = function wrap(generateNodes) {
|
|
63
|
+
return this.wrapNodes(generateNodes());
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
Element.prototype.wrapNodes = function wrapNodes(nodes) {
|
|
67
|
+
return [html.elementWithTag(this, nodes)];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
exports.empty = elements([]);
|
|
71
|
+
exports.ignore = {
|
|
72
|
+
wrap: function() {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var lop = require("lop");
|
|
2
|
+
var RegexTokeniser = lop.RegexTokeniser;
|
|
3
|
+
|
|
4
|
+
exports.tokenise = tokenise;
|
|
5
|
+
|
|
6
|
+
var stringPrefix = "'((?:\\\\.|[^'])*)";
|
|
7
|
+
|
|
8
|
+
function tokenise(string) {
|
|
9
|
+
var identifierCharacter = "(?:[a-zA-Z\\-_]|\\\\.)";
|
|
10
|
+
var tokeniser = new RegexTokeniser([
|
|
11
|
+
{name: "identifier", regex: new RegExp("(" + identifierCharacter + "(?:" + identifierCharacter + "|[0-9])*)")},
|
|
12
|
+
{name: "dot", regex: /\./},
|
|
13
|
+
{name: "colon", regex: /:/},
|
|
14
|
+
{name: "gt", regex: />/},
|
|
15
|
+
{name: "whitespace", regex: /\s+/},
|
|
16
|
+
{name: "arrow", regex: /=>/},
|
|
17
|
+
{name: "equals", regex: /=/},
|
|
18
|
+
{name: "startsWith", regex: /\^=/},
|
|
19
|
+
{name: "open-paren", regex: /\(/},
|
|
20
|
+
{name: "close-paren", regex: /\)/},
|
|
21
|
+
{name: "open-square-bracket", regex: /\[/},
|
|
22
|
+
{name: "close-square-bracket", regex: /\]/},
|
|
23
|
+
{name: "string", regex: new RegExp(stringPrefix + "'")},
|
|
24
|
+
{name: "unterminated-string", regex: new RegExp(stringPrefix)},
|
|
25
|
+
{name: "integer", regex: /([0-9]+)/},
|
|
26
|
+
{name: "choice", regex: /\|/},
|
|
27
|
+
{name: "bang", regex: /(!)/}
|
|
28
|
+
]);
|
|
29
|
+
return tokeniser.tokenise(string);
|
|
30
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
exports.paragraph = paragraph;
|
|
4
|
+
exports.run = run;
|
|
5
|
+
exports._elements = elements;
|
|
6
|
+
exports._elementsOfType = elementsOfType;
|
|
7
|
+
exports.getDescendantsOfType = getDescendantsOfType;
|
|
8
|
+
exports.getDescendants = getDescendants;
|
|
9
|
+
|
|
10
|
+
function paragraph(transform) {
|
|
11
|
+
return elementsOfType("paragraph", transform);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function run(transform) {
|
|
15
|
+
return elementsOfType("run", transform);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function elementsOfType(elementType, transform) {
|
|
19
|
+
return elements(function(element) {
|
|
20
|
+
if (element.type === elementType) {
|
|
21
|
+
return transform(element);
|
|
22
|
+
} else {
|
|
23
|
+
return element;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function elements(transform) {
|
|
29
|
+
return function transformElement(element) {
|
|
30
|
+
if (element.children) {
|
|
31
|
+
var children = _.map(element.children, transformElement);
|
|
32
|
+
element = _.extend(element, {children: children});
|
|
33
|
+
}
|
|
34
|
+
return transform(element);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
function getDescendantsOfType(element, type) {
|
|
40
|
+
return getDescendants(element).filter(function(descendant) {
|
|
41
|
+
return descendant.type === type;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getDescendants(element) {
|
|
46
|
+
var descendants = [];
|
|
47
|
+
|
|
48
|
+
visitDescendants(element, function(descendant) {
|
|
49
|
+
descendants.push(descendant);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return descendants;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function visitDescendants(element, visit) {
|
|
56
|
+
if (element.children) {
|
|
57
|
+
element.children.forEach(function(child) {
|
|
58
|
+
visitDescendants(child, visit);
|
|
59
|
+
visit(child);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lib/underline.js
ADDED
package/lib/unzip.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var fs = require("fs");
|
|
2
|
+
|
|
3
|
+
var promises = require("./promises");
|
|
4
|
+
var zipfile = require("./zipfile");
|
|
5
|
+
|
|
6
|
+
exports.openZip = openZip;
|
|
7
|
+
|
|
8
|
+
var readFile = promises.promisify(fs.readFile);
|
|
9
|
+
|
|
10
|
+
function openZip(options) {
|
|
11
|
+
if (options.path) {
|
|
12
|
+
return readFile(options.path).then(zipfile.openArrayBuffer);
|
|
13
|
+
} else if (options.buffer) {
|
|
14
|
+
return promises.resolve(zipfile.openArrayBuffer(options.buffer));
|
|
15
|
+
} else if (options.file) {
|
|
16
|
+
return promises.resolve(options.file);
|
|
17
|
+
} else {
|
|
18
|
+
return promises.reject(new Error("Could not find file in options"));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
exports.writer = writer;
|
|
4
|
+
|
|
5
|
+
function writer(options) {
|
|
6
|
+
options = options || {};
|
|
7
|
+
if (options.prettyPrint) {
|
|
8
|
+
return prettyWriter();
|
|
9
|
+
} else {
|
|
10
|
+
return simpleWriter();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
var indentedElements = {
|
|
16
|
+
div: true,
|
|
17
|
+
p: true,
|
|
18
|
+
ul: true,
|
|
19
|
+
li: true
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function prettyWriter() {
|
|
24
|
+
var indentationLevel = 0;
|
|
25
|
+
var indentation = " ";
|
|
26
|
+
var stack = [];
|
|
27
|
+
var start = true;
|
|
28
|
+
var inText = false;
|
|
29
|
+
|
|
30
|
+
var writer = simpleWriter();
|
|
31
|
+
|
|
32
|
+
function open(tagName, attributes) {
|
|
33
|
+
if (indentedElements[tagName]) {
|
|
34
|
+
indent();
|
|
35
|
+
}
|
|
36
|
+
stack.push(tagName);
|
|
37
|
+
writer.open(tagName, attributes);
|
|
38
|
+
if (indentedElements[tagName]) {
|
|
39
|
+
indentationLevel++;
|
|
40
|
+
}
|
|
41
|
+
start = false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function close(tagName) {
|
|
45
|
+
if (indentedElements[tagName]) {
|
|
46
|
+
indentationLevel--;
|
|
47
|
+
indent();
|
|
48
|
+
}
|
|
49
|
+
stack.pop();
|
|
50
|
+
writer.close(tagName);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function text(value) {
|
|
54
|
+
startText();
|
|
55
|
+
var text = isInPre() ? value : value.replace("\n", "\n" + indentation);
|
|
56
|
+
writer.text(text);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function selfClosing(tagName, attributes) {
|
|
60
|
+
indent();
|
|
61
|
+
writer.selfClosing(tagName, attributes);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function insideIndentedElement() {
|
|
65
|
+
return stack.length === 0 || indentedElements[stack[stack.length - 1]];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function startText() {
|
|
69
|
+
if (!inText) {
|
|
70
|
+
indent();
|
|
71
|
+
inText = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function indent() {
|
|
76
|
+
inText = false;
|
|
77
|
+
if (!start && insideIndentedElement() && !isInPre()) {
|
|
78
|
+
writer._append("\n");
|
|
79
|
+
for (var i = 0; i < indentationLevel; i++) {
|
|
80
|
+
writer._append(indentation);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isInPre() {
|
|
86
|
+
return _.some(stack, function(tagName) {
|
|
87
|
+
return tagName === "pre";
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
asString: writer.asString,
|
|
93
|
+
open: open,
|
|
94
|
+
close: close,
|
|
95
|
+
text: text,
|
|
96
|
+
selfClosing: selfClosing
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
function simpleWriter() {
|
|
102
|
+
var fragments = [];
|
|
103
|
+
|
|
104
|
+
function open(tagName, attributes) {
|
|
105
|
+
var attributeString = generateAttributeString(attributes);
|
|
106
|
+
fragments.push("<" + tagName + attributeString + ">");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function close(tagName) {
|
|
110
|
+
fragments.push("</" + tagName + ">");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function selfClosing(tagName, attributes) {
|
|
114
|
+
var attributeString = generateAttributeString(attributes);
|
|
115
|
+
fragments.push("<" + tagName + attributeString + " />");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function generateAttributeString(attributes) {
|
|
119
|
+
return _.map(attributes, function(value, key) {
|
|
120
|
+
return " " + key + '="' + escapeHtmlAttribute(value) + '"';
|
|
121
|
+
}).join("");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function text(value) {
|
|
125
|
+
fragments.push(escapeHtmlText(value));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function append(html) {
|
|
129
|
+
fragments.push(html);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function asString() {
|
|
133
|
+
return fragments.join("");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
asString: asString,
|
|
138
|
+
open: open,
|
|
139
|
+
close: close,
|
|
140
|
+
text: text,
|
|
141
|
+
selfClosing: selfClosing,
|
|
142
|
+
_append: append
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function escapeHtmlText(value) {
|
|
147
|
+
return value
|
|
148
|
+
.replace(/&/g, '&')
|
|
149
|
+
.replace(/</g, '<')
|
|
150
|
+
.replace(/>/g, '>');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function escapeHtmlAttribute(value) {
|
|
154
|
+
return value
|
|
155
|
+
.replace(/&/g, '&')
|
|
156
|
+
.replace(/"/g, '"')
|
|
157
|
+
.replace(/</g, '<')
|
|
158
|
+
.replace(/>/g, '>');
|
|
159
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var htmlWriter = require("./html-writer");
|
|
2
|
+
var markdownWriter = require("./markdown-writer");
|
|
3
|
+
|
|
4
|
+
exports.writer = writer;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function writer(options) {
|
|
8
|
+
options = options || {};
|
|
9
|
+
if (options.outputFormat === "markdown") {
|
|
10
|
+
return markdownWriter.writer();
|
|
11
|
+
} else {
|
|
12
|
+
return htmlWriter.writer(options);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function symmetricMarkdownElement(end) {
|
|
5
|
+
return markdownElement(end, end);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function markdownElement(start, end) {
|
|
9
|
+
return function() {
|
|
10
|
+
return {start: start, end: end};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function markdownLink(attributes) {
|
|
15
|
+
var href = attributes.href || "";
|
|
16
|
+
if (href) {
|
|
17
|
+
return {
|
|
18
|
+
start: "[",
|
|
19
|
+
end: "](" + href + ")",
|
|
20
|
+
anchorPosition: "before"
|
|
21
|
+
};
|
|
22
|
+
} else {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function markdownImage(attributes) {
|
|
28
|
+
var src = attributes.src || "";
|
|
29
|
+
var altText = attributes.alt || "";
|
|
30
|
+
if (src || altText) {
|
|
31
|
+
return {start: ""};
|
|
32
|
+
} else {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function markdownList(options) {
|
|
38
|
+
return function(attributes, list) {
|
|
39
|
+
return {
|
|
40
|
+
start: list ? "\n" : "",
|
|
41
|
+
end: list ? "" : "\n",
|
|
42
|
+
list: {
|
|
43
|
+
isOrdered: options.isOrdered,
|
|
44
|
+
indent: list ? list.indent + 1 : 0,
|
|
45
|
+
count: 0
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function markdownListItem(attributes, list, listItem) {
|
|
52
|
+
list = list || {indent: 0, isOrdered: false, count: 0};
|
|
53
|
+
list.count++;
|
|
54
|
+
listItem.hasClosed = false;
|
|
55
|
+
|
|
56
|
+
var bullet = list.isOrdered ? list.count + "." : "-";
|
|
57
|
+
var start = repeatString("\t", list.indent) + bullet + " ";
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
start: start,
|
|
61
|
+
end: function() {
|
|
62
|
+
if (!listItem.hasClosed) {
|
|
63
|
+
listItem.hasClosed = true;
|
|
64
|
+
return "\n";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var htmlToMarkdown = {
|
|
71
|
+
"p": markdownElement("", "\n\n"),
|
|
72
|
+
"br": markdownElement("", " \n"),
|
|
73
|
+
"ul": markdownList({isOrdered: false}),
|
|
74
|
+
"ol": markdownList({isOrdered: true}),
|
|
75
|
+
"li": markdownListItem,
|
|
76
|
+
"strong": symmetricMarkdownElement("__"),
|
|
77
|
+
"em": symmetricMarkdownElement("*"),
|
|
78
|
+
"a": markdownLink,
|
|
79
|
+
"img": markdownImage
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
(function() {
|
|
83
|
+
for (var i = 1; i <= 6; i++) {
|
|
84
|
+
htmlToMarkdown["h" + i] = markdownElement(repeatString("#", i) + " ", "\n\n");
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
|
|
88
|
+
function repeatString(value, count) {
|
|
89
|
+
return new Array(count + 1).join(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function markdownWriter() {
|
|
93
|
+
var fragments = [];
|
|
94
|
+
var elementStack = [];
|
|
95
|
+
var list = null;
|
|
96
|
+
var listItem = {};
|
|
97
|
+
|
|
98
|
+
function open(tagName, attributes) {
|
|
99
|
+
attributes = attributes || {};
|
|
100
|
+
|
|
101
|
+
var createElement = htmlToMarkdown[tagName] || function() {
|
|
102
|
+
return {};
|
|
103
|
+
};
|
|
104
|
+
var element = createElement(attributes, list, listItem);
|
|
105
|
+
elementStack.push({end: element.end, list: list});
|
|
106
|
+
|
|
107
|
+
if (element.list) {
|
|
108
|
+
list = element.list;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var anchorBeforeStart = element.anchorPosition === "before";
|
|
112
|
+
if (anchorBeforeStart) {
|
|
113
|
+
writeAnchor(attributes);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fragments.push(element.start || "");
|
|
117
|
+
if (!anchorBeforeStart) {
|
|
118
|
+
writeAnchor(attributes);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function writeAnchor(attributes) {
|
|
123
|
+
if (attributes.id) {
|
|
124
|
+
fragments.push('<a id="' + attributes.id + '"></a>');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function close(tagName) {
|
|
129
|
+
var element = elementStack.pop();
|
|
130
|
+
list = element.list;
|
|
131
|
+
var end = _.isFunction(element.end) ? element.end() : element.end;
|
|
132
|
+
fragments.push(end || "");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function selfClosing(tagName, attributes) {
|
|
136
|
+
open(tagName, attributes);
|
|
137
|
+
close(tagName);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function text(value) {
|
|
141
|
+
fragments.push(escapeMarkdown(value));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function asString() {
|
|
145
|
+
return fragments.join("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
asString: asString,
|
|
150
|
+
open: open,
|
|
151
|
+
close: close,
|
|
152
|
+
text: text,
|
|
153
|
+
selfClosing: selfClosing
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
exports.writer = markdownWriter;
|
|
158
|
+
|
|
159
|
+
function escapeMarkdown(value) {
|
|
160
|
+
return value
|
|
161
|
+
.replace(/\\/g, '\\\\')
|
|
162
|
+
.replace(/([\`\*_\{\}\[\]\(\)\#\+\-\.\!])/g, '\\$1');
|
|
163
|
+
}
|
package/lib/xml/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var nodes = require("./nodes");
|
|
2
|
+
|
|
3
|
+
exports.Element = nodes.Element;
|
|
4
|
+
exports.element = nodes.element;
|
|
5
|
+
exports.emptyElement = nodes.emptyElement;
|
|
6
|
+
exports.text = nodes.text;
|
|
7
|
+
exports.readString = require("./reader").readString;
|
|
8
|
+
exports.writeString = require("./writer").writeString;
|
package/lib/xml/nodes.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
exports.Element = Element;
|
|
5
|
+
exports.element = function(name, attributes, children) {
|
|
6
|
+
return new Element(name, attributes, children);
|
|
7
|
+
};
|
|
8
|
+
exports.text = function(value) {
|
|
9
|
+
return {
|
|
10
|
+
type: "text",
|
|
11
|
+
value: value
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
var emptyElement = exports.emptyElement = {
|
|
17
|
+
first: function() {
|
|
18
|
+
return null;
|
|
19
|
+
},
|
|
20
|
+
firstOrEmpty: function() {
|
|
21
|
+
return emptyElement;
|
|
22
|
+
},
|
|
23
|
+
attributes: {},
|
|
24
|
+
children: []
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function Element(name, attributes, children) {
|
|
28
|
+
this.type = "element";
|
|
29
|
+
this.name = name;
|
|
30
|
+
this.attributes = attributes || {};
|
|
31
|
+
this.children = children || [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Element.prototype.first = function(name) {
|
|
35
|
+
return _.find(this.children, function(child) {
|
|
36
|
+
return child.name === name;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
Element.prototype.firstOrEmpty = function(name) {
|
|
41
|
+
return this.first(name) || emptyElement;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
Element.prototype.getElementsByTagName = function(name) {
|
|
45
|
+
var elements = _.filter(this.children, function(child) {
|
|
46
|
+
return child.name === name;
|
|
47
|
+
});
|
|
48
|
+
return toElementList(elements);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
Element.prototype.text = function() {
|
|
52
|
+
if (this.children.length === 0) {
|
|
53
|
+
return "";
|
|
54
|
+
} else if (this.children.length !== 1 || this.children[0].type !== "text") {
|
|
55
|
+
throw new Error("Not implemented");
|
|
56
|
+
}
|
|
57
|
+
return this.children[0].value;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
var elementListPrototype = {
|
|
61
|
+
getElementsByTagName: function(name) {
|
|
62
|
+
return toElementList(_.flatten(this.map(function(element) {
|
|
63
|
+
return element.getElementsByTagName(name);
|
|
64
|
+
}, true)));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function toElementList(array) {
|
|
69
|
+
return _.extend(array, elementListPrototype);
|
|
70
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var promises = require("../promises");
|
|
2
|
+
var _ = require("underscore");
|
|
3
|
+
|
|
4
|
+
var xmldom = require("./xmldom");
|
|
5
|
+
var nodes = require("./nodes");
|
|
6
|
+
var Element = nodes.Element;
|
|
7
|
+
|
|
8
|
+
exports.readString = readString;
|
|
9
|
+
|
|
10
|
+
var Node = xmldom.Node;
|
|
11
|
+
|
|
12
|
+
function readString(xmlString, namespaceMap) {
|
|
13
|
+
namespaceMap = namespaceMap || {};
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
var document = xmldom.parseFromString(xmlString, "text/xml");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return promises.reject(error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (document.documentElement.tagName === "parsererror") {
|
|
22
|
+
return promises.resolve(new Error(document.documentElement.textContent));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function convertNode(node) {
|
|
26
|
+
switch (node.nodeType) {
|
|
27
|
+
case Node.ELEMENT_NODE:
|
|
28
|
+
return convertElement(node);
|
|
29
|
+
case Node.TEXT_NODE:
|
|
30
|
+
return nodes.text(node.nodeValue);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function convertElement(element) {
|
|
35
|
+
var convertedName = convertName(element);
|
|
36
|
+
|
|
37
|
+
var convertedChildren = [];
|
|
38
|
+
_.forEach(element.childNodes, function(childNode) {
|
|
39
|
+
var convertedNode = convertNode(childNode);
|
|
40
|
+
if (convertedNode) {
|
|
41
|
+
convertedChildren.push(convertedNode);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
var convertedAttributes = {};
|
|
46
|
+
_.forEach(element.attributes, function(attribute) {
|
|
47
|
+
convertedAttributes[convertName(attribute)] = attribute.value;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return new Element(convertedName, convertedAttributes, convertedChildren);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function convertName(node) {
|
|
54
|
+
if (node.namespaceURI) {
|
|
55
|
+
var mappedPrefix = namespaceMap[node.namespaceURI];
|
|
56
|
+
var prefix;
|
|
57
|
+
if (mappedPrefix) {
|
|
58
|
+
prefix = mappedPrefix + ":";
|
|
59
|
+
} else {
|
|
60
|
+
prefix = "{" + node.namespaceURI + "}";
|
|
61
|
+
}
|
|
62
|
+
return prefix + node.localName;
|
|
63
|
+
} else {
|
|
64
|
+
return node.localName;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return promises.resolve(convertNode(document.documentElement));
|
|
69
|
+
}
|