-
-
Notifications
You must be signed in to change notification settings - Fork 194
Expand file tree
/
Copy pathStringUtils.js
More file actions
322 lines (288 loc) · 10.4 KB
/
StringUtils.js
File metadata and controls
322 lines (288 loc) · 10.4 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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
* GNU AGPL-3.0 License
*
* Copyright (c) 2021 - present core.ai . All rights reserved.
* Original work Copyright (c) 2012 - 2021 Adobe Systems Incorporated. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
* for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
*
*/
// @INCLUDE_IN_API_DOCS
/* The hash code routne is taken from http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
@CC wiki attribution: esmiralha
*/
/*eslint no-bitwise: off */
/*jslint bitwise: true */
/**
* Utilities functions related to string manipulation
*
* @module utils/StringUtils
*/
define(function (require, exports, module) {
var _ = require("thirdparty/lodash");
/**
* Format a string by replacing placeholder symbols with passed in arguments.
*
* Example: var formatted = StringUtils.format("Hello {0}", "World");
*
* @param {string} str The base string
* @param {rest} Arguments to be substituted into the string
*
* @return {string} Formatted string
*/
function format(str) {
// arguments[0] is the base string, so we need to adjust index values here
var args = [].slice.call(arguments, 1);
return str.replace(/\{\s*(\d+)\s*\}/g, function (match, num) {
return typeof args[num] !== "undefined" ? args[num] : match;
});
}
/**
* Regex escape
*
* @param {string} str
* @returns {string}
*/
function regexEscape(str) {
return str.replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
}
/**
* Periods (aka "dots") are allowed in HTML identifiers, but jQuery interprets
* them as the start of a class selector, so they need to be escaped
*
* @param {string} str
* @returns {string}
*/
function jQueryIdEscape(str) {
return str.replace(/\./g, "\\.");
}
/**
* Splits the text by new line characters and returns an array of lines
*
* @param {string} text
* @return {Array.<string>} lines
*/
function getLines(text) {
return text.split("\n");
}
/**
* Returns a line number corresponding to an offset in some text. The text can
* be specified as a single string or as an array of strings that correspond to
* the lines of the string.
*
* Specify the text in lines when repeatedly calling the function on the same
* text in a loop. Use getLines() to divide the text into lines, then repeatedly call
* this function to compute a line number from the offset.
*
* @param {string | Array.<string>} textOrLines - string or array of lines from which
* to compute the line number from the offset
* @param {number} offset
* @return {number} line number
*/
function offsetToLineNum(textOrLines, offset) {
if (Array.isArray(textOrLines)) {
var lines = textOrLines,
total = 0,
line;
for (line = 0; line < lines.length; line++) {
if (total < offset) {
// add 1 per line since /n were removed by splitting, but they needed to
// contribute to the total offset count
total += lines[line].length + 1;
} else if (total === offset) {
return line;
} else {
return line - 1;
}
}
// if offset is NOT over the total then offset is in the last line
if (offset <= total) {
return line - 1;
}
return undefined;
}
return textOrLines.substr(0, offset).split("\n").length - 1;
}
/**
* Returns true if the given string starts with the given prefix.
*
* @param {String} str
* @param {String} prefix
* @return {Boolean}
*/
function startsWith(str, prefix) {
return str.slice(0, prefix.length) === prefix;
}
/**
* Returns true if the given string ends with the given suffix.
*
* @param {string} str
* @param {string} suffix
*/
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
/**
* sort two urls alphabetically
* ensure folders appear before files on windows
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
function urlSort(a, b) {
var a2, b2;
function isFile(s) {
return ((s.lastIndexOf("/") + 1) < s.length);
}
if (brackets.platform === "win") {
// Windows: prepend folder names with a '0' and file names with a '1' so folders are listed first
a2 = ((isFile(a)) ? "1" : "0") + a.toLowerCase();
b2 = ((isFile(b)) ? "1" : "0") + b.toLowerCase();
} else {
a2 = a.toLowerCase();
b2 = b.toLowerCase();
}
if (a2 === b2) {
return 0;
}
return (a2 > b2) ? 1 : -1;
}
/**
* Return an escaped path or URL string that can be broken near path separators.
*
* @param {string} url the path or URL to format
* @return {string} the formatted path or URL
*/
function breakableUrl(url) {
// This is for displaying in UI, so always want it escaped
var escUrl = _.escape(url);
// Inject zero-width space character (U+200B) near path separators (/) to allow line breaking there
return escUrl.replace(
new RegExp(regexEscape("/"), "g"),
"/" + "​"
);
}
/**
* Converts number of bytes into human readable format.
* If param bytes is negative it returns the number without any changes.
*
* @param {number} bytes Number of bytes to convert
* @param {number} precision Number of digits after the decimal separator
* @return {string}
*/
function prettyPrintBytes(bytes, precision) {
var kilobyte = 1024,
megabyte = kilobyte * 1024,
gigabyte = megabyte * 1024,
terabyte = gigabyte * 1024,
returnVal = bytes;
if ((bytes >= 0) && (bytes < kilobyte)) {
returnVal = bytes + " B";
} else if (bytes < megabyte) {
returnVal = (bytes / kilobyte).toFixed(precision) + " KB";
} else if (bytes < gigabyte) {
returnVal = (bytes / megabyte).toFixed(precision) + " MB";
} else if (bytes < terabyte) {
returnVal = (bytes / gigabyte).toFixed(precision) + " GB";
} else if (bytes >= terabyte) {
return (bytes / terabyte).toFixed(precision) + " TB";
}
return returnVal;
}
/**
* Truncate text to specified length.
*
* @param {string} str Text to be truncated.
* @param {number} len Length to which text should be truncated
* @return {?string} Returns truncated text only if it was changed
*/
function truncate(str, len) {
// Truncate text to specified length
if (str.length > len) {
str = str.substr(0, len);
// To prevent awkwardly truncating in the middle of a word,
// attempt to truncate at the end of the last whole word
var lastSpaceChar = str.lastIndexOf(" ");
if (lastSpaceChar < len && lastSpaceChar > -1) {
str = str.substr(0, lastSpaceChar);
}
return str;
}
}
/**
* Computes a 32bit hash from the given string
* Taken from http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
* @CC wiki attribution: esmiralha
* @param {string} str The string for which hash is to be computed
* @return {number} The 32-bit hash
*/
function hashCode(str) {
var hash = 0, i, chr, len;
if (str.length === 0) {
return hash;
}
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
/**
* Generates a random nonce string of the specified length.
*
* !!!Should not be used for crypto secure workflows.!!!
*
* @param {number} stringLength - The length of the nonce in bytes. default 10.
* @param {string} [prefix] - optional prefix
* @returns {string} - The randomly generated nonce.
*/
function randomString(stringLength=10, prefix="") {
const randomBuffer = new Uint8Array(stringLength);
crypto.getRandomValues(randomBuffer);
// Define the character set for the random string
const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
// Convert the ArrayBuffer to a case-sensitive random string with numbers
let randomId = prefix || '';
Array.from(randomBuffer).forEach(byte => {
randomId += charset[byte % charset.length];
});
return randomId;
}
/**
* Check if value is a valid number
*
* @param {string} value
* @returns {boolean} true if value is valid number, else false
*/
function isNumber(value) {
return parseFloat(value).toString() === value;
}
// Define public API
exports.format = format;
exports.regexEscape = regexEscape;
exports.jQueryIdEscape = jQueryIdEscape;
exports.getLines = getLines;
exports.offsetToLineNum = offsetToLineNum;
exports.urlSort = urlSort;
exports.breakableUrl = breakableUrl;
exports.startsWith = startsWith;
exports.endsWith = endsWith;
exports.prettyPrintBytes = prettyPrintBytes;
exports.truncate = truncate;
exports.hashCode = hashCode;
exports.randomString = randomString;
exports.isNumber = isNumber;
});