@shaxpir/duiduidui-models 1.0.0

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 (211) hide show
  1. package/README.md +1 -0
  2. package/decs.d.ts +87 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +20 -0
  5. package/dist/models/OutboundMessage.d.ts +18 -0
  6. package/dist/models/OutboundMessage.js +25 -0
  7. package/dist/models/content/Activity.d.ts +10 -0
  8. package/dist/models/content/Activity.js +2 -0
  9. package/dist/models/content/ArrayView.d.ts +26 -0
  10. package/dist/models/content/ArrayView.js +174 -0
  11. package/dist/models/content/Billing.d.ts +144 -0
  12. package/dist/models/content/Billing.js +418 -0
  13. package/dist/models/content/Book.d.ts +77 -0
  14. package/dist/models/content/Book.js +407 -0
  15. package/dist/models/content/Category.d.ts +16 -0
  16. package/dist/models/content/Category.js +20 -0
  17. package/dist/models/content/Checkpointable.d.ts +21 -0
  18. package/dist/models/content/Checkpointable.js +156 -0
  19. package/dist/models/content/Comment.d.ts +19 -0
  20. package/dist/models/content/Comment.js +53 -0
  21. package/dist/models/content/ConceptArt.d.ts +31 -0
  22. package/dist/models/content/ConceptArt.js +84 -0
  23. package/dist/models/content/Content.d.ts +52 -0
  24. package/dist/models/content/Content.js +61 -0
  25. package/dist/models/content/ContentKind.d.ts +10 -0
  26. package/dist/models/content/ContentKind.js +16 -0
  27. package/dist/models/content/Context.d.ts +28 -0
  28. package/dist/models/content/Context.js +162 -0
  29. package/dist/models/content/DevEnv.d.ts +5 -0
  30. package/dist/models/content/DevEnv.js +9 -0
  31. package/dist/models/content/Device.d.ts +24 -0
  32. package/dist/models/content/Device.js +62 -0
  33. package/dist/models/content/Dictionary.d.ts +31 -0
  34. package/dist/models/content/Dictionary.js +5 -0
  35. package/dist/models/content/DictionaryEntry.d.ts +20 -0
  36. package/dist/models/content/DictionaryEntry.js +2 -0
  37. package/dist/models/content/ElasticModel.d.ts +149 -0
  38. package/dist/models/content/ElasticModel.js +179 -0
  39. package/dist/models/content/Environment.d.ts +61 -0
  40. package/dist/models/content/Environment.js +124 -0
  41. package/dist/models/content/ExportOptions.d.ts +64 -0
  42. package/dist/models/content/ExportOptions.js +213 -0
  43. package/dist/models/content/Folder.d.ts +16 -0
  44. package/dist/models/content/Folder.js +33 -0
  45. package/dist/models/content/Fragment.d.ts +54 -0
  46. package/dist/models/content/Fragment.js +181 -0
  47. package/dist/models/content/GeoLocation.d.ts +4 -0
  48. package/dist/models/content/GeoLocation.js +2 -0
  49. package/dist/models/content/Hanzi.d.ts +21 -0
  50. package/dist/models/content/Hanzi.js +2 -0
  51. package/dist/models/content/HighlightRule.d.ts +9 -0
  52. package/dist/models/content/HighlightRule.js +2 -0
  53. package/dist/models/content/Manifest.d.ts +42 -0
  54. package/dist/models/content/Manifest.js +114 -0
  55. package/dist/models/content/Media.d.ts +32 -0
  56. package/dist/models/content/Media.js +98 -0
  57. package/dist/models/content/Metric.d.ts +46 -0
  58. package/dist/models/content/Metric.js +183 -0
  59. package/dist/models/content/Migration.d.ts +68 -0
  60. package/dist/models/content/Migration.js +155 -0
  61. package/dist/models/content/Model.d.ts +45 -0
  62. package/dist/models/content/Model.js +280 -0
  63. package/dist/models/content/Permissions.d.ts +7 -0
  64. package/dist/models/content/Permissions.js +20 -0
  65. package/dist/models/content/Phrase.d.ts +8 -0
  66. package/dist/models/content/Phrase.js +2 -0
  67. package/dist/models/content/Placeholder.d.ts +8 -0
  68. package/dist/models/content/Placeholder.js +36 -0
  69. package/dist/models/content/Profile.d.ts +30 -0
  70. package/dist/models/content/Profile.js +95 -0
  71. package/dist/models/content/RichText.d.ts +58 -0
  72. package/dist/models/content/RichText.js +79 -0
  73. package/dist/models/content/Session.d.ts +39 -0
  74. package/dist/models/content/Session.js +173 -0
  75. package/dist/models/content/Speech.d.ts +67 -0
  76. package/dist/models/content/Speech.js +97 -0
  77. package/dist/models/content/Stub.d.ts +24 -0
  78. package/dist/models/content/Stub.js +179 -0
  79. package/dist/models/content/Time.d.ts +56 -0
  80. package/dist/models/content/Time.js +295 -0
  81. package/dist/models/content/User.d.ts +36 -0
  82. package/dist/models/content/User.js +95 -0
  83. package/dist/models/content/Workspace.d.ts +71 -0
  84. package/dist/models/content/Workspace.js +237 -0
  85. package/dist/models/content/index.d.ts +36 -0
  86. package/dist/models/content/index.js +53 -0
  87. package/dist/models/index.d.ts +4 -0
  88. package/dist/models/index.js +21 -0
  89. package/dist/models/legacy/LegacyBodyFormat.d.ts +9 -0
  90. package/dist/models/legacy/LegacyBodyFormat.js +2 -0
  91. package/dist/models/legacy/LegacyComment.d.ts +12 -0
  92. package/dist/models/legacy/LegacyComment.js +2 -0
  93. package/dist/models/legacy/LegacyContent.d.ts +53 -0
  94. package/dist/models/legacy/LegacyContent.js +55 -0
  95. package/dist/models/legacy/LegacyConversion.d.ts +55 -0
  96. package/dist/models/legacy/LegacyConversion.js +401 -0
  97. package/dist/models/legacy/LegacyFragment.d.ts +21 -0
  98. package/dist/models/legacy/LegacyFragment.js +2 -0
  99. package/dist/models/legacy/LegacyLocator.d.ts +8 -0
  100. package/dist/models/legacy/LegacyLocator.js +31 -0
  101. package/dist/models/legacy/LegacyOutboundMessage.d.ts +16 -0
  102. package/dist/models/legacy/LegacyOutboundMessage.js +13 -0
  103. package/dist/models/legacy/LegacyPicture.d.ts +14 -0
  104. package/dist/models/legacy/LegacyPicture.js +2 -0
  105. package/dist/models/legacy/LegacyProfile.d.ts +9 -0
  106. package/dist/models/legacy/LegacyProfile.js +2 -0
  107. package/dist/models/legacy/LegacySession.d.ts +41 -0
  108. package/dist/models/legacy/LegacySession.js +35 -0
  109. package/dist/models/legacy/LegacyStory.d.ts +23 -0
  110. package/dist/models/legacy/LegacyStory.js +2 -0
  111. package/dist/models/legacy/LegacyStub.d.ts +15 -0
  112. package/dist/models/legacy/LegacyStub.js +2 -0
  113. package/dist/models/legacy/LegacyTransaction.d.ts +14 -0
  114. package/dist/models/legacy/LegacyTransaction.js +49 -0
  115. package/dist/models/legacy/LegacyUser.d.ts +28 -0
  116. package/dist/models/legacy/LegacyUser.js +32 -0
  117. package/dist/models/legacy/LegacyWorkspace.d.ts +23 -0
  118. package/dist/models/legacy/LegacyWorkspace.js +6 -0
  119. package/dist/models/legacy/index.d.ts +15 -0
  120. package/dist/models/legacy/index.js +32 -0
  121. package/dist/models/markup/BodyFormat.d.ts +14 -0
  122. package/dist/models/markup/BodyFormat.js +190 -0
  123. package/dist/models/markup/ChangeModel.d.ts +22 -0
  124. package/dist/models/markup/ChangeModel.js +107 -0
  125. package/dist/models/markup/DeltaOps.d.ts +5 -0
  126. package/dist/models/markup/DeltaOps.js +74 -0
  127. package/dist/models/markup/HtmlMarkup.d.ts +4 -0
  128. package/dist/models/markup/HtmlMarkup.js +21 -0
  129. package/dist/models/markup/Operation.d.ts +32 -0
  130. package/dist/models/markup/Operation.js +194 -0
  131. package/dist/models/markup/TextEditOps.d.ts +9 -0
  132. package/dist/models/markup/TextEditOps.js +50 -0
  133. package/dist/models/markup/index.d.ts +6 -0
  134. package/dist/models/markup/index.js +23 -0
  135. package/dist/repo/ConnectionListener.d.ts +9 -0
  136. package/dist/repo/ConnectionListener.js +21 -0
  137. package/dist/repo/PermissiveJson1.d.ts +58 -0
  138. package/dist/repo/PermissiveJson1.js +39 -0
  139. package/dist/repo/ShareSync.d.ts +60 -0
  140. package/dist/repo/ShareSync.js +348 -0
  141. package/dist/repo/index.d.ts +3 -0
  142. package/dist/repo/index.js +20 -0
  143. package/dist/util/Async.d.ts +8 -0
  144. package/dist/util/Async.js +18 -0
  145. package/dist/util/Base62.d.ts +6 -0
  146. package/dist/util/Base62.js +47 -0
  147. package/dist/util/BinarySearch.d.ts +7 -0
  148. package/dist/util/BinarySearch.js +46 -0
  149. package/dist/util/CachingHasher.d.ts +8 -0
  150. package/dist/util/CachingHasher.js +41 -0
  151. package/dist/util/Color.d.ts +32 -0
  152. package/dist/util/Color.js +204 -0
  153. package/dist/util/Dispatch.d.ts +15 -0
  154. package/dist/util/Dispatch.js +79 -0
  155. package/dist/util/EditDistance.d.ts +13 -0
  156. package/dist/util/EditDistance.js +184 -0
  157. package/dist/util/Encryption.d.ts +5 -0
  158. package/dist/util/Encryption.js +2 -0
  159. package/dist/util/Logging.d.ts +108 -0
  160. package/dist/util/Logging.js +412 -0
  161. package/dist/util/NumberFormat.d.ts +14 -0
  162. package/dist/util/NumberFormat.js +224 -0
  163. package/dist/util/Struct.d.ts +4 -0
  164. package/dist/util/Struct.js +15 -0
  165. package/dist/util/Template.d.ts +16 -0
  166. package/dist/util/Template.js +128 -0
  167. package/dist/util/Text.d.ts +45 -0
  168. package/dist/util/Text.js +243 -0
  169. package/dist/util/Tuples.d.ts +9 -0
  170. package/dist/util/Tuples.js +135 -0
  171. package/dist/util/Validate.d.ts +4 -0
  172. package/dist/util/Validate.js +11 -0
  173. package/dist/util/Vocabulary.d.ts +3 -0
  174. package/dist/util/Vocabulary.js +35 -0
  175. package/dist/util/index.d.ts +16 -0
  176. package/dist/util/index.js +33 -0
  177. package/lib/models/content/ArrayView.ts +203 -0
  178. package/lib/models/content/Billing.ts +558 -0
  179. package/lib/models/content/Content.ts +110 -0
  180. package/lib/models/content/ContentKind.ts +14 -0
  181. package/lib/models/content/DevEnv.ts +5 -0
  182. package/lib/models/content/Device.ts +86 -0
  183. package/lib/models/content/DictionaryEntry.ts +22 -0
  184. package/lib/models/content/GeoLocation.ts +4 -0
  185. package/lib/models/content/Hanzi.ts +25 -0
  186. package/lib/models/content/Manifest.ts +162 -0
  187. package/lib/models/content/Media.ts +126 -0
  188. package/lib/models/content/Model.ts +327 -0
  189. package/lib/models/content/Permissions.ts +21 -0
  190. package/lib/models/content/Phrase.ts +10 -0
  191. package/lib/models/content/Profile.ts +119 -0
  192. package/lib/models/content/Time.ts +328 -0
  193. package/lib/models/content/User.ts +130 -0
  194. package/lib/models/markup/ChangeModel.ts +95 -0
  195. package/lib/models/markup/DeltaOps.ts +71 -0
  196. package/lib/models/markup/Operation.ts +215 -0
  197. package/lib/models/markup/TextEditOps.ts +50 -0
  198. package/lib/repo/ConnectionListener.ts +25 -0
  199. package/lib/repo/PermissiveJson1.ts +14 -0
  200. package/lib/repo/ShareSync.ts +390 -0
  201. package/lib/util/Base62.ts +47 -0
  202. package/lib/util/CachingHasher.ts +38 -0
  203. package/lib/util/Dispatch.ts +92 -0
  204. package/lib/util/Encryption.ts +5 -0
  205. package/lib/util/Logging.ts +568 -0
  206. package/lib/util/NumberFormat.ts +194 -0
  207. package/lib/util/Struct.ts +14 -0
  208. package/lib/util/Tuples.ts +131 -0
  209. package/package.json +41 -0
  210. package/tsconfig.json +25 -0
  211. package/tslint.json +46 -0
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Book = exports.BookStubArrayKey = exports.BookType = void 0;
4
+ const ShareSync_1 = require("../../repo/ShareSync");
5
+ const Struct_1 = require("../../util/Struct");
6
+ const Operation_1 = require("../markup/Operation");
7
+ const ArrayView_1 = require("./ArrayView");
8
+ const Category_1 = require("./Category");
9
+ const ContentKind_1 = require("./ContentKind");
10
+ const Context_1 = require("./Context");
11
+ const ExportOptions_1 = require("./ExportOptions");
12
+ const Folder_1 = require("./Folder");
13
+ const Fragment_1 = require("./Fragment");
14
+ const Placeholder_1 = require("./Placeholder");
15
+ const Stub_1 = require("./Stub");
16
+ const Time_1 = require("./Time");
17
+ const Workspace_1 = require("./Workspace");
18
+ var BookType;
19
+ (function (BookType) {
20
+ BookType["FICTION"] = "fiction";
21
+ BookType["NONFICTION"] = "non-fiction";
22
+ BookType["EMPTY"] = "empty";
23
+ })(BookType || (exports.BookType = BookType = {}));
24
+ var BookStubArrayKey;
25
+ (function (BookStubArrayKey) {
26
+ BookStubArrayKey["MANUSCRIPT"] = "manuscript";
27
+ BookStubArrayKey["NOTEBOOK"] = "notebook";
28
+ BookStubArrayKey["TRASH"] = "trash";
29
+ })(BookStubArrayKey || (exports.BookStubArrayKey = BookStubArrayKey = {}));
30
+ class Book extends Context_1.Context {
31
+ constructor(doc, shouldAcquire, shareSync) {
32
+ super(doc, shouldAcquire, shareSync);
33
+ this._manuscriptView = new ArrayView_1.ArrayView(this, ['payload', 'manuscript']);
34
+ this._notebookView = new ArrayView_1.ArrayView(this, ['payload', 'notebook']);
35
+ }
36
+ get payload() {
37
+ this.checkDisposed("Book.payload");
38
+ return this.doc.data.payload;
39
+ }
40
+ static create(userId, template) {
41
+ const shareSync = ShareSync_1.ShareSyncFactory.get();
42
+ const bookId = shareSync.makeContentId();
43
+ // Create a synopsis and a chapter
44
+ const synopsis = Fragment_1.Fragment.create(userId, bookId, Category_1.Category.SYNOPSIS);
45
+ const chapter = Fragment_1.Fragment.create(userId, bookId, Category_1.Category.CHAPTER);
46
+ // Add three folders to the notebook
47
+ const notebookStubs = [];
48
+ if (template === BookType.FICTION) {
49
+ notebookStubs.push(Folder_1.Folder.create("CHARACTERS"));
50
+ notebookStubs.push(Folder_1.Folder.create("PLACES"));
51
+ notebookStubs.push(Folder_1.Folder.create("THEMES"));
52
+ }
53
+ else if (template === BookType.NONFICTION) {
54
+ notebookStubs.push(Folder_1.Folder.create("TOPICS"));
55
+ notebookStubs.push(Folder_1.Folder.create("RESOURCES"));
56
+ notebookStubs.push(Folder_1.Folder.create("QUESTIONS"));
57
+ }
58
+ const manuscriptStubs = [
59
+ {
60
+ kind: ContentKind_1.ContentKind.FRAGMENT,
61
+ category: Category_1.Category.CHAPTER,
62
+ ref: chapter.id,
63
+ label: ""
64
+ }
65
+ ];
66
+ const now = Time_1.MultiClock.now();
67
+ return shareSync.createContent({
68
+ meta: {
69
+ ref: bookId,
70
+ is_head: true,
71
+ kind: ContentKind_1.ContentKind.BOOK,
72
+ id: bookId,
73
+ owner: userId,
74
+ created_at: now,
75
+ updated_at: now
76
+ },
77
+ payload: {
78
+ name: "",
79
+ subtitle: "",
80
+ author: "",
81
+ contributors: new Array(),
82
+ copyright: "© " + Time_1.Time.currentYear(),
83
+ publisher: "",
84
+ isbn: "",
85
+ synopsis_ref: synopsis.id,
86
+ cover_media_ref: null,
87
+ manuscript: manuscriptStubs,
88
+ notebook: notebookStubs,
89
+ trash: [],
90
+ export_options: ExportOptions_1.ExportOptionsModel.makeDefaultOptions(),
91
+ total_word_count: 0
92
+ }
93
+ });
94
+ }
95
+ get coverMediaRef() {
96
+ this.checkDisposed("Book.coverMediaRef");
97
+ return this.payload.cover_media_ref;
98
+ }
99
+ setCoverMediaRef(value) {
100
+ this.checkDisposed("Book.setCoverMediaRef");
101
+ if (this.coverMediaRef != value) {
102
+ const batch = new Operation_1.BatchOperation(this);
103
+ batch.setPathValue(['payload', 'cover_media_ref'], value);
104
+ batch.commit();
105
+ }
106
+ }
107
+ get synopsisRef() {
108
+ this.checkDisposed("Book.synopsisRef");
109
+ return this.payload.synopsis_ref;
110
+ }
111
+ setSynopsisId(value) {
112
+ this.checkDisposed("Book.setSynopsisId");
113
+ if (this.synopsisRef != value) {
114
+ const batch = new Operation_1.BatchOperation(this);
115
+ batch.setPathValue(['payload', 'synopsis_ref'], value);
116
+ batch.commit();
117
+ }
118
+ }
119
+ get name() {
120
+ this.checkDisposed("Book.name");
121
+ return this.payload.name;
122
+ }
123
+ setName(value) {
124
+ this.checkDisposed("Book.setName");
125
+ if (this.name != value) {
126
+ const book = this;
127
+ const batch = new Operation_1.BatchOperation(this);
128
+ batch.editPathText(['payload', 'name'], value);
129
+ batch.commit();
130
+ // Update the name of this book in the workspace stub arrays.
131
+ const workspaceId = Workspace_1.Workspace.makeWorkspaceId(book.meta.owner);
132
+ ShareSync_1.ShareSyncFactory.get().acquire(ContentKind_1.ContentKind.WORKSPACE, workspaceId).then((workspace) => {
133
+ workspace.editStubFieldText(book.id, 'label', book.name);
134
+ workspace.release();
135
+ });
136
+ }
137
+ }
138
+ get subtitle() {
139
+ this.checkDisposed("Book.subtitle");
140
+ return this.payload.subtitle;
141
+ }
142
+ setSubtitle(value) {
143
+ this.checkDisposed("Book.setSubtitle");
144
+ if (this.subtitle != value) {
145
+ const batch = new Operation_1.BatchOperation(this);
146
+ batch.editPathText(['payload', 'subtitle'], value);
147
+ batch.commit();
148
+ }
149
+ }
150
+ get author() {
151
+ this.checkDisposed("Book.author");
152
+ return this.payload.author;
153
+ }
154
+ setAuthor(value) {
155
+ this.checkDisposed("Book.setAuthor");
156
+ if (this.author != value) {
157
+ const batch = new Operation_1.BatchOperation(this);
158
+ batch.editPathText(['payload', 'author'], value);
159
+ batch.commit();
160
+ }
161
+ }
162
+ get copyright() {
163
+ this.checkDisposed("Book.copyright");
164
+ return this.payload.copyright;
165
+ }
166
+ setCopyright(value) {
167
+ this.checkDisposed("Book.setCopyright");
168
+ if (this.copyright != value) {
169
+ const batch = new Operation_1.BatchOperation(this);
170
+ batch.editPathText(['payload', 'copyright'], value);
171
+ batch.commit();
172
+ }
173
+ }
174
+ get publisher() {
175
+ this.checkDisposed("Book.publisher");
176
+ return this.payload.publisher;
177
+ }
178
+ setPublisher(value) {
179
+ this.checkDisposed("Book.setPublisher");
180
+ if (this.publisher != value) {
181
+ const batch = new Operation_1.BatchOperation(this);
182
+ batch.editPathText(['payload', 'publisher'], value);
183
+ batch.commit();
184
+ }
185
+ }
186
+ get isbn() {
187
+ this.checkDisposed("Book.isbn");
188
+ return this.payload.isbn;
189
+ }
190
+ setIsbn(value) {
191
+ this.checkDisposed("Book.setIsbn");
192
+ if (this.isbn != value) {
193
+ const batch = new Operation_1.BatchOperation(this);
194
+ batch.editPathText(['payload', 'isbn'], value);
195
+ batch.commit();
196
+ }
197
+ }
198
+ get totalWordCount() {
199
+ this.checkDisposed("Book.totalWordCount");
200
+ return this.payload.total_word_count;
201
+ }
202
+ get manuscript() {
203
+ this.checkDisposed("Book.manuscript");
204
+ return this._manuscriptView;
205
+ }
206
+ get notebook() {
207
+ this.checkDisposed("Book.notebook");
208
+ return this._notebookView;
209
+ }
210
+ get exportOptions() {
211
+ this.checkDisposed("Book.exportOptions");
212
+ return this.payload.export_options || null;
213
+ }
214
+ replaceExportOptions(newOptions) {
215
+ this.checkDisposed("Book.replaceExportOptions");
216
+ // This method does a parallel-walk of the old export-options and the new
217
+ // export-options, and performs a minimal set of batch operations to bring
218
+ // this book's own embedded export-options inline with the new options.
219
+ const oldOptions = (this.exportOptions || {});
220
+ if (!Struct_1.Struct.equals(oldOptions, newOptions)) {
221
+ const batch = new Operation_1.BatchOperation(this);
222
+ if (oldOptions.selected_format != newOptions.selected_format) {
223
+ batch.setPathValue(['payload', 'export_options', 'selected_format'], newOptions.selected_format);
224
+ }
225
+ const oldFormatNames = Object.keys(oldOptions.formats).sort();
226
+ const newFormatNames = Object.keys(newOptions.formats).sort();
227
+ const commonFormatNames = [];
228
+ for (let i = 0; i < oldFormatNames.length; i++) {
229
+ const oldFormatName = oldFormatNames[i];
230
+ if (newFormatNames.includes(oldFormatName)) {
231
+ commonFormatNames.push(oldFormatName);
232
+ }
233
+ else {
234
+ batch.removeValueAtPath(['payload', 'export_options', 'formats', oldFormatName]);
235
+ }
236
+ }
237
+ for (let i = 0; i < newFormatNames.length; i++) {
238
+ const newFormatName = newFormatNames[i];
239
+ if (!oldFormatNames.includes(newFormatName)) {
240
+ const newFormatValue = newOptions.formats[newFormatName];
241
+ batch.setPathValue(['payload', 'export_options', 'formats', newFormatName], newFormatValue);
242
+ }
243
+ }
244
+ for (let i = 0; i < commonFormatNames.length; i++) {
245
+ const formatName = commonFormatNames[i];
246
+ const oldFormatOptions = oldOptions.formats[formatName];
247
+ const newFormatOptions = newOptions.formats[formatName];
248
+ if (oldFormatOptions.include_toc != newFormatOptions.include_toc) {
249
+ batch.setPathValue(['payload', 'export_options', 'formats', formatName, 'include_toc'], newFormatOptions.include_toc);
250
+ }
251
+ if (oldFormatOptions.include_synopsis != newFormatOptions.include_synopsis) {
252
+ batch.setPathValue(['payload', 'export_options', 'formats', formatName, 'include_synopsis'], newFormatOptions.include_synopsis);
253
+ }
254
+ const stubKeys = ['manuscript', 'notebook'];
255
+ for (let j = 0; j < stubKeys.length; j++) {
256
+ const stubKey = stubKeys[j];
257
+ const oldStubKeyOptions = oldFormatOptions[stubKey];
258
+ const newStubKeyOptions = newFormatOptions[stubKey];
259
+ if (oldStubKeyOptions.include != newStubKeyOptions.include) {
260
+ batch.setPathValue(['payload', 'export_options', 'formats', formatName, stubKey, 'include'], newStubKeyOptions.include);
261
+ }
262
+ const oldCategoryNames = Object.keys(oldStubKeyOptions.categories).sort();
263
+ const newCategoryNames = Object.keys(newStubKeyOptions.categories).sort();
264
+ const commonCategoryNames = [];
265
+ for (let k = 0; k < oldCategoryNames.length; k++) {
266
+ const oldCategoryName = oldCategoryNames[k];
267
+ if (newCategoryNames.includes(oldCategoryName)) {
268
+ commonCategoryNames.push(oldCategoryName);
269
+ }
270
+ else {
271
+ batch.removeValueAtPath(['payload', 'export_options', 'formats', formatName, stubKey, 'categories', oldCategoryName]);
272
+ }
273
+ }
274
+ for (let k = 0; k < newCategoryNames.length; k++) {
275
+ const newCategoryName = newCategoryNames[k];
276
+ if (!oldCategoryNames.includes(newCategoryName)) {
277
+ const newCategoryOptions = newStubKeyOptions.categories[newCategoryName];
278
+ batch.setPathValue(['payload', 'export_options', 'formats', formatName, stubKey, 'categories', newCategoryName], newCategoryOptions);
279
+ }
280
+ }
281
+ for (let k = 0; k < commonCategoryNames.length; k++) {
282
+ const categoryName = commonCategoryNames[k];
283
+ const oldCategoryOptions = oldStubKeyOptions.categories[categoryName];
284
+ const newCategoryOptions = newStubKeyOptions.categories[categoryName];
285
+ const categoryOptionKeys = ['toc', 'name', 'number', 'prefix', 'brk'];
286
+ for (let m = 0; m < categoryOptionKeys.length; m++) {
287
+ const categoryOptionKey = categoryOptionKeys[m];
288
+ const oldCategoryOptionValue = oldCategoryOptions[categoryOptionKey];
289
+ const newCategoryOptionValue = newCategoryOptions[categoryOptionKey];
290
+ if (oldCategoryOptionValue != newCategoryOptionValue) {
291
+ batch.setPathValue(['payload', 'export_options', formatName, stubKey, 'categories', categoryName, categoryOptionKey], newCategoryOptionValue);
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+ batch.commit();
298
+ }
299
+ }
300
+ getStubArrayKeys() {
301
+ return [
302
+ BookStubArrayKey.MANUSCRIPT,
303
+ BookStubArrayKey.NOTEBOOK,
304
+ BookStubArrayKey.TRASH
305
+ ];
306
+ }
307
+ getStubArrayView(key) {
308
+ this.checkDisposed("Book.getStubArrayView");
309
+ if (key === BookStubArrayKey.MANUSCRIPT) {
310
+ return this._manuscriptView;
311
+ }
312
+ else if (key === BookStubArrayKey.NOTEBOOK) {
313
+ return this._notebookView;
314
+ }
315
+ else if (key === BookStubArrayKey.TRASH) {
316
+ return this._trashView;
317
+ }
318
+ return null;
319
+ }
320
+ addFragmentToManuscript(fragment, activeId) {
321
+ this.checkDisposed("Book.addFragmentToManuscript");
322
+ const stub = fragment.asStub();
323
+ if (fragment.category === Category_1.Category.FRONT_MATTER) {
324
+ // Always add front-matter to the front.
325
+ this.manuscript.unshift(stub);
326
+ }
327
+ else if (fragment.category === Category_1.Category.BACK_MATTER) {
328
+ // Always add back-matter to the back.
329
+ this.manuscript.push(stub);
330
+ }
331
+ else {
332
+ // All other content should be added to the most natural point, based on whatever content is currently active
333
+ Stub_1.Stubs.insertStubAtActivePoint(stub, this._manuscriptView, fragment.category, activeId, false);
334
+ }
335
+ if (fragment.wordCount > 0) {
336
+ this.updateWordCount();
337
+ }
338
+ }
339
+ addFragmentToNotebook(fragment, activeId) {
340
+ this.checkDisposed("Book.addFragmentToNotebook");
341
+ const stub = fragment.asStub();
342
+ Stub_1.Stubs.insertStubAtActivePoint(stub, this._notebookView, fragment.category, activeId, true);
343
+ }
344
+ async updateWordCount() {
345
+ this.checkDisposed("Book.updateWordCount");
346
+ const prevTotalWordCount = this.totalWordCount;
347
+ const shareSync = ShareSync_1.ShareSyncFactory.get();
348
+ let totalWordCount = 0;
349
+ for (let i = 0, len = this.manuscript.length; i < len; i++) {
350
+ const stub = this.manuscript.get(i);
351
+ if (Stub_1.Stubs.isContentStub(stub)) {
352
+ // TODO: should we skip front-matter and back-matter fragments?
353
+ const ref = stub.ref;
354
+ const chapter = await shareSync.acquire(ContentKind_1.ContentKind.FRAGMENT, ref);
355
+ totalWordCount += chapter.wordCount;
356
+ chapter.release();
357
+ }
358
+ }
359
+ if (totalWordCount != prevTotalWordCount) {
360
+ const batch = new Operation_1.BatchOperation(this);
361
+ batch.setPathValue(['payload', 'total_word_count'], totalWordCount);
362
+ batch.commit();
363
+ }
364
+ }
365
+ getAllCategories(context) {
366
+ this.checkDisposed("Book.getAllCategories");
367
+ return Book.harvestUniqueCategories(this.payload, context);
368
+ }
369
+ getPlaceholder() {
370
+ this.checkDisposed("Book.getPlaceholder");
371
+ return Placeholder_1.Placeholder.forContent(this);
372
+ }
373
+ asStub() {
374
+ this.checkDisposed("Book.asStub");
375
+ return {
376
+ kind: ContentKind_1.ContentKind.BOOK,
377
+ ref: this.id,
378
+ label: this.name
379
+ };
380
+ }
381
+ static harvestUniqueCategories(bookPayload, context) {
382
+ const stubs = context === BookStubArrayKey.MANUSCRIPT ?
383
+ bookPayload.manuscript :
384
+ bookPayload.notebook;
385
+ const unique = [];
386
+ const categories = {};
387
+ for (let i = 0, len = stubs.length; i < len; i++) {
388
+ const stub = stubs[i];
389
+ let category = null;
390
+ if (Stub_1.Stubs.isFolderStub(stub)) {
391
+ category = ContentKind_1.ContentKind.FOLDER;
392
+ }
393
+ else if (Stub_1.Stubs.isContentStub(stub)) {
394
+ category = stub.category;
395
+ }
396
+ if (!categories.hasOwnProperty(category)) {
397
+ categories[category] = true;
398
+ unique.push(category);
399
+ }
400
+ }
401
+ return unique;
402
+ }
403
+ mergeIncomingContext(incomingBook) {
404
+ return super.mergeIncomingContext(incomingBook);
405
+ }
406
+ }
407
+ exports.Book = Book;
@@ -0,0 +1,16 @@
1
+ export declare enum Category {
2
+ FRONT_MATTER = "front-matter",
3
+ BACK_MATTER = "back-matter",
4
+ FOLDER = "folder",
5
+ PART = "part",
6
+ CHAPTER = "chapter",
7
+ SCENE = "scene",
8
+ SYNOPSIS = "synopsis",
9
+ ARTICLE = "article",
10
+ SHORT_STORY = "short-story",
11
+ IDEA = "idea",
12
+ NOTE = "note",
13
+ CHARACTER = "character",
14
+ PLACE = "place",
15
+ THEME = "theme"
16
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Category = void 0;
4
+ var Category;
5
+ (function (Category) {
6
+ Category["FRONT_MATTER"] = "front-matter";
7
+ Category["BACK_MATTER"] = "back-matter";
8
+ Category["FOLDER"] = "folder";
9
+ Category["PART"] = "part";
10
+ Category["CHAPTER"] = "chapter";
11
+ Category["SCENE"] = "scene";
12
+ Category["SYNOPSIS"] = "synopsis";
13
+ Category["ARTICLE"] = "article";
14
+ Category["SHORT_STORY"] = "short-story";
15
+ Category["IDEA"] = "idea";
16
+ Category["NOTE"] = "note";
17
+ Category["CHARACTER"] = "character";
18
+ Category["PLACE"] = "place";
19
+ Category["THEME"] = "theme";
20
+ })(Category || (exports.Category = Category = {}));
@@ -0,0 +1,21 @@
1
+ /// <reference path="../../../decs.d.ts" />
2
+ import { Doc } from '@shaxpir/sharedb/lib/client';
3
+ import { ShareSync } from '../../repo/ShareSync';
4
+ import { ArrayView } from './ArrayView';
5
+ import { Content, ContentBody } from "./Content";
6
+ import { CompactDateTime, MultiTime } from './Time';
7
+ export declare abstract class Checkpointable extends Content {
8
+ private _checkpointsView;
9
+ constructor(doc: Doc, shouldAcquire: boolean, shareSync: ShareSync);
10
+ get checkpoints(): ArrayView<CompactDateTime>;
11
+ hasCheckpoint(timestamp: CompactDateTime): boolean;
12
+ addCheckpoint(timestamp: CompactDateTime): boolean;
13
+ makeCheckpointBody(): ContentBody;
14
+ cloneCheckpoints(): CompactDateTime[];
15
+ recordCheckpoint(checkpointBody: ContentBody): void;
16
+ acquireLastCheckpoint(): Promise<Content>;
17
+ acquireLatestCheckpointAt(timestamp: CompactDateTime): Promise<Content>;
18
+ loadLatestCheckpointAt(timestamp: CompactDateTime): Promise<Content>;
19
+ beforeRestoreTo(checkpoint: Checkpointable, now: MultiTime): Promise<void>;
20
+ restoreTo(checkpoint: Checkpointable, now: MultiTime): Promise<void>;
21
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Checkpointable = void 0;
4
+ const util_1 = require("../../util");
5
+ const Operation_1 = require("../markup/Operation");
6
+ const ArrayView_1 = require("./ArrayView");
7
+ const Content_1 = require("./Content");
8
+ const Time_1 = require("./Time");
9
+ class Checkpointable extends Content_1.Content {
10
+ constructor(doc, shouldAcquire, shareSync) {
11
+ super(doc, shouldAcquire, shareSync);
12
+ this._checkpointsView = null;
13
+ this._checkpointsView = new ArrayView_1.ArrayView(this, ['meta', 'checkpoints']);
14
+ }
15
+ get checkpoints() {
16
+ this.checkDisposed("Content.checkpoints");
17
+ return this._checkpointsView;
18
+ }
19
+ hasCheckpoint(timestamp) {
20
+ this.checkDisposed("Content.hasCheckpoint");
21
+ let result = util_1.BinarySearch.ofDateTimes(timestamp, this.meta.checkpoints);
22
+ return result >= 0;
23
+ }
24
+ addCheckpoint(timestamp) {
25
+ this.checkDisposed("Content.addCheckpoint");
26
+ // We always want to add the new checkpoint at the correctly-sorted index.
27
+ let result = util_1.BinarySearch.ofDateTimes(timestamp, this.meta.checkpoints);
28
+ if (result >= 0) {
29
+ // This checkpoint timestamp is already recorded. Don't add it again.
30
+ return false;
31
+ }
32
+ let insertionPoint = -(result + 1);
33
+ this.checkpoints.insert(insertionPoint, timestamp);
34
+ return true;
35
+ }
36
+ makeCheckpointBody() {
37
+ this.checkDisposed("Checkpointable.makeCheckpointBody");
38
+ const body = this.cloneContentBody();
39
+ if (body && body.meta) {
40
+ body.meta.is_head = false;
41
+ body.meta.created_at = util_1.Struct.clone(body.meta.updated_at);
42
+ body.meta.ref = Content_1.Content.makeContentRef(body.meta.id, body.meta.updated_at.utc_time);
43
+ if (body.meta.hasOwnProperty('checkpoints')) {
44
+ delete body.meta.checkpoints;
45
+ }
46
+ }
47
+ return body;
48
+ }
49
+ cloneCheckpoints() {
50
+ this.checkDisposed("Checkpointable.cloneCheckpoints");
51
+ if (this.meta.checkpoints) {
52
+ return util_1.Struct.clone(this.meta.checkpoints);
53
+ }
54
+ else {
55
+ return null;
56
+ }
57
+ }
58
+ recordCheckpoint(checkpointBody) {
59
+ this.checkDisposed("Checkpointable.recordCheckpoint");
60
+ // Don't take new checkpoints for timestamps that have already been checkpointed.
61
+ let checkpointTimestamp = checkpointBody.meta.created_at.utc_time;
62
+ if (!this.hasCheckpoint(checkpointTimestamp)) {
63
+ // Record the new checkpoint content
64
+ const checkpoint = this.shareSync.createContent(checkpointBody);
65
+ checkpoint.release();
66
+ // Add a new timestamp entry to the checkpoints array
67
+ this._checkpointsView.push(checkpointTimestamp);
68
+ }
69
+ }
70
+ async acquireLastCheckpoint() {
71
+ if (this.meta.checkpoints && this.meta.checkpoints.length > 0) {
72
+ let checkpoints = this.cloneCheckpoints().sort();
73
+ const latestCheckpointTimestamp = checkpoints[checkpoints.length - 1];
74
+ const latestCheckpointRef = Content_1.Content.makeContentRef(this.id, latestCheckpointTimestamp);
75
+ const checkpoint = await this.shareSync.acquire(this.kind, latestCheckpointRef);
76
+ return checkpoint;
77
+ }
78
+ return null;
79
+ }
80
+ async acquireLatestCheckpointAt(timestamp) {
81
+ let checkpoint = await this.loadLatestCheckpointAt(timestamp);
82
+ checkpoint = await checkpoint.acquire();
83
+ return checkpoint;
84
+ }
85
+ async loadLatestCheckpointAt(timestamp) {
86
+ this.checkDisposed("Checkpointable.findLatestCheckpointAt");
87
+ // To search through checkpoints, we need to make sure we're looking at the HEAD version.
88
+ if (this.isCheckpoint) {
89
+ const head = await this.shareSync.acquire(this.kind, this.id);
90
+ const latestCheckpoint = await head.loadLatestCheckpointAt(timestamp);
91
+ head.release();
92
+ return latestCheckpoint;
93
+ }
94
+ // First, check to see if the HEAD version of this content might be the "latest checkpoint".
95
+ if (Time_1.Time.isDateTimeBefore(this.updatedAt.utc_time, timestamp)) {
96
+ return this;
97
+ }
98
+ // The HEAD version is not the latest checkpoint at the given timestamp. Try to find an actual checkpoint.
99
+ let checkpoints = this.cloneCheckpoints();
100
+ if (checkpoints) {
101
+ // Sort the checkpoints array in ascending order (oldest checkpoints are first).
102
+ checkpoints.sort();
103
+ // Use binary-search to find this timestamp
104
+ let idx = util_1.BinarySearch.ofDateTimes(timestamp, checkpoints);
105
+ // If the resut is negative, then it's an insertion-point...
106
+ if (idx < 0) {
107
+ // Convert the result into an insertion point.
108
+ let insertionPoint = -(idx + 1);
109
+ // Insertion points always represent the position AFTER the first element that's LESS THAN this element.
110
+ // So to find the checkpoint that was active at the designated timestamp, we need to find the position
111
+ // BEFORE this insertion point. But if the insertion point is ZERO, then there is no such checkpoint...
112
+ if (insertionPoint == 0) {
113
+ return null;
114
+ }
115
+ idx = insertionPoint - 1;
116
+ }
117
+ const checkpointTimestamp = checkpoints[idx];
118
+ const checkpointRef = Content_1.Content.makeContentRef(this.id, checkpointTimestamp);
119
+ const checkpoint = await this.shareSync.acquire(this.kind, checkpointRef);
120
+ return checkpoint;
121
+ }
122
+ return this;
123
+ }
124
+ async beforeRestoreTo(checkpoint, now) {
125
+ this.checkDisposed("Checkpointable.beforeRestoreTo");
126
+ // By default, do nothing. Subclasses can override this behavior, if they choose.
127
+ }
128
+ async restoreTo(checkpoint, now) {
129
+ this.checkDisposed("Checkpointable.restoreTo");
130
+ if (this.isCheckpoint || checkpoint.kind != this.kind || checkpoint.id !== this.id) {
131
+ throw new Error(`can't restore checkpoint ${checkpoint.kind}/${checkpoint.ref} into ${this.kind}/${this.ref}`);
132
+ }
133
+ // Ensure that the data in the head model has been populated.
134
+ await this.ensureData();
135
+ // Call any kind-specific logic preceeding the restoration. For example, a Fragment
136
+ // might need to restore embedded Comments before restoring the Fragment itself.
137
+ await this.beforeRestoreTo(checkpoint, now);
138
+ // We need to make sure the current HEAD state is recorded as a checkpoint. If the most-recent
139
+ // checkpoint has the same payload as the head, then we can skip recording a new checkpoint.
140
+ let lastCheckpointPayload = null;
141
+ const lastCheckpoint = await this.acquireLastCheckpoint();
142
+ if (lastCheckpoint !== null) {
143
+ lastCheckpointPayload = lastCheckpoint.payload;
144
+ lastCheckpoint.release();
145
+ }
146
+ if (!util_1.Struct.equals(lastCheckpointPayload, this.payload)) {
147
+ const checkpointBody = this.makeCheckpointBody();
148
+ this.recordCheckpoint(checkpointBody);
149
+ }
150
+ // Replace the current payload with the payload from the selected checkpoint.
151
+ let batch = new Operation_1.BatchOperation(this, now);
152
+ batch.setPathValue(['payload'], util_1.Struct.clone(checkpoint.payload));
153
+ batch.commit();
154
+ }
155
+ }
156
+ exports.Checkpointable = Checkpointable;
@@ -0,0 +1,19 @@
1
+ /// <reference path="../../../decs.d.ts" />
2
+ import { Doc } from '@shaxpir/sharedb/lib/client';
3
+ import { ShareSync } from '../../repo/ShareSync';
4
+ import { ContentBody, ContentId, ContentMeta, ContentRef } from './Content';
5
+ import { RichText, RichTextPayload } from './RichText';
6
+ export interface CommentPayload extends RichTextPayload {
7
+ anchor: ContentRef;
8
+ }
9
+ export interface CommentBody extends ContentBody {
10
+ meta: ContentMeta;
11
+ payload: CommentPayload;
12
+ }
13
+ export declare class Comment extends RichText {
14
+ static create(userId: ContentId, anchorContentRef: ContentRef): Comment;
15
+ constructor(doc: Doc, shouldAcquire: boolean, shareSync: ShareSync);
16
+ get payload(): CommentPayload;
17
+ get anchor(): ContentRef;
18
+ setAnchor(value: ContentRef): void;
19
+ }