solid-ui 2.4.23 → 2.4.24

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 (136) hide show
  1. package/dist/main.js +2159 -5039
  2. package/dist/main.js.map +1 -1
  3. package/lib/acl/access-controller.js +4 -67
  4. package/lib/acl/access-controller.js.map +1 -1
  5. package/lib/acl/access-groups.js +59 -137
  6. package/lib/acl/access-groups.js.map +1 -1
  7. package/lib/acl/acl-control.js +14 -54
  8. package/lib/acl/acl-control.js.map +1 -1
  9. package/lib/acl/acl.js +32 -107
  10. package/lib/acl/acl.js.map +1 -1
  11. package/lib/acl/add-agent-buttons.js +0 -92
  12. package/lib/acl/add-agent-buttons.js.map +1 -1
  13. package/lib/acl/index.js +1 -3
  14. package/lib/acl/index.js.map +1 -1
  15. package/lib/acl/styles.js +1 -2
  16. package/lib/acl/styles.js.map +1 -1
  17. package/lib/chat/bookmarks.js +8 -80
  18. package/lib/chat/bookmarks.js.map +1 -1
  19. package/lib/chat/chatLogic.js +109 -180
  20. package/lib/chat/chatLogic.js.map +1 -1
  21. package/lib/chat/dateFolder.js +6 -91
  22. package/lib/chat/dateFolder.js.map +1 -1
  23. package/lib/chat/infinite.js +33 -185
  24. package/lib/chat/infinite.js.map +1 -1
  25. package/lib/chat/message.js +37 -150
  26. package/lib/chat/message.js.map +1 -1
  27. package/lib/chat/messageTools.js +23 -96
  28. package/lib/chat/messageTools.js.map +1 -1
  29. package/lib/chat/thread.js +33 -91
  30. package/lib/chat/thread.js.map +1 -1
  31. package/lib/create/create.js +19 -51
  32. package/lib/create/create.js.map +1 -1
  33. package/lib/create/index.js +0 -2
  34. package/lib/create/index.js.map +1 -1
  35. package/lib/debug.js +0 -8
  36. package/lib/debug.js.map +1 -1
  37. package/lib/folders.js +4 -27
  38. package/lib/folders.js.map +1 -1
  39. package/lib/footer/index.js +2 -24
  40. package/lib/footer/index.js.map +1 -1
  41. package/lib/footer/styleMap.js.map +1 -1
  42. package/lib/header/empty-profile.js.map +1 -1
  43. package/lib/header/index.js +3 -72
  44. package/lib/header/index.js.map +1 -1
  45. package/lib/header/styleMap.js +4 -3
  46. package/lib/header/styleMap.js.map +1 -1
  47. package/lib/iconBase.js +3 -4
  48. package/lib/iconBase.js.map +1 -1
  49. package/lib/index.js +5 -46
  50. package/lib/index.js.map +1 -1
  51. package/lib/jss/index.js +3 -9
  52. package/lib/jss/index.js.map +1 -1
  53. package/lib/log.js +15 -51
  54. package/lib/log.js.map +1 -1
  55. package/lib/login/login.js +74 -303
  56. package/lib/login/login.js.map +1 -1
  57. package/lib/matrix/index.js +0 -2
  58. package/lib/matrix/index.js.map +1 -1
  59. package/lib/matrix/matrix.js +7 -55
  60. package/lib/matrix/matrix.js.map +1 -1
  61. package/lib/media/index.js +0 -2
  62. package/lib/media/index.js.map +1 -1
  63. package/lib/media/media-capture.js +13 -33
  64. package/lib/media/media-capture.js.map +1 -1
  65. package/lib/messageArea.js +23 -64
  66. package/lib/messageArea.js.map +1 -1
  67. package/lib/noun_Camera_1618446_000000.js.map +1 -1
  68. package/lib/ns.js +5 -7
  69. package/lib/ns.js.map +1 -1
  70. package/lib/pad.js +48 -234
  71. package/lib/pad.js.map +1 -1
  72. package/lib/participation.js +10 -65
  73. package/lib/participation.js.map +1 -1
  74. package/lib/preferences.js +21 -42
  75. package/lib/preferences.js.map +1 -1
  76. package/lib/signup/config-default.js +1 -8
  77. package/lib/signup/config-default.js.map +1 -1
  78. package/lib/signup/signup.js +6 -13
  79. package/lib/signup/signup.js.map +1 -1
  80. package/lib/stories/decorators.js +0 -2
  81. package/lib/stories/decorators.js.map +1 -1
  82. package/lib/style.js +8 -5
  83. package/lib/style.js.map +1 -1
  84. package/lib/style_multiSelect.js +1 -5
  85. package/lib/style_multiSelect.js.map +1 -1
  86. package/lib/table.js +309 -322
  87. package/lib/table.js.map +1 -1
  88. package/lib/tabs.js +20 -100
  89. package/lib/tabs.js.map +1 -1
  90. package/lib/utils/headerFooterHelpers.js +4 -52
  91. package/lib/utils/headerFooterHelpers.js.map +1 -1
  92. package/lib/utils/index.js +41 -135
  93. package/lib/utils/index.js.map +1 -1
  94. package/lib/utils/label.js +13 -34
  95. package/lib/utils/label.js.map +1 -1
  96. package/lib/versionInfo.js +4 -4
  97. package/lib/versionInfo.js.map +1 -1
  98. package/lib/widgets/buttons/iconLinks.js +0 -8
  99. package/lib/widgets/buttons/iconLinks.js.map +1 -1
  100. package/lib/widgets/buttons.js +115 -291
  101. package/lib/widgets/buttons.js.map +1 -1
  102. package/lib/widgets/dragAndDrop.js +12 -41
  103. package/lib/widgets/dragAndDrop.js.map +1 -1
  104. package/lib/widgets/error.js +3 -9
  105. package/lib/widgets/error.js.map +1 -1
  106. package/lib/widgets/forms/autocomplete/autocompleteBar.js +8 -71
  107. package/lib/widgets/forms/autocomplete/autocompleteBar.js.map +1 -1
  108. package/lib/widgets/forms/autocomplete/autocompleteField.js +13 -65
  109. package/lib/widgets/forms/autocomplete/autocompleteField.js.map +1 -1
  110. package/lib/widgets/forms/autocomplete/autocompletePicker.js +15 -107
  111. package/lib/widgets/forms/autocomplete/autocompletePicker.js.map +1 -1
  112. package/lib/widgets/forms/autocomplete/language.js +6 -55
  113. package/lib/widgets/forms/autocomplete/language.js.map +1 -1
  114. package/lib/widgets/forms/autocomplete/publicData.js +12 -131
  115. package/lib/widgets/forms/autocomplete/publicData.js.map +1 -1
  116. package/lib/widgets/forms/basic.js +12 -55
  117. package/lib/widgets/forms/basic.js.map +1 -1
  118. package/lib/widgets/forms/comment.js +1 -15
  119. package/lib/widgets/forms/comment.js.map +1 -1
  120. package/lib/widgets/forms/fieldFunction.js +3 -16
  121. package/lib/widgets/forms/fieldFunction.js.map +1 -1
  122. package/lib/widgets/forms/fieldParams.js +0 -12
  123. package/lib/widgets/forms/fieldParams.js.map +1 -1
  124. package/lib/widgets/forms/formStyle.js +1 -14
  125. package/lib/widgets/forms/formStyle.js.map +1 -1
  126. package/lib/widgets/forms.js +70 -474
  127. package/lib/widgets/forms.js.map +1 -1
  128. package/lib/widgets/index.js +0 -21
  129. package/lib/widgets/index.js.map +1 -1
  130. package/lib/widgets/multiSelect.js +48 -171
  131. package/lib/widgets/multiSelect.js.map +1 -1
  132. package/lib/widgets/peoplePicker.js +20 -90
  133. package/lib/widgets/peoplePicker.js.map +1 -1
  134. package/lib/widgets/widgetHelpers.js +2 -9
  135. package/lib/widgets/widgetHelpers.js.map +1 -1
  136. package/package.json +28 -28
package/lib/pad.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
4
  var _typeof = require("@babel/runtime/helpers/typeof");
6
-
7
5
  Object.defineProperty(exports, "__esModule", {
8
6
  value: true
9
7
  });
@@ -36,91 +34,57 @@ Object.defineProperty(exports, "renderPartipants", {
36
34
  }
37
35
  });
38
36
  exports.xmlEncode = xmlEncode;
39
-
40
37
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
41
-
42
38
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
43
-
44
39
  var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
45
-
46
40
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
47
-
48
41
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
49
-
50
42
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
51
-
52
43
  var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
53
-
54
44
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
55
-
56
45
  var ns = _interopRequireWildcard(require("./ns"));
57
-
58
46
  var _rdflib = require("rdflib");
59
-
60
47
  var _widgets = require("./widgets");
61
-
62
48
  var _utils = require("./utils");
63
-
64
49
  var _debug = require("./debug");
65
-
66
50
  var _solidLogic = require("solid-logic");
67
-
68
51
  var _participation = require("./participation");
69
-
70
52
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
71
-
72
53
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
73
-
74
54
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
75
-
76
55
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
77
-
78
56
  var store = _solidLogic.solidLogicSingleton.store;
79
57
  var PAD = (0, _rdflib.Namespace)('http://www.w3.org/ns/pim/pad#');
80
-
81
58
  /**
82
59
  * @ignore
83
60
  */
84
61
  var NotepadElement = /*#__PURE__*/function (_HTMLElement) {
85
62
  (0, _inherits2["default"])(NotepadElement, _HTMLElement);
86
-
87
63
  var _super = _createSuper(NotepadElement);
88
-
89
64
  function NotepadElement() {
90
65
  var _this;
91
-
92
66
  (0, _classCallCheck2["default"])(this, NotepadElement);
93
-
94
67
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
95
68
  args[_key] = arguments[_key];
96
69
  }
97
-
98
70
  _this = _super.call.apply(_super, [this].concat(args));
99
71
  (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "subject", void 0);
100
72
  return _this;
101
73
  }
102
-
103
74
  return (0, _createClass2["default"])(NotepadElement);
104
75
  }( /*#__PURE__*/(0, _wrapNativeSuper2["default"])(HTMLElement));
105
76
  /**
106
77
  * @ignore
107
78
  */
108
-
109
-
110
79
  var NotepadPart = /*#__PURE__*/function (_HTMLElement2) {
111
80
  (0, _inherits2["default"])(NotepadPart, _HTMLElement2);
112
-
113
81
  var _super2 = _createSuper(NotepadPart);
114
-
115
82
  function NotepadPart() {
116
83
  var _this2;
117
-
118
84
  (0, _classCallCheck2["default"])(this, NotepadPart);
119
-
120
85
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
121
86
  args[_key2] = arguments[_key2];
122
87
  }
123
-
124
88
  _this2 = _super2.call.apply(_super2, [this].concat(args));
125
89
  (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this2), "subject", void 0);
126
90
  (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this2), "value", void 0);
@@ -128,7 +92,6 @@ var NotepadPart = /*#__PURE__*/function (_HTMLElement2) {
128
92
  (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this2), "lastSent", void 0);
129
93
  return _this2;
130
94
  }
131
-
132
95
  return (0, _createClass2["default"])(NotepadPart);
133
96
  }( /*#__PURE__*/(0, _wrapNativeSuper2["default"])(HTMLElement));
134
97
  /** Figure out a random color from my webid
@@ -136,8 +99,6 @@ var NotepadPart = /*#__PURE__*/function (_HTMLElement2) {
136
99
  * @param {NamedNode} author - The author of text being displayed
137
100
  * @returns {String} The CSS color generated, constrained to be light for a background color
138
101
  */
139
-
140
-
141
102
  function lightColorHash(author) {
142
103
  var hash = function hash(x) {
143
104
  return x.split('').reduce(function (a, b) {
@@ -145,7 +106,6 @@ function lightColorHash(author) {
145
106
  return a & a;
146
107
  }, 0);
147
108
  };
148
-
149
109
  return author && author.uri ? '#' + (hash(author.uri) & 0xffffff | 0xc0c0c0).toString(16) : '#ffffff'; // c0c0c0 forces pale
150
110
  } // no id -> white
151
111
 
@@ -157,8 +117,6 @@ function lightColorHash(author) {
157
117
  * @param {NamedNode} me - person who is logged into the pod
158
118
  * @param {notepadOptions} options - the options that can be passed in consist of statusArea, exists
159
119
  */
160
-
161
-
162
120
  function notepad(dom, padDoc, subject, me, options) {
163
121
  options = options || {};
164
122
  var exists = options.exists;
@@ -170,17 +128,14 @@ function notepad(dom, padDoc, subject, me, options) {
170
128
  table.setAttribute('style', 'padding: 1em; overflow: auto; resize: horizontal; min-width: 40em;');
171
129
  var upstreamStatus = null;
172
130
  var downstreamStatus = null;
173
-
174
131
  if (options.statusArea) {
175
132
  var t = options.statusArea.appendChild(dom.createElement('table'));
176
133
  var tr = t.appendChild(dom.createElement('tr'));
177
134
  upstreamStatus = tr.appendChild(dom.createElement('td'));
178
135
  downstreamStatus = tr.appendChild(dom.createElement('td'));
179
-
180
136
  if (upstreamStatus) {
181
137
  upstreamStatus.setAttribute('style', 'width:50%');
182
138
  }
183
-
184
139
  if (downstreamStatus) {
185
140
  downstreamStatus.setAttribute('style', 'width:50%');
186
141
  }
@@ -188,25 +143,20 @@ function notepad(dom, padDoc, subject, me, options) {
188
143
  /* @@ TODO want to look into this, it seems upstream should be a boolean and default to false ?
189
144
  *
190
145
  */
191
-
192
-
193
146
  var complain = function complain(message) {
194
147
  var upstream = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
195
148
  (0, _debug.log)(message);
196
-
197
149
  if (options.statusArea) {
198
150
  ;
199
151
  (upstream ? upstreamStatus : downstreamStatus).appendChild((0, _widgets.errorMessageBlock)(dom, message, 'pink'));
200
152
  }
201
- }; // @@ TODO need to refactor so that we don't have to type cast
202
-
203
-
153
+ };
154
+ // @@ TODO need to refactor so that we don't have to type cast
204
155
  var clearStatus = function clearStatus(_upsteam) {
205
156
  if (options.statusArea) {
206
157
  options.statusArea.innerHTML = '';
207
158
  }
208
159
  };
209
-
210
160
  var setPartStyle = function setPartStyle(part, colors, pending) {
211
161
  var chunk = part.subject;
212
162
  colors = colors || '';
@@ -214,61 +164,52 @@ function notepad(dom, padDoc, subject, me, options) {
214
164
  var headingCore = 'font-family: sans-serif; font-weight: bold; border: none;';
215
165
  var headingStyle = ['font-size: 110%; padding-top: 0.5em; padding-bottom: 0.5em; width: 100%;', 'font-size: 120%; padding-top: 1em; padding-bottom: 1em; width: 100%;', 'font-size: 150%; padding-top: 1em; padding-bottom: 1em; width: 100%;'];
216
166
  var author = kb.any(chunk, ns.dc('author'));
217
-
218
167
  if (!colors && author) {
219
168
  // Hash the user webid for now -- later allow user selection!
220
169
  var bgcolor = lightColorHash(author);
221
170
  colors = 'color: ' + (pending ? '#888' : 'black') + '; background-color: ' + bgcolor + ';';
222
- } // @@ TODO Need to research when this can be an object with the indent stored in value
223
- // and when the indent is stored as a Number itself, not in an object.
224
-
171
+ }
225
172
 
173
+ // @@ TODO Need to research when this can be an object with the indent stored in value
174
+ // and when the indent is stored as a Number itself, not in an object.
226
175
  var indent = kb.any(chunk, PAD('indent'));
227
176
  indent = indent ? indent.value : 0;
228
- var style = indent >= 0 ? baseStyle + 'text-indent: ' + indent * 3 + 'em;' : headingCore + headingStyle[-1 - indent]; // ? baseStyle + 'padding-left: ' + (indent * 3) + 'em;'
229
-
177
+ var style = indent >= 0 ? baseStyle + 'text-indent: ' + indent * 3 + 'em;' : headingCore + headingStyle[-1 - indent];
178
+ // ? baseStyle + 'padding-left: ' + (indent * 3) + 'em;'
230
179
  part.setAttribute('style', style + colors);
231
180
  };
232
-
233
181
  var removePart = function removePart(part) {
234
182
  var chunk = part.subject;
235
183
  if (!chunk) throw new Error('No chunk for line to be deleted!'); // just in case
236
-
237
184
  var prev = kb.any(undefined, PAD('next'), chunk);
238
185
  var next = kb.any(chunk, PAD('next'));
239
-
240
186
  if (prev.sameTerm(subject) && next.sameTerm(subject)) {
241
187
  // Last one
242
188
  (0, _debug.log)("You can't delete the only line.");
243
189
  return;
244
190
  }
245
-
246
191
  var del = kb.statementsMatching(chunk, undefined, undefined, padDoc).concat(kb.statementsMatching(undefined, undefined, chunk, padDoc));
247
- var ins = [(0, _rdflib.st)(prev, PAD('next'), next, padDoc)]; // @@ TODO what should we do if chunk is not a NamedNode should we
248
- // assume then it is a string?
192
+ var ins = [(0, _rdflib.st)(prev, PAD('next'), next, padDoc)];
249
193
 
194
+ // @@ TODO what should we do if chunk is not a NamedNode should we
195
+ // assume then it is a string?
250
196
  if (chunk instanceof _rdflib.NamedNode) {
251
197
  var label = chunk.uri.slice(-4);
252
198
  (0, _debug.log)('Deleting line ' + label);
253
199
  }
254
-
255
200
  if (!updater) {
256
201
  throw new Error('have no updater');
257
- } // @@ TODO below you can see that before is redefined and not a boolean
258
-
259
-
202
+ }
203
+ // @@ TODO below you can see that before is redefined and not a boolean
260
204
  updater.update(del, ins, function (uri, ok, errorMessage, response) {
261
205
  if (ok) {
262
206
  var row = part.parentNode;
263
-
264
207
  if (row) {
265
208
  var before = row.previousSibling;
266
-
267
209
  if (row.parentNode) {
268
210
  row.parentNode.removeChild(row);
269
- } // console.log(' deleted line ' + label + ' ok ' + part.value)
270
-
271
-
211
+ }
212
+ // console.log(' deleted line ' + label + ' ok ' + part.value)
272
213
  if (before && before.firstChild) {
273
214
  // @@ TODO IMPORTANT FOCUS ISN'T A PROPERTY ON A CHILDNODE
274
215
  before.firstChild.focus();
@@ -277,11 +218,8 @@ function notepad(dom, padDoc, subject, me, options) {
277
218
  } else if (response && response.status === 409) {
278
219
  // Conflict
279
220
  setPartStyle(part, 'color: black; background-color: #ffd;'); // yellow
280
-
281
221
  part.state = 0; // Needs downstream refresh
282
-
283
222
  (0, _utils.beep)(0.5, 512); // Ooops clash with other person
284
-
285
223
  setTimeout(function () {
286
224
  // Ideally, beep! @@
287
225
  reloadAndSync(); // Throw away our changes and
@@ -291,7 +229,6 @@ function notepad(dom, padDoc, subject, me, options) {
291
229
  (0, _debug.log)(' removePart FAILED ' + chunk + ': ' + errorMessage);
292
230
  (0, _debug.log)(" removePart was deleteing :'" + del);
293
231
  setPartStyle(part, 'color: black; background-color: #fdd;'); // failed
294
-
295
232
  var res = response ? response.status : ' [no response field] ';
296
233
  complain('Error ' + res + ' saving changes: ' + errorMessage["true"]); // upstream,
297
234
  // updater.requestDownstreamAction(padDoc, reloadAndSync);
@@ -299,31 +236,27 @@ function notepad(dom, padDoc, subject, me, options) {
299
236
  });
300
237
  }; // removePart
301
238
 
302
-
303
239
  var changeIndent = function changeIndent(part, chunk, delta) {
304
240
  var del = kb.statementsMatching(chunk, PAD('indent'));
305
241
  var current = del.length ? Number(del[0].object.value) : 0;
306
242
  if (current + delta < -3) return; // limit negative indent
307
-
308
243
  var newIndent = current + delta;
309
244
  var ins = (0, _rdflib.st)(chunk, PAD('indent'), newIndent, padDoc);
310
-
311
245
  if (!updater) {
312
246
  throw new Error('no updater');
313
247
  }
314
-
315
248
  updater.update(del, ins, function (uri, ok, errorBody) {
316
249
  if (!ok) {
317
250
  (0, _debug.log)("Indent change FAILED '" + newIndent + "' for " + padDoc + ': ' + errorBody);
318
251
  setPartStyle(part, 'color: black; background-color: #fdd;'); // failed
319
-
320
252
  updater.requestDownstreamAction(padDoc, reloadAndSync);
321
253
  } else {
322
254
  setPartStyle(part); // Implement the indent
323
255
  }
324
256
  });
325
- }; // Use this sort of code to split the line when return pressed in the middle @@
257
+ };
326
258
 
259
+ // Use this sort of code to split the line when return pressed in the middle @@
327
260
  /*
328
261
  function doGetCaretPosition doGetCaretPosition (oField) {
329
262
  var iCaretPos = 0
@@ -345,23 +278,19 @@ function notepad(dom, padDoc, subject, me, options) {
345
278
  return (iCaretPos)
346
279
  }
347
280
  */
348
-
349
-
350
281
  var addListeners = function addListeners(part, chunk) {
351
282
  part.addEventListener('keydown', function (event) {
352
283
  if (!updater) {
353
284
  throw new Error('no updater');
354
285
  }
355
-
356
- var queueProperty, queue; // up 38; down 40; left 37; right 39 tab 9; shift 16; escape 27
357
-
286
+ var queueProperty, queue;
287
+ // up 38; down 40; left 37; right 39 tab 9; shift 16; escape 27
358
288
  switch (event.keyCode) {
359
289
  case 13:
360
290
  // Return
361
291
  {
362
292
  var before = event.shiftKey;
363
293
  (0, _debug.log)('enter'); // Shift-return inserts before -- only way to add to top of pad.
364
-
365
294
  if (before) {
366
295
  queue = kb.any(undefined, PAD('next'), chunk);
367
296
  queueProperty = 'newlinesAfter';
@@ -369,116 +298,92 @@ function notepad(dom, padDoc, subject, me, options) {
369
298
  queue = kb.any(chunk, PAD('next'));
370
299
  queueProperty = 'newlinesBefore';
371
300
  }
372
-
373
301
  queue[queueProperty] = queue[queueProperty] || 0;
374
302
  queue[queueProperty] += 1;
375
-
376
303
  if (queue[queueProperty] > 1) {
377
304
  (0, _debug.log)(' queueing newline queue = ' + queue[queueProperty]);
378
305
  return;
379
306
  }
380
-
381
307
  (0, _debug.log)(' go ahead line before ' + queue[queueProperty]);
382
308
  newChunk(part, before); // was document.activeElement
383
-
384
309
  break;
385
310
  }
386
-
387
311
  case 8:
388
312
  // Delete
389
313
  if (part.value.length === 0) {
390
314
  (0, _debug.log)('Delete key line ' + chunk.uri.slice(-4) + ' state ' + part.state);
391
-
392
315
  switch (part.state) {
393
316
  case 1: // contents being sent
394
-
395
317
  case 2:
396
318
  // contents need to be sent again
397
319
  part.state = 4; // delete me
398
-
399
320
  return;
400
-
401
321
  case 3: // being deleted already
402
-
403
322
  case 4:
404
323
  // already deleme state
405
324
  return;
406
-
407
325
  case undefined:
408
326
  case 0:
409
327
  part.state = 3; // being deleted
410
-
411
328
  removePart(part);
412
329
  event.preventDefault();
413
330
  break;
414
331
  // continue
415
-
416
332
  default:
417
333
  throw new Error('pad: Unexpected state ' + part);
418
334
  }
419
335
  }
420
-
421
336
  break;
422
-
423
337
  case 9:
424
338
  // Tab
425
339
  {
426
340
  var delta = event.shiftKey ? -1 : 1;
427
341
  changeIndent(part, chunk, delta);
428
342
  event.preventDefault(); // default is to highlight next field
429
-
430
343
  break;
431
344
  }
432
-
433
345
  case 27:
434
346
  // ESC
435
347
  (0, _debug.log)('escape');
436
348
  updater.requestDownstreamAction(padDoc, reloadAndSync);
437
349
  event.preventDefault();
438
350
  break;
439
-
440
351
  case 38:
441
352
  // Up
442
353
  if (part.parentNode.previousSibling) {
443
354
  part.parentNode.previousSibling.firstChild.focus();
444
355
  event.preventDefault();
445
356
  }
446
-
447
357
  break;
448
-
449
358
  case 40:
450
359
  // Down
451
360
  if (part.parentNode.nextSibling) {
452
361
  part.parentNode.nextSibling.firstChild.focus();
453
362
  event.preventDefault();
454
363
  }
455
-
456
364
  break;
457
-
458
365
  default:
459
366
  }
460
367
  });
461
-
462
368
  var updateStore = function updateStore(part) {
463
369
  var chunk = part.subject;
464
370
  setPartStyle(part, undefined, true);
465
371
  var old = kb.any(chunk, ns.sioc('content')).value;
466
372
  var del = [(0, _rdflib.st)(chunk, ns.sioc('content'), old, padDoc)];
467
373
  var ins;
468
-
469
374
  if (part.value) {
470
375
  ins = [(0, _rdflib.st)(chunk, ns.sioc('content'), part.value, padDoc)];
471
376
  }
377
+ var newOne = part.value;
472
378
 
473
- var newOne = part.value; // DEBUGGING ONLY
474
-
379
+ // DEBUGGING ONLY
475
380
  if (part.lastSent) {
476
381
  if (old !== part.lastSent) {
477
382
  throw new Error("Out of order, last sent expected '" + old + "' but found '" + part.lastSent + "'");
478
383
  }
479
384
  }
480
-
481
385
  part.lastSent = newOne;
386
+
482
387
  /* console.log(
483
388
  ' Patch proposed to ' +
484
389
  chunk.uri.slice(-4) +
@@ -488,29 +393,23 @@ function notepad(dom, padDoc, subject, me, options) {
488
393
  newOne +
489
394
  "' "
490
395
  ) */
491
-
492
396
  if (!updater) {
493
397
  throw new Error('no updater');
494
398
  }
495
-
496
399
  updater.update(del, ins, function (uri, ok, errorBody, xhr) {
497
400
  if (!ok) {
498
401
  // alert("clash " + errorBody);
499
402
  (0, _debug.log)(' patch FAILED ' + xhr.status + " for '" + old + "' -> '" + newOne + "': " + errorBody);
500
-
501
403
  if (xhr.status === 409) {
502
404
  // Conflict - @@ we assume someone else
503
405
  setPartStyle(part, 'color: black; background-color: #fdd;');
504
406
  part.state = 0; // Needs downstream refresh
505
-
506
407
  (0, _utils.beep)(0.5, 512); // Ooops clash with other person
507
-
508
408
  setTimeout(function () {
509
409
  updater.requestDownstreamAction(padDoc, reloadAndSync);
510
410
  }, 1000);
511
411
  } else {
512
412
  setPartStyle(part, 'color: black; background-color: #fdd;'); // failed pink
513
-
514
413
  part.state = 0;
515
414
  complain(' Error ' + xhr.status + ' sending data: ' + errorBody, true);
516
415
  (0, _utils.beep)(1.0, 128); // Other
@@ -518,20 +417,17 @@ function notepad(dom, padDoc, subject, me, options) {
518
417
  }
519
418
  } else {
520
419
  clearStatus(true); // upstream
521
-
522
420
  setPartStyle(part); // synced
523
-
524
421
  (0, _debug.log)(" Patch ok '" + old + "' -> '" + newOne + "' ");
525
-
526
422
  if (part.state === 4) {
527
423
  // delete me
528
424
  part.state = 3;
529
425
  removePart(part);
530
- } else if (part.state === 3) {// being deleted
426
+ } else if (part.state === 3) {
427
+ // being deleted
531
428
  // pass
532
429
  } else if (part.state === 2) {
533
430
  part.state = 1; // pending: lock
534
-
535
431
  updateStore(part);
536
432
  } else {
537
433
  part.state = 0; // clear lock
@@ -543,44 +439,34 @@ function notepad(dom, padDoc, subject, me, options) {
543
439
  part.addEventListener('input', function inputChangeListener(_event) {
544
440
  // debug.log("input changed "+part.value);
545
441
  setPartStyle(part, undefined, true); // grey out - not synced
546
-
547
442
  (0, _debug.log)('Input event state ' + part.state + " value '" + part.value + "'");
548
-
549
443
  switch (part.state) {
550
444
  case 3:
551
445
  // being deleted
552
446
  return;
553
-
554
447
  case 4:
555
448
  // needs to be deleted
556
449
  return;
557
-
558
450
  case 2:
559
451
  // needs content updating, we know
560
452
  return;
561
-
562
453
  case 1:
563
454
  part.state = 2; // lag we need another patch
564
-
565
455
  return;
566
-
567
456
  case 0:
568
457
  case undefined:
569
458
  part.state = 1; // being upadted
570
-
571
459
  updateStore(part);
572
460
  }
573
461
  }); // listener
574
462
  }; // addlisteners
575
- // @@ TODO Need to research before as it appears to be used as an Element and a boolean
576
-
577
463
 
464
+ // @@ TODO Need to research before as it appears to be used as an Element and a boolean
578
465
  var newPartAfter = function newPartAfter(tr1, chunk, before) {
579
466
  // @@ take chunk and add listeners
580
467
  var text = kb.any(chunk, ns.sioc('content'));
581
468
  text = text ? text.value : '';
582
469
  var tr = dom.createElement('tr');
583
-
584
470
  if (before) {
585
471
  table.insertBefore(tr, tr1);
586
472
  } else {
@@ -591,12 +477,10 @@ function notepad(dom, padDoc, subject, me, options) {
591
477
  table.appendChild(tr);
592
478
  }
593
479
  }
594
-
595
480
  var part = tr.appendChild(dom.createElement('input'));
596
481
  part.subject = chunk;
597
482
  part.setAttribute('type', 'text');
598
483
  part.value = text;
599
-
600
484
  if (me) {
601
485
  setPartStyle(part, '');
602
486
  addListeners(part, chunk);
@@ -604,29 +488,24 @@ function notepad(dom, padDoc, subject, me, options) {
604
488
  setPartStyle(part, 'color: #222; background-color: #fff');
605
489
  (0, _debug.log)("Note can't add listeners - not logged in");
606
490
  }
607
-
608
491
  return part;
609
492
  };
493
+
610
494
  /* @@ TODO we need to look at indent, it can be a Number or an Object this doesn't seem correct.
611
495
  */
612
-
613
-
614
496
  var newChunk = function newChunk(ele, before) {
615
497
  // element of chunk being split
616
498
  var kb = store;
617
499
  var indent = 0;
618
500
  var queueProperty = null;
619
501
  var here, prev, next, queue, tr1;
620
-
621
502
  if (ele) {
622
503
  if (ele.tagName.toLowerCase() !== 'input') {
623
504
  (0, _debug.log)('return pressed when current document is: ' + ele.tagName);
624
505
  }
625
-
626
506
  here = ele.subject;
627
507
  indent = kb.any(here, PAD('indent'));
628
508
  indent = indent ? Number(indent.value) : 0;
629
-
630
509
  if (before) {
631
510
  prev = kb.any(undefined, PAD('next'), here);
632
511
  next = here;
@@ -638,30 +517,24 @@ function notepad(dom, padDoc, subject, me, options) {
638
517
  queue = next;
639
518
  queueProperty = 'newlinesBefore';
640
519
  }
641
-
642
520
  tr1 = ele.parentNode;
643
521
  } else {
644
522
  prev = subject;
645
523
  next = subject;
646
524
  tr1 = undefined;
647
525
  }
648
-
649
526
  var chunk = (0, _widgets.newThing)(padDoc);
650
527
  var label = chunk.uri.slice(-4);
651
528
  var del = [(0, _rdflib.st)(prev, PAD('next'), next, padDoc)];
652
529
  var ins = [(0, _rdflib.st)(prev, PAD('next'), chunk, padDoc), (0, _rdflib.st)(chunk, PAD('next'), next, padDoc), (0, _rdflib.st)(chunk, ns.dc('author'), me, padDoc), (0, _rdflib.st)(chunk, ns.sioc('content'), '', padDoc)];
653
-
654
530
  if (indent > 0) {
655
531
  // Do not inherit
656
532
  ins.push((0, _rdflib.st)(chunk, PAD('indent'), indent, padDoc));
657
533
  }
658
-
659
534
  (0, _debug.log)(' Fresh chunk ' + label + ' proposed');
660
-
661
535
  if (!updater) {
662
536
  throw new Error('no updater');
663
537
  }
664
-
665
538
  updater.update(del, ins, function (uri, ok, errorBody, _xhr) {
666
539
  if (!ok) {
667
540
  // alert("Error writing new line " + label + ": " + errorBody);
@@ -670,11 +543,9 @@ function notepad(dom, padDoc, subject, me, options) {
670
543
  var newPart = newPartAfter(tr1, chunk, before);
671
544
  setPartStyle(newPart);
672
545
  newPart.focus(); // Note this is delayed
673
-
674
546
  if (queueProperty) {
675
547
  (0, _debug.log)(' Fresh chunk ' + label + ' updated, queue = ' + queue[queueProperty]);
676
548
  queue[queueProperty] -= 1;
677
-
678
549
  if (queue[queueProperty] > 0) {
679
550
  (0, _debug.log)(' Implementing queued newlines = ' + next.newLinesBefore);
680
551
  newChunk(newPart, before);
@@ -683,69 +554,51 @@ function notepad(dom, padDoc, subject, me, options) {
683
554
  }
684
555
  });
685
556
  };
686
-
687
557
  var consistencyCheck = function consistencyCheck() {
688
558
  var found = {};
689
559
  var failed = 0;
690
-
691
560
  function complain2(msg) {
692
561
  complain(msg);
693
562
  failed++;
694
563
  }
695
-
696
564
  if (!kb.the(subject, PAD('next'))) {
697
565
  complain2('No initial next pointer');
698
566
  return false; // can't do linked list
699
- } // var chunk = kb.the(subject, PAD('next'))
700
-
701
-
567
+ }
568
+ // var chunk = kb.the(subject, PAD('next'))
702
569
  var prev = subject;
703
570
  var chunk;
704
-
705
571
  for (;;) {
706
572
  chunk = kb.the(prev, PAD('next'));
707
-
708
573
  if (!chunk) {
709
574
  complain2('No next pointer from ' + prev);
710
575
  }
711
-
712
576
  if (chunk.sameTerm(subject)) {
713
577
  break;
714
578
  }
715
-
716
579
  prev = chunk;
717
580
  var label = chunk.uri.split('#')[1];
718
-
719
581
  if (found[chunk.uri]) {
720
582
  complain2('Loop!');
721
583
  return false;
722
584
  }
723
-
724
585
  found[chunk.uri] = true;
725
586
  var k = kb.each(chunk, PAD('next')).length;
726
-
727
587
  if (k !== 1) {
728
588
  complain2('Should be 1 not ' + k + ' next pointer for ' + label);
729
589
  }
730
-
731
590
  k = kb.each(chunk, PAD('indent')).length;
732
-
733
591
  if (k > 1) {
734
592
  complain2('Should be 0 or 1 not ' + k + ' indent for ' + label);
735
593
  }
736
-
737
594
  k = kb.each(chunk, ns.sioc('content')).length;
738
-
739
595
  if (k !== 1) {
740
596
  complain2('Should be 1 not ' + k + ' contents for ' + label);
741
597
  }
742
-
743
598
  k = kb.each(chunk, ns.dc('author')).length;
744
-
745
599
  if (k !== 1) {
746
600
  complain2('Should be 1 not ' + k + ' author for ' + label);
747
601
  }
748
-
749
602
  var sts = kb.statementsMatching(undefined, ns.sioc('contents'));
750
603
  sts.forEach(function (st) {
751
604
  if (!found[st.subject.value]) {
@@ -753,103 +606,89 @@ function notepad(dom, padDoc, subject, me, options) {
753
606
  }
754
607
  });
755
608
  }
756
-
757
609
  return !failed;
758
- }; // Ensure that the display matches the current state of the
759
- // @@ TODO really need to refactor this so that we don't need to cast types
760
-
610
+ };
761
611
 
612
+ // Ensure that the display matches the current state of the
613
+ // @@ TODO really need to refactor this so that we don't need to cast types
762
614
  var sync = function sync() {
763
615
  // var first = kb.the(subject, PAD('next'))
764
616
  if (kb.each(subject, PAD('next')).length !== 1) {
765
617
  var msg = 'Pad: Inconsistent data - NEXT pointers: ' + kb.each(subject, PAD('next')).length;
766
618
  (0, _debug.log)(msg);
767
-
768
619
  if (options.statusArea) {
769
620
  options.statusArea.textContent += msg;
770
621
  }
771
-
772
622
  return;
773
- } // var last = kb.the(undefined, PAD('previous'), subject)
623
+ }
624
+ // var last = kb.the(undefined, PAD('previous'), subject)
774
625
  // var chunk = first // = kb.the(subject, PAD('next'));
626
+ var row;
775
627
 
776
-
777
- var row; // First see which of the logical chunks have existing physical manifestations
778
-
779
- var manif = []; // Find which lines correspond to existing chunks
628
+ // First see which of the logical chunks have existing physical manifestations
629
+ var manif = [];
630
+ // Find which lines correspond to existing chunks
780
631
 
781
632
  for (var chunk = kb.the(subject, PAD('next')); !chunk.sameTerm(subject); chunk = kb.the(chunk, PAD('next'))) {
782
633
  for (var i = 0; i < table.children.length; i++) {
783
634
  var _tr = table.children[i];
784
-
785
635
  if (_tr.firstChild) {
786
636
  if (_tr.firstChild.subject.sameTerm(chunk)) {
787
637
  manif[chunk.uri] = _tr.firstChild;
788
638
  }
789
639
  }
790
640
  }
791
- } // Remove any deleted lines
792
-
641
+ }
793
642
 
643
+ // Remove any deleted lines
794
644
  for (var _i = table.children.length - 1; _i >= 0; _i--) {
795
645
  row = table.children[_i];
796
-
797
646
  if (!manif[row.firstChild.subject.uri]) {
798
647
  table.removeChild(row);
799
648
  }
800
- } // Insert any new lines and update old ones
801
-
802
-
649
+ }
650
+ // Insert any new lines and update old ones
803
651
  row = table.firstChild; // might be null
804
-
805
652
  for (var _chunk = kb.the(subject, PAD('next')); !_chunk.sameTerm(subject); _chunk = kb.the(_chunk, PAD('next'))) {
806
- var text = kb.any(_chunk, ns.sioc('content')).value; // superstitious -- don't mess with unchanged input fields
653
+ var text = kb.any(_chunk, ns.sioc('content')).value;
654
+ // superstitious -- don't mess with unchanged input fields
807
655
  // which may be selected by the user
808
-
809
656
  if (row && manif[_chunk.uri]) {
810
657
  var part = row.firstChild;
811
-
812
658
  if (text !== part.value) {
813
659
  part.value = text;
814
660
  }
815
-
816
661
  setPartStyle(part);
817
662
  part.state = 0; // Clear the state machine
818
-
819
663
  delete part.lastSent; // DEBUG ONLY
820
-
821
664
  row = row.nextSibling;
822
665
  } else {
823
666
  newPartAfter(row, _chunk, true); // actually before
824
667
  }
825
668
  }
826
- }; // Refresh the DOM tree
669
+ };
827
670
 
671
+ // Refresh the DOM tree
828
672
 
829
673
  var refreshTree = function refreshTree(root) {
830
674
  if (root.refresh) {
831
675
  root.refresh();
832
676
  return;
833
677
  }
834
-
835
678
  for (var i = 0; i < root.children.length; i++) {
836
679
  refreshTree(root.children[i]);
837
680
  }
838
681
  };
839
-
840
682
  var reloading = false;
841
-
842
683
  var checkAndSync = function checkAndSync() {
843
684
  (0, _debug.log)(' reloaded OK');
844
685
  clearStatus();
845
-
846
686
  if (!consistencyCheck()) {
847
687
  complain('CONSITENCY CHECK FAILED');
848
688
  } else {
849
689
  refreshTree(table);
850
690
  }
851
691
  };
852
-
853
692
  var reloadAndSync = function reloadAndSync() {
854
693
  if (reloading) {
855
694
  (0, _debug.log)(' Already reloading - stop');
@@ -858,17 +697,13 @@ function notepad(dom, padDoc, subject, me, options) {
858
697
 
859
698
  reloading = true;
860
699
  var retryTimeout = 1000; // ms
861
-
862
700
  var tryReload = function tryReload() {
863
701
  (0, _debug.log)('try reload - timeout = ' + retryTimeout);
864
-
865
702
  if (!updater) {
866
703
  throw new Error('no updater');
867
704
  }
868
-
869
705
  updater.reload(updater.store, padDoc, function (ok, message, xhr) {
870
706
  reloading = false;
871
-
872
707
  if (ok) {
873
708
  checkAndSync();
874
709
  } else {
@@ -883,21 +718,15 @@ function notepad(dom, padDoc, subject, me, options) {
883
718
  }
884
719
  });
885
720
  };
886
-
887
721
  tryReload();
888
722
  };
889
-
890
723
  table.refresh = sync; // Catch downward propagating refresh events
891
-
892
724
  table.reloadAndSync = reloadAndSync;
893
725
  if (!me) (0, _debug.log)('Warning: must be logged in for pad to be edited');
894
-
895
726
  if (exists) {
896
727
  (0, _debug.log)('Existing pad.');
897
-
898
728
  if (consistencyCheck()) {
899
729
  sync();
900
-
901
730
  if (kb.holds(subject, PAD('next'), subject)) {
902
731
  // Empty list untenable
903
732
  newChunk(); // require at least one line
@@ -909,11 +738,9 @@ function notepad(dom, padDoc, subject, me, options) {
909
738
  // Make new pad
910
739
  (0, _debug.log)('No pad exists - making new one.');
911
740
  var insertables = [(0, _rdflib.st)(subject, ns.rdf('type'), PAD('Notepad'), padDoc), (0, _rdflib.st)(subject, ns.dc('author'), me, padDoc), (0, _rdflib.st)(subject, ns.dc('created'), new Date(), padDoc), (0, _rdflib.st)(subject, PAD('next'), subject, padDoc)];
912
-
913
741
  if (!updater) {
914
742
  throw new Error('no updater');
915
743
  }
916
-
917
744
  updater.update([], insertables, function (uri, ok, errorBody) {
918
745
  if (!ok) {
919
746
  complain(errorBody || '');
@@ -927,74 +754,62 @@ function notepad(dom, padDoc, subject, me, options) {
927
754
 
928
755
  return table;
929
756
  }
757
+
930
758
  /**
931
759
  * Get the chunks of the notepad
932
760
  * They are stored in a RDF linked list
933
761
  */
934
- // @ignore exporting this only for the unit test
935
-
936
762
 
763
+ // @ignore exporting this only for the unit test
937
764
  function getChunks(subject, kb) {
938
765
  var chunks = [];
939
-
940
766
  for (var chunk = kb.the(subject, PAD('next')); !chunk.sameTerm(subject); chunk = kb.the(chunk, PAD('next'))) {
941
767
  chunks.push(chunk);
942
768
  }
943
-
944
769
  return chunks;
945
770
  }
771
+
946
772
  /**
947
773
  * Encode content to be put in XML or HTML elements
948
774
  */
949
775
  // @ignore exporting this only for the unit test
950
-
951
-
952
776
  function xmlEncode(str) {
953
777
  return str.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;');
954
778
  }
779
+
955
780
  /**
956
781
  * Convert a notepad to HTML
957
782
  * @param { } pad - the notepad
958
783
  * @param {store} pad - the data store
959
784
  */
960
-
961
-
962
785
  function notepadToHTML(pad, kb) {
963
786
  var chunks = getChunks(pad, kb);
964
787
  var html = '<html>\n <head>\n';
965
788
  var title = kb.anyValue(pad, ns.dct('title'));
966
-
967
789
  if (title) {
968
790
  html += " <title>".concat(xmlEncode(title), "</title>\n");
969
791
  }
970
-
971
792
  html += ' </head>\n <body>\n';
972
793
  var level = 0;
973
-
974
794
  function increaseLevel(indent) {
975
795
  for (; level < indent; level++) {
976
796
  html += '<ul>\n';
977
797
  }
978
798
  }
979
-
980
799
  function decreaseLevel(indent) {
981
800
  for (; level > indent; level--) {
982
801
  html += '</ul>\n';
983
802
  }
984
803
  }
985
-
986
804
  chunks.forEach(function (chunk) {
987
805
  var indent = kb.anyJS(chunk, PAD('indent'));
988
806
  var rawContent = kb.anyJS(chunk, ns.sioc('content'));
989
807
  if (!rawContent) return; // seed chunk is dummy
990
-
991
808
  var content = xmlEncode(rawContent);
992
-
993
809
  if (indent < 0) {
994
810
  // negative indent levels represent heading levels
995
811
  decreaseLevel(0);
996
812
  var h = indent >= -3 ? 4 + indent : 1; // -1 -> h4, -2 -> h3
997
-
998
813
  html += "\n<h".concat(h, ">").concat(content, "</h").concat(h, ">\n");
999
814
  } else {
1000
815
  // >= 0
@@ -1011,7 +826,6 @@ function notepadToHTML(pad, kb) {
1011
826
  }
1012
827
  }); // foreach chunk
1013
828
  // At the end decreaseLevel any open ULs
1014
-
1015
829
  decreaseLevel(0);
1016
830
  html += ' </body>\n</html>\n';
1017
831
  return html;