Spaces:
Running
Running
const log = require('./log'); | |
class StringUtil { | |
static withoutTrailingDigits (s) { | |
let i = s.length - 1; | |
while ((i >= 0) && ('0123456789'.indexOf(s.charAt(i)) > -1)) i--; | |
return s.slice(0, i + 1); | |
} | |
static unusedName (name, existingNames) { | |
if (existingNames.indexOf(name) < 0) return name; | |
name = StringUtil.withoutTrailingDigits(name); | |
let i = 2; | |
while (existingNames.indexOf(name + i) >= 0) i++; | |
return name + i; | |
} | |
/** | |
* Split a string on the first occurrence of a split character. | |
* @param {string} text - the string to split. | |
* @param {string} separator - split the text on this character. | |
* @returns {string[]} - the two parts of the split string, or [text, null] if no split character found. | |
* @example | |
* // returns ['foo', 'tar.gz'] | |
* splitFirst('foo.tar.gz', '.'); | |
* @example | |
* // returns ['foo', null] | |
* splitFirst('foo', '.'); | |
* @example | |
* // returns ['foo', ''] | |
* splitFirst('foo.', '.'); | |
*/ | |
static splitFirst (text, separator) { | |
const index = text.indexOf(separator); | |
if (index >= 0) { | |
return [text.substring(0, index), text.substring(index + 1)]; | |
} | |
return [text, null]; | |
} | |
/** | |
* A customized version of JSON.stringify that sets Infinity/NaN to 0, | |
* instead of the default (null). | |
* Needed because null is not of type number, but Infinity/NaN are, which | |
* can lead to serialization producing JSON that isn't valid based on the parser schema. | |
* It is also consistent with the behavior of saving 2.0 projects. | |
* This is only needed when stringifying an object for saving. | |
* | |
* @param {!object} obj - The object to serialize | |
* @return {!string} The JSON.stringified string with Infinity/NaN replaced with 0 | |
*/ | |
static stringify (obj) { | |
return JSON.stringify(obj, (_key, value) => { | |
if (typeof value === 'number' && | |
(value === Infinity || value === -Infinity || isNaN(value))){ | |
return 0; | |
} | |
return value; | |
}); | |
} | |
/** | |
* A function to replace unsafe characters (not allowed in XML) with safe ones. This is used | |
* in cases where we're replacing non-user facing strings (e.g. variable IDs). | |
* When replacing user facing strings, the xmlEscape utility function should be used | |
* instead so that the user facing string does not change how it displays. | |
* @param {!string | !Array.<string>} unsafe Unsafe string possibly containing unicode control characters. | |
* In some cases this argument may be an array (e.g. hacked inputs from 2.0) | |
* @return {string} String with control characters replaced. | |
*/ | |
static replaceUnsafeChars (unsafe) { | |
if (typeof unsafe !== 'string') { | |
if (Array.isArray(unsafe)) { | |
// This happens when we have hacked blocks from 2.0 | |
// See #1030 | |
unsafe = String(unsafe); | |
} else { | |
log.error('Unexpected input recieved in replaceUnsafeChars'); | |
return unsafe; | |
} | |
} | |
return unsafe.replace(/[<>&'"]/g, c => { | |
switch (c) { | |
case '<': return 'lt'; | |
case '>': return 'gt'; | |
case '&': return 'amp'; | |
case '\'': return 'apos'; | |
case '"': return 'quot'; | |
} | |
}); | |
} | |
} | |
module.exports = StringUtil; | |