@pie-lib/mask-markup 1.13.12 → 2.0.0-beta.1

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +0 -186
  2. package/lib/choices/choice.js +2 -2
  3. package/lib/choices/choice.js.map +1 -1
  4. package/lib/choices/index.js +2 -6
  5. package/lib/choices/index.js.map +1 -1
  6. package/lib/componentize.js +1 -1
  7. package/lib/componentize.js.map +1 -1
  8. package/lib/components/blank.js +1 -1
  9. package/lib/components/blank.js.map +1 -1
  10. package/lib/components/correct-input.js +1 -1
  11. package/lib/components/correct-input.js.map +1 -1
  12. package/lib/components/dropdown.js +1 -1
  13. package/lib/components/dropdown.js.map +1 -1
  14. package/lib/components/input.js +1 -1
  15. package/lib/components/input.js.map +1 -1
  16. package/lib/constructed-response.js +1 -1
  17. package/lib/constructed-response.js.map +1 -1
  18. package/lib/drag-in-the-blank.js +1 -1
  19. package/lib/drag-in-the-blank.js.map +1 -1
  20. package/lib/index.js +1 -1
  21. package/lib/index.js.map +1 -1
  22. package/lib/inline-dropdown.js +1 -1
  23. package/lib/inline-dropdown.js.map +1 -1
  24. package/lib/mask.js +10 -19
  25. package/lib/mask.js.map +1 -1
  26. package/lib/new-serialization.js +320 -0
  27. package/lib/parse-html.js +16 -0
  28. package/lib/serialization.js +33 -30
  29. package/lib/serialization.js.map +1 -1
  30. package/lib/test-serializer.js +215 -0
  31. package/lib/with-mask.js +2 -2
  32. package/lib/with-mask.js.map +1 -1
  33. package/package.json +12 -11
  34. package/src/choices/choice.jsx +13 -13
  35. package/src/choices/index.jsx +13 -17
  36. package/src/components/blank.jsx +30 -30
  37. package/src/components/correct-input.jsx +18 -18
  38. package/src/components/dropdown.jsx +38 -27
  39. package/src/components/input.jsx +3 -3
  40. package/src/constructed-response.jsx +4 -2
  41. package/src/drag-in-the-blank.jsx +11 -8
  42. package/src/index.js +8 -1
  43. package/src/inline-dropdown.jsx +3 -2
  44. package/src/mask.jsx +18 -29
  45. package/src/new-serialization.jsx +291 -0
  46. package/src/parse-html.js +8 -0
  47. package/src/serialization.js +43 -37
  48. package/src/test-serializer.js +163 -0
  49. package/src/with-mask.jsx +10 -3
  50. package/LICENSE.md +0 -5
@@ -0,0 +1,291 @@
1
+ import TestSerializer from './test-serializer';
2
+ import React from 'react';
3
+ import debug from 'debug';
4
+ import { object as toStyleObject } from 'to-style';
5
+
6
+ import { serialization as imgSerialization } from './plugins/image';
7
+ import { serialization as mathSerialization } from './plugins/math';
8
+ import { serialization as mediaSerialization } from './plugins/media';
9
+ import { serialization as listSerialization } from './plugins/list';
10
+ import { serialization as tableSerialization } from './plugins/table';
11
+ import { serialization as responseAreaSerialization } from './plugins/respArea';
12
+ import { Mark, Value } from 'slate';
13
+ import { jsx } from 'slate-hyperscript';
14
+
15
+ const log = debug('@pie-lib:editable-html:serialization');
16
+
17
+ /**
18
+ * Tags to blocks.
19
+ *
20
+ * @type {Object}
21
+ */
22
+
23
+ export const BLOCK_TAGS = {
24
+ div: 'div',
25
+ span: 'span',
26
+ p: 'paragraph',
27
+ blockquote: 'quote',
28
+ pre: 'code',
29
+ h1: 'heading-one',
30
+ h2: 'heading-two',
31
+ h3: 'heading-three',
32
+ h4: 'heading-four',
33
+ h5: 'heading-five',
34
+ h6: 'heading-six'
35
+ };
36
+
37
+ /**
38
+ * Tags to marks.
39
+ *
40
+ * @type {Object}
41
+ */
42
+
43
+ const MARK_TAGS = {
44
+ b: 'bold',
45
+ em: 'italic',
46
+ u: 'underline',
47
+ s: 'strikethrough',
48
+ code: 'code',
49
+ strong: 'bold'
50
+ };
51
+
52
+ export const parseStyleString = s => {
53
+ const regex = /([\w-]*)\s*:\s*([^;]*)/g;
54
+ let match;
55
+ const result = {};
56
+ while ((match = regex.exec(s))) {
57
+ result[match[1]] = match[2].trim();
58
+ }
59
+ return result;
60
+ };
61
+
62
+ export const getBase64 = file => {
63
+ return new Promise((resolve, reject) => {
64
+ const reader = new FileReader();
65
+ reader.readAsDataURL(file);
66
+ reader.onload = () => resolve(reader.result);
67
+ reader.onerror = error => reject(error);
68
+ });
69
+ };
70
+
71
+ export const reactAttributes = o => toStyleObject(o, { camelize: true, addUnits: false });
72
+
73
+ const attributesToMap = el => (acc, attribute) => {
74
+ const value = el.getAttribute(attribute);
75
+ if (value) {
76
+ if (attribute === 'style') {
77
+ const styleString = el.getAttribute(attribute);
78
+ const reactStyleObject = reactAttributes(parseStyleString(styleString));
79
+ acc['style'] = reactStyleObject;
80
+ } else {
81
+ acc[attribute] = el.getAttribute(attribute);
82
+ }
83
+ }
84
+ return acc;
85
+ };
86
+
87
+ const attributes = ['border', 'cellpadding', 'cellspacing', 'class', 'style'];
88
+
89
+ /**
90
+ * Serializer rules.
91
+ *
92
+ * @type {Array}
93
+ */
94
+
95
+ const blocks = {
96
+ deserialize(el, next) {
97
+ log('[blocks:deserialize] block: ', el);
98
+ const block = BLOCK_TAGS[el.tagName.toLowerCase()];
99
+ if (!block) return;
100
+ log('[blocks:deserialize] block: ', block);
101
+
102
+ if (el.childNodes.length === 1) {
103
+ const cn = el.childNodes[0];
104
+ if (cn && cn.tagName && cn.tagName.toLowerCase() === block) {
105
+ log('[we have a child node of the same]...');
106
+ return;
107
+ }
108
+ }
109
+
110
+ return jsx(
111
+ 'element',
112
+ {
113
+ type: block,
114
+ /**
115
+ * Here for rendering styles for all block elements
116
+ */
117
+ data: { attributes: attributes.reduce(attributesToMap(el), {}) }
118
+ },
119
+ next(el.childNodes)
120
+ );
121
+ },
122
+ serialize: (object, children) => {
123
+ if (object.object !== 'block') return;
124
+
125
+ const jsonData = object.data.toJSON();
126
+
127
+ log('[blocks:serialize] object: ', object, children);
128
+ let key;
129
+
130
+ for (key in BLOCK_TAGS) {
131
+ if (BLOCK_TAGS[key] === object.type) {
132
+ const Tag = key;
133
+
134
+ return <Tag {...jsonData.attributes}>{children}</Tag>;
135
+ }
136
+ }
137
+ }
138
+ };
139
+
140
+ const marks = {
141
+ deserialize(el, next) {
142
+ const mark = MARK_TAGS[el.tagName.toLowerCase()];
143
+ if (!mark) {
144
+ return;
145
+ }
146
+ log('[deserialize] mark: ', mark);
147
+
148
+ return jsx('element', { type: mark }, next(el.childNodes));
149
+ },
150
+ serialize(object, children) {
151
+ /*if (Mark.isMark(object)) {
152
+ for (var key in MARK_TAGS) {
153
+ if (MARK_TAGS[key] === object.type) {
154
+ const Tag = key;
155
+ return <Tag>{children}</Tag>;
156
+ }
157
+ }
158
+ }*/
159
+ }
160
+ };
161
+
162
+ const findPreviousText = el => {
163
+ if (el.nodeName === '#text') {
164
+ return el;
165
+ }
166
+
167
+ if (el.previousSibling) {
168
+ return findPreviousText(el.previousSibling);
169
+ }
170
+
171
+ return null;
172
+ };
173
+
174
+ export const TEXT_RULE = {
175
+ deserialize(el) {
176
+ /**
177
+ * This needs to be called on the dom element in order to merge the adjacent text nodes together
178
+ * */
179
+ el.normalize();
180
+
181
+ if (el.tagName && el.tagName.toLowerCase() === 'br') {
182
+ return jsx('text', {});
183
+ }
184
+
185
+ if (el.nodeName === '#text') {
186
+ if (el.nodeValue && el.nodeValue.match(/<!--.*?-->/)) return;
187
+
188
+ log('[text:deserialize] return text object..');
189
+ return jsx('text', {}, el.nodeValue);
190
+ }
191
+ },
192
+
193
+ serialize(obj, children) {
194
+ if (obj.object === 'string') {
195
+ return children.split('\n').reduce((array, text, i) => {
196
+ if (i !== 0) array.push(<br />);
197
+ array.push(text);
198
+ return array;
199
+ }, []);
200
+ }
201
+ }
202
+ };
203
+
204
+ const RULES = [
205
+ listSerialization,
206
+ mathSerialization,
207
+ mediaSerialization,
208
+ imgSerialization,
209
+ tableSerialization,
210
+ responseAreaSerialization,
211
+ TEXT_RULE,
212
+ blocks,
213
+ marks
214
+ ];
215
+
216
+ function allWhitespace(node) {
217
+ // Use ECMA-262 Edition 3 String and RegExp features
218
+ return !/[^\t\n\r ]/.test(node.textContent);
219
+ }
220
+
221
+ function defaultParseHtml(html) {
222
+ if (typeof DOMParser === 'undefined') {
223
+ throw new Error(
224
+ 'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.'
225
+ );
226
+ }
227
+
228
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
229
+
230
+ const { body } = parsed;
231
+ const textNodes = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, null);
232
+ let n = textNodes.nextNode();
233
+
234
+ while (n) {
235
+ if (allWhitespace(n) || n.nodeValue === '\u200B') {
236
+ n.parentNode.removeChild(n);
237
+ }
238
+ n = textNodes.nextNode();
239
+ }
240
+
241
+ return body;
242
+ }
243
+
244
+ /** If this lib is used on the server side, we need to bypass using the DOMParser - just put in a stub. */
245
+ const parseHtml =
246
+ typeof window === 'undefined'
247
+ ? () => ({
248
+ childNodes: []
249
+ })
250
+ : defaultParseHtml;
251
+
252
+ const serializer = new TestSerializer({
253
+ defaultBlock: 'div',
254
+ rules: RULES,
255
+ parseHtml
256
+ });
257
+
258
+ const _extends =
259
+ Object.assign ||
260
+ function(target) {
261
+ for (var i = 1; i < arguments.length; i++) {
262
+ var source = arguments[i];
263
+
264
+ for (var key in source) {
265
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
266
+ target[key] = source[key];
267
+ }
268
+ }
269
+ }
270
+
271
+ return target;
272
+ };
273
+
274
+ export const htmlToValue = html => {
275
+ try {
276
+ return serializer.deserialize(html);
277
+ } catch (e) {
278
+ console.log("Couldn't parse html: ", e);
279
+ return {};
280
+ }
281
+ };
282
+
283
+ export const valueToHtml = value => serializer.serialize(value);
284
+
285
+ /**
286
+ *
287
+ * <div><div>a</div></div> -> <div>a</div>
288
+ *
289
+ * <div><div>a</div><div>b</div></div> -> <div>a</div><div>b</div>
290
+ * <div><div>a</div>4444<div>b</div></div> -> <div>a</div>4444<div>b</div>
291
+ */
@@ -0,0 +1,8 @@
1
+ export const parseDegrees = html =>
2
+ html
3
+ // removes \( use case: 50°
4
+ .replace(/\\[(]/g, '')
5
+ // removes \) use case: 50°+m<1
6
+ .replace(/\\[)]/g, '')
7
+ // removes \degree use case: 50°
8
+ .replace(/\\degree/g, '&deg;');
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
- import Html from 'slate-html-serializer';
2
+ import { jsx } from 'slate-hyperscript';
3
3
  import { object as toStyleObject } from 'to-style';
4
4
  import debug from 'debug';
5
5
 
6
+ import Html from './test-serializer';
7
+
6
8
  const log = debug('@pie-lib:mask-markup:serialization');
7
9
 
8
10
  const INLINE = ['span'];
@@ -10,7 +12,7 @@ const MARK = ['em', 'strong', 'u'];
10
12
  const TEXT_NODE = 3;
11
13
  const COMMENT_NODE = 8;
12
14
 
13
- const attr = (el) => {
15
+ const attr = el => {
14
16
  if (!el.attributes || el.attributes.length <= 0) {
15
17
  return undefined;
16
18
  }
@@ -27,7 +29,7 @@ const attr = (el) => {
27
29
  return out;
28
30
  };
29
31
 
30
- const getObject = (type) => {
32
+ const getObject = type => {
31
33
  if (INLINE.includes(type)) {
32
34
  return 'inline';
33
35
  } else if (MARK.includes(type)) {
@@ -36,7 +38,7 @@ const getObject = (type) => {
36
38
  return 'block';
37
39
  };
38
40
 
39
- export const parseStyleString = (s) => {
41
+ export const parseStyleString = s => {
40
42
  const regex = /([\w-]*)\s*:\s*([^;]*)/g;
41
43
  let match;
42
44
  const result = {};
@@ -46,7 +48,7 @@ export const parseStyleString = (s) => {
46
48
  return result;
47
49
  };
48
50
 
49
- export const reactAttributes = (o) => toStyleObject(o, { camelize: true, addUnits: false });
51
+ export const reactAttributes = o => toStyleObject(o, { camelize: true, addUnits: false });
50
52
 
51
53
  const handleStyles = (el, attribute) => {
52
54
  const styleString = el.getAttribute(attribute);
@@ -62,7 +64,7 @@ const handleClass = (el, acc, attribute) => {
62
64
  return classNames;
63
65
  };
64
66
 
65
- const attributesToMap = (el) => (acc, attribute) => {
67
+ const attributesToMap = el => (acc, attribute) => {
66
68
  if (!el.getAttribute) {
67
69
  return acc;
68
70
  }
@@ -100,20 +102,20 @@ export const MARK_TAGS = {
100
102
  u: 'underline',
101
103
  s: 'strikethrough',
102
104
  code: 'code',
103
- strong: 'strong',
105
+ strong: 'strong'
104
106
  };
105
107
 
106
108
  const marks = {
107
109
  deserialize(el, next) {
108
110
  const mark = MARK_TAGS[el.tagName.toLowerCase()];
109
- if (!mark) return;
111
+
112
+ if (!mark) {
113
+ return;
114
+ }
115
+
110
116
  log('[deserialize] mark: ', mark);
111
- return {
112
- object: 'mark',
113
- type: mark,
114
- nodes: next(el.childNodes),
115
- };
116
- },
117
+ return jsx('text', { type: mark }, next(el.childNodes));
118
+ }
117
119
  };
118
120
 
119
121
  const rules = [
@@ -128,38 +130,42 @@ const rules = [
128
130
  }
129
131
 
130
132
  if (el.nodeType === TEXT_NODE) {
131
- return {
132
- object: 'text',
133
- leaves: [{ text: el.textContent }],
134
- };
133
+ return jsx('text', el.textContent);
135
134
  }
136
135
 
137
136
  const type = el.tagName.toLowerCase();
138
137
 
139
138
  const normalAttrs = attr(el) || {};
140
-
141
- if (type == 'audio' && normalAttrs.controls == '') {
142
- normalAttrs.controls = true;
143
- }
144
-
145
139
  const allAttrs = attributes.reduce(attributesToMap(el), { ...normalAttrs });
146
- const object = getObject(type);
147
140
 
148
141
  if (el.tagName.toLowerCase() === 'math') {
149
- return {
150
- isMath: true,
151
- nodes: [el],
152
- };
142
+ return jsx('element', {
143
+ type: 'mathml',
144
+ data: {
145
+ html: el.innerHTML
146
+ }
147
+ });
148
+
149
+ // return {
150
+ // isMath: true,
151
+ // nodes: [el]
152
+ // };
153
+ }
154
+
155
+ if (el.tagName.toLowerCase() === 'br') {
156
+ return jsx('element', { type, data: {} });
153
157
  }
154
158
 
155
- return {
156
- object,
157
- type,
158
- data: { dataset: { ...el.dataset }, attributes: { ...allAttrs } },
159
- nodes: next(el.childNodes),
160
- };
161
- },
162
- },
159
+ return jsx(
160
+ 'element',
161
+ {
162
+ type,
163
+ data: { dataset: { ...el.dataset }, attributes: { ...allAttrs } }
164
+ },
165
+ next(el.childNodes)
166
+ );
167
+ }
168
+ }
163
169
  ];
164
170
 
165
171
  /**
@@ -168,4 +174,4 @@ const rules = [
168
174
  */
169
175
  const html = new Html({ rules, defaultBlock: 'span' });
170
176
 
171
- export const deserialize = (s) => html.deserialize(s, { toJSON: true });
177
+ export const deserialize = s => html.deserialize(s, { toJSON: true });
@@ -0,0 +1,163 @@
1
+ import React from 'react';
2
+ import ReactServer from 'react-dom/server';
3
+ import escapeHtml from 'escape-html';
4
+ import { Text } from 'slate';
5
+ import { jsx } from 'slate-hyperscript';
6
+
7
+ function allWhitespace(node) {
8
+ // Use ECMA-262 Edition 3 String and RegExp features
9
+ return !/[^\t\n\r ]/.test(node.textContent);
10
+ }
11
+
12
+ function defaultParseHtml(html) {
13
+ if (typeof DOMParser === 'undefined') {
14
+ throw new Error(
15
+ 'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.'
16
+ );
17
+ }
18
+
19
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
20
+
21
+ const { body } = parsed;
22
+ const textNodes = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null, null);
23
+ let n = textNodes.nextNode();
24
+
25
+ while (n) {
26
+ if (allWhitespace(n) || n.nodeValue === '\u200B') {
27
+ n.parentNode.removeChild(n);
28
+ }
29
+ n = textNodes.nextNode();
30
+ }
31
+
32
+ return body;
33
+ }
34
+
35
+ class Html {
36
+ constructor(props) {
37
+ this.defaultBlock = props.defaultBlock;
38
+ this.parseHtml = defaultParseHtml;
39
+ this.rules = props.rules;
40
+ }
41
+
42
+ serializeEls = node => {
43
+ if (Text.isText(node)) {
44
+ let string = escapeHtml(node.text);
45
+ if (node.bold) {
46
+ string = <strong>{string}</strong>;
47
+ }
48
+ return string;
49
+ }
50
+
51
+ let children = (node.children || []).map(n => this.serializeEls(n));
52
+
53
+ const correctRule = this.rules.reduce((res, rule) => {
54
+ return res || rule.serialize(node, children);
55
+ }, null);
56
+
57
+ if (correctRule) {
58
+ return correctRule;
59
+ }
60
+
61
+ switch (node.type) {
62
+ case 'quote':
63
+ return (
64
+ <blockquote>
65
+ <p>{children}</p>
66
+ </blockquote>
67
+ );
68
+ case 'paragraph':
69
+ return <p>{children}</p>;
70
+ case 'link':
71
+ return <a href={escapeHtml(node.url)}>{children}</a>;
72
+ default:
73
+ return children;
74
+ }
75
+ };
76
+
77
+ serialize = node => {
78
+ const deserialized = this.serializeEls(node);
79
+ const html = ReactServer.renderToStaticMarkup(React.createElement('body', null, deserialized));
80
+ const inner = html.slice(6, -7);
81
+ return inner;
82
+ };
83
+
84
+ deserialize = html => {
85
+ let body = this.parseHtml(html);
86
+
87
+ if (body.firstChild && body.firstChild.nodeType === Node.TEXT_NODE) {
88
+ body = this.parseHtml(`<p>${html}</p>`);
89
+ }
90
+
91
+ return this.deserializeEls(body);
92
+ };
93
+
94
+ deserializeEls = (element, markAttributes = {}) => {
95
+ if (element.nodeType === Node.TEXT_NODE) {
96
+ return jsx('text', markAttributes, element.textContent);
97
+ } else if (element.nodeType !== Node.ELEMENT_NODE) {
98
+ return null;
99
+ }
100
+
101
+ const nodeAttributes = { ...markAttributes };
102
+
103
+ // define attributes for text nodes
104
+ if (element.nodeName === 'STRONG') {
105
+ nodeAttributes.bold = true;
106
+ }
107
+
108
+ const nextFn = nodes => {
109
+ const childNodes = Array.from(nodes);
110
+ const children = Array.from(childNodes)
111
+ .map(node => this.deserializeEls(node, nodeAttributes))
112
+ .flat();
113
+
114
+ if (children.length === 0) {
115
+ children.push(jsx('text', nodeAttributes, ''));
116
+ }
117
+
118
+ return children;
119
+ };
120
+
121
+ const correctRule = this.rules.reduce((res, rule) => {
122
+ return res || rule.deserialize(element, nextFn);
123
+ }, null);
124
+
125
+ if (correctRule) {
126
+ return correctRule;
127
+ }
128
+
129
+ const childNodes = Array.from(element.childNodes);
130
+ const children = Array.from(childNodes)
131
+ .map(node => this.deserializeEls(node, nodeAttributes))
132
+ .flat();
133
+
134
+ if (children.length === 0) {
135
+ children.push(jsx('text', nodeAttributes, ''));
136
+ }
137
+
138
+ switch (element.nodeName) {
139
+ case 'TABLE':
140
+ return jsx('element', { type: 'table' }, children);
141
+ case 'TBODY':
142
+ return jsx('element', { type: 'tbody' }, children);
143
+ case 'TR':
144
+ return jsx('element', { type: 'tr' }, children);
145
+ case 'TD':
146
+ return jsx('element', { type: 'td' }, children);
147
+ case 'BODY':
148
+ return jsx('fragment', {}, children);
149
+ case 'BR':
150
+ return '\n';
151
+ case 'BLOCKQUOTE':
152
+ return jsx('element', { type: 'quote' }, children);
153
+ case 'P':
154
+ return jsx('element', { type: 'paragraph' }, children);
155
+ case 'A':
156
+ return jsx('element', { type: 'link', url: element.getAttribute('href') }, children);
157
+ default:
158
+ return children;
159
+ }
160
+ };
161
+ }
162
+
163
+ export default Html;
package/src/with-mask.jsx CHANGED
@@ -7,7 +7,7 @@ import { deserialize } from './serialization';
7
7
  export const buildLayoutFromMarkup = (markup, type) => {
8
8
  const { markup: processed } = componentize(markup, type);
9
9
  const value = deserialize(processed);
10
- return value.document;
10
+ return value;
11
11
  };
12
12
 
13
13
  export const withMask = (type, renderChildren) => {
@@ -22,14 +22,21 @@ export const withMask = (type, renderChildren) => {
22
22
  */
23
23
  layout: PropTypes.object,
24
24
  value: PropTypes.object,
25
- onChange: PropTypes.func,
25
+ onChange: PropTypes.func
26
26
  };
27
27
 
28
28
  render() {
29
29
  const { markup, layout, value, onChange } = this.props;
30
30
 
31
31
  const maskLayout = layout ? layout : buildLayoutFromMarkup(markup, type);
32
- return <Mask layout={maskLayout} value={value} onChange={onChange} renderChildren={renderChildren(this.props)} />;
32
+ return (
33
+ <Mask
34
+ layout={maskLayout}
35
+ value={value}
36
+ onChange={onChange}
37
+ renderChildren={renderChildren(this.props)}
38
+ />
39
+ );
33
40
  }
34
41
  };
35
42
  };
package/LICENSE.md DELETED
@@ -1,5 +0,0 @@
1
- Copyright 2019 CoreSpring Inc
2
-
3
- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
-
5
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.