-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsquibview-dev.js
More file actions
267 lines (228 loc) · 6.42 KB
/
squibview-dev.js
File metadata and controls
267 lines (228 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* squibview-dev.js - Development version wrapper
*
* This file wraps the original squibview.js and handles
* the import-map-based dependencies for local development.
*/
// Import the shims for development mode
import * as shim from './import-shim.js';
// Now we can safely use these in our code
const TinyEmitter = shim.TinyEmitter;
const DiffMatchPatch = shim.DiffMatchPatch;
/**
* RevisionManager class handles content revisions with memory-efficient diff storage
*/
class RevisionManager {
/**
* Creates a new RevisionManager instance
*/
constructor() {
this.initialContent = '';
this.contentType = '';
this.diffs = [];
this.currentIndex = -1;
this.diffEngine = new DiffMatchPatch();
}
/**
* Initializes the revision manager with initial content
*
* @param {string} content - The initial content
* @param {string} contentType - The content type
*/
initialize(content, contentType) {
this.initialContent = content;
this.contentType = contentType;
this.diffs = [];
this.currentIndex = -1;
}
/**
* Adds a new revision
*
* @param {string} newContent - The new content to add as a revision
* @param {string} contentType - The content type of the revision
*/
addRevision(newContent, contentType) {
// Calculate diff between current state and new state
const currentContent = this.getCurrentContent();
const diff = this.diffEngine.diff_main(currentContent, newContent);
this.diffEngine.diff_cleanupSemantic(diff);
const patchText = this.diffEngine.patch_toText(
this.diffEngine.patch_make(currentContent, diff)
);
// Remove any forward history if we're branching
if (this.currentIndex < this.diffs.length - 1) {
this.diffs = this.diffs.slice(0, this.currentIndex + 1);
}
// Add the new diff
this.diffs.push({
patch: patchText,
contentType
});
this.currentIndex = this.diffs.length - 1;
}
/**
* Gets the current revision content
*
* @returns {string} The current content
*/
getCurrentContent() {
if (this.currentIndex < 0) {
return this.initialContent;
}
// Start with initial content and apply all diffs up to current index
let content = this.initialContent;
for (let i = 0; i <= this.currentIndex; i++) {
const patches = this.diffEngine.patch_fromText(this.diffs[i].patch);
const [patchedText] = this.diffEngine.patch_apply(patches, content);
content = patchedText;
}
return content;
}
/**
* Gets the current revision content type
*
* @returns {string} The current content type
*/
getCurrentContentType() {
if (this.currentIndex < 0) {
return this.contentType;
}
return this.diffs[this.currentIndex].contentType;
}
/**
* Moves to the previous revision if available
*
* @returns {Object|null} The previous state or null if at the beginning
*/
undo() {
if (this.currentIndex >= 0) {
this.currentIndex--;
return {
content: this.getCurrentContent(),
contentType: this.getCurrentContentType()
};
}
return null;
}
/**
* Moves to the next revision if available
*
* @returns {Object|null} The next state or null if at the end
*/
redo() {
if (this.currentIndex < this.diffs.length - 1) {
this.currentIndex++;
return {
content: this.getCurrentContent(),
contentType: this.getCurrentContentType()
};
}
return null;
}
/**
* Sets the revision to a specific index
*
* @param {number} index - The revision index to set
* @returns {Object|null} The state at the index or null if invalid
*/
setRevision(index) {
if (index >= -1 && index < this.diffs.length) {
this.currentIndex = index;
return {
content: this.getCurrentContent(),
contentType: this.getCurrentContentType()
};
}
return null;
}
/**
* Gets the total number of revisions
*
* @returns {number} The number of revisions
*/
getRevisionCount() {
return this.diffs.length;
}
/**
* Gets the current revision index
*
* @returns {number} The current revision index
*/
getCurrentIndex() {
return this.currentIndex;
}
}
/**
* SquibView class for markdown and HTML editing with live preview
*/
class SquibView {
/**
* Creates a new SquibView instance
*
* @param {string|HTMLElement} element - The selector or element to initialize SquibView in
* @param {Object} options - Configuration options
*/
constructor(element, options = {}) {
// Set up the options with defaults
this.options = {
baseClass: 'squibview',
titleShow: true,
titleContent: 'SquibView',
showControls: true,
show_md_buttons: false,
initialContent: '',
inputContentType: 'md',
initialView: 'split',
onReplaceSelectedText: null,
...options
};
// Initialize the event emitter
this.events = new TinyEmitter();
// Initialize the revision manager
this.revisionManager = new RevisionManager();
// Set up the container
if (typeof element === 'string') {
this.container = document.querySelector(element);
} else {
this.container = element;
}
if (!this.container) {
throw new Error(`Element ${element} not found`);
}
// Create the basic structure
this.createStructure();
// Set up event handlers
this.initializeEventHandlers();
// Initialize input content
this.inputContentType = this.options.inputContentType;
this.input.value = this.options.initialContent;
// Save initial revision if content provided
if (this.options.initialContent) {
this.revisionManager.initialize(this.options.initialContent, this.inputContentType);
}
// Set view mode
this.setView(this.options.initialView);
// Initialize selection state
this._selectionCache = {
source: null,
rendered: null
};
// Render output
this.renderOutput();
}
// Include the rest of the SquibView class here
// ...
/**
* Version information
*/
static get version() {
return {
version: '0.0.30-dev',
build: 'development',
date: new Date().toISOString()
};
}
}
// Export the SquibView class and RevisionManager
export default SquibView;
export { RevisionManager };