-
Notifications
You must be signed in to change notification settings - Fork 0
/
ETO_Foundation.js
280 lines (245 loc) · 7.82 KB
/
ETO_Foundation.js
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
268
269
270
271
272
273
274
275
276
277
278
279
280
// Author:
// Evan Thomas Olds
//
// Creation Date:
// January 26, 2017
// (some classes, originally created November 28, 2016, migrated to this file)
//
// Implementation references:
// https://developer.mozilla.org/
//
// File Dependencies:
// (none)
//
// Technology Dependencies:
// ES 5.1 or later
//
// Declared global functions in this file:
// ETO_StringCount
// ETO_StringIsWhitespace
// ETO_StringFromRepeat
// ETO_StringReplaceAll
//
// Declared classes (constructor functions) in this file:
// ETO_Result
// ETO_TextSelection
// ----------------------------------------------------------
// ETO_Result(success, message)
//
// Constructor function for a result object with read-only members "success" and "message".
// An optional "op" member is a string to provide the operation name. Suggested form of this
// string is "className.functionName", although this is not required.
// More members can (and probably should) be added, if needed.
function ETO_Result(success, message, operationName)
{
// Add "success" member, making sure it's a bool
if (success)
success = true;
else
success = false;
Object.defineProperty(this, "success", {"enumerable": true, "value": success});
// Add "message" member
Object.defineProperty(this, "message", {"enumerable": true, "value": message});
// If the "operationName" argument was provided, then add "op" member
if (typeof operationName != "undefined")
Object.defineProperty(this, "op", {"enumerable": true, "value": operationName});
}
// ----------------------------------------------------------
// Constructor function for an immutable object representing a single selected range within
// a text string. Internally this is stored as a reference to the text string, a starting
// character index, and a length, in characters. The length can be any integer, including 0
// or negative values.
// If the starting index is outside of the range [0, text.length - 1], then it will be
// clamped to this range, regardless of the length value.
// After clamping the start index, if the start index + length goes beyond the bounds of
// the text string then the length value will be clamped.
function ETO_TextSelection(text, startIndex, length)
{
this.text = text;
// Clamp starting index if need be
if (startIndex < 0) { this.startIndex = 0; }
else if (startIndex >= text.length) { this.startIndex = text.length - 1; }
else { this.startIndex = startIndex; }
// Clamp length if need be
if (this.startIndex + length >= text.length)
{
this.length = text.length - this.startIndex;
}
else if (this.startIndex + length < 0)
{
this.length = -this.startIndex;
}
else { this.length = length; }
// Make immutable
Object.freeze(this);
}
// Returns an array of indices for lines that are completely contained within the selection.
// Uses '\n' as the line break string.
ETO_TextSelection.prototype.getFullySelectedLineIndices = function()
{
if (this.length === 0) { return []; }
// Compute inclusive start index and exclusive end index
var start, end;
if (this.length > 0)
{
start = this.startIndex;
end = start + this.length;
}
else
{
start = this.startIndex + this.length;
end = this.startIndex;
}
var lineStart = 0;
var lineIndex = 0;
var go = true;
var result = [];
while (go && lineStart < this.text.length)
{
var lineEnd = this.text.indexOf('\n', lineStart + 1);
if (lineEnd == -1)
{
lineEnd = this.text.length;
go = false;
}
// If the whole line is within the selected range, then add
// the line index
if (lineStart >= start && lineEnd <= end)
{
result.push(lineIndex);
}
// Go to next line for next loop iteration
lineStart = lineEnd + 1;
lineIndex++;
}
return result;
}
// Gets the 0-based line index of the line where the selection starts. Uses '\n' as the
// line break string.
ETO_TextSelection.prototype.getStartLineIndex = function()
{
var lineIndex = 0;
// Count the number of line break characters before the selection starting index
for (var i = 0; i < this.startIndex; i++)
{
if (this.text[i] == "\n")
{
lineIndex++;
}
}
return lineIndex;
}
// Gets the 0-based character index of the where the selection starts, relative to the line
// that the selection is on. Example:
// If the text is "AAA\nBB\C" and the selection start is 5, meaning the selection starts
// before the second 'B' on the second line, then this function would return 1.
// Uses '\n' as the line break string.
ETO_TextSelection.prototype.getStartLineCharIndex = function()
{
var index = 0;
for (var i = 0; i < this.startIndex; i++)
{
// Reset the index to 0 each time we pass a line break
if (this.text[i] == "\n")
index = 0;
else
index++;
}
return index;
}
ETO_TextSelection.prototype.getSelectedText = function()
{
if (this.length === 0) { return ""; }
else if (this.length > 0)
{
return this.text.substr(this.startIndex, this.length);
}
// Negative length means the starting index is really at start+length
var len = Math.abs(this.length);
return this.text.substr(this.startIndex+this.length, len);
}
// Finds the last line that has one or more character within the selection range
// and returns a text string for this line. If the 'fullText' parameter is true,
// then the full text of this line, including any portions outside of this
// selection, is returned. If 'fullText' is false, undefined, or any other
// non-true value, then only the selected text on the last line is returned.
// Uses '\n' as the line break string.
ETO_TextSelection.prototype.getTextOfLastLine = function(fullText)
{
// Start by getting the selected text
var selText = this.getSelectedText();
// First special case is empty string
if (selText.length == 0) { return ""; }
// Next special case if the selected text ends with '\n'
if (selText[selText.length - 1] == "\n")
{
// Chop off the last '\n' and fall through
selText = selText.substr(0, selText.length - 1);
}
// Find the last line break and return what's after it
var lastBreakIndex = selText.lastIndexOf("\n");
return selText.substr(lastBreakIndex + 1);
}
ETO_TextSelection.prototype.getTextWithSelectionRemoved = function()
{
// If there is no selection (selection length 0), then return whole text
if (this.length === 0) { return this.text; }
var start, len;
if (this.length < 0)
{
// Negative length means the starting index is really at start+length
len = Math.abs(this.length);
start = this.startIndex + this.length;
}
else
{
start = this.startIndex;
len = this.length;
}
// The string substr function allows length 0, so we don't need special
// cases and instead can just take text before and after selection.
return this.text.substr(0, start) + this.text.substr(start + len);
}
// ----------------------------------------------------------
// String utility functions follow
// ETO_StringCount(str, substr)
// Counts the number of non-overlapping occurrences of substr within str
function ETO_StringCount(str, substr)
{
var count = 0;
var index = str.indexOf(substr);
while (index !== -1)
{
count++;
index = str.indexOf(substr, index + substr.length);
}
return count;
}
function ETO_StringIsWhitespace(str)
{
for (var i = 0; i < str.length; i++)
{
var theChar = str.charAt(i);
if (theChar != " " && theChar != "\t")
return false;
}
return true;
}
function ETO_StringFromRepeat(str, repeatCount)
{
var result = str;
for (var i = 1; i < repeatCount; i++)
{
result += str;
}
return result;
}
function ETO_StringReplaceAll(str, replaceMe, withMe)
{
// Should probably implement a more efficient version later
while (str.indexOf(replaceMe) !== -1)
{
str = str.replace(replaceMe, withMe);
}
return str;
}