define([ "./_base/kernel", // kernel.global "./_base/lang" ], function(kernel, lang){ // module: // dojo/string var ESCAPE_REGEXP = /[&<>'"\/]/g; var ESCAPE_MAP = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/' }; var string = { // summary: // String utilities for Dojo }; lang.setObject("dojo.string", string); string.escape = function(/*String*/str){ // summary: // Efficiently escape a string for insertion into HTML (innerHTML or attributes), replacing &, <, >, ", ', and / characters. // str: // the string to escape if(!str){ return ""; } return str.replace(ESCAPE_REGEXP, function(c) { return ESCAPE_MAP[c]; }); }; // Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt#Polyfill string.codePointAt = String.prototype.codePointAt ? function (str, position) { return String.prototype.codePointAt.call(str, position); } : function(str, position) { if (str == null) { throw new TypeError('codePointAt called on null or undefined'); } var size; var first; var second; var index; str = String(str); size = str.length; // `ToInteger` index = position ? Number(position) : 0; if (index != index) { // better `isNaN` index = 0; } // Account for out-of-bounds indices: if (index < 0 || index >= size) { return undefined; } // Get the first code unit first = str.charCodeAt(index); // check if it's the start of a surrogate pair if (first >= 0xD800 && first <= 0xDBFF && // high surrogate size > index + 1 // there is a next code unit ) { second = str.charCodeAt(index + 1); if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } } return first; }; // Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint#Polyfill string.fromCodePoint = String.fromCodePoint || function () { var codeUnits = []; var codeLen = 0; var result = ""; var codePoint; var index; for (index = 0, len = arguments.length; index !== len; ++index) { codePoint = +arguments[index]; // correctly handles all cases including `NaN`, `-Infinity`, `+Infinity` // The surrounding `!(...)` is required to correctly handle `NaN` cases // The (codePoint>>>0) === codePoint clause handles decimals and negatives if (!(codePoint < 0x10FFFF && (codePoint>>>0) === codePoint)) { throw RangeError("Invalid code point: " + codePoint); } if (codePoint <= 0xFFFF) { // BMP code point codeLen = codeUnits.push(codePoint); } else { // Astral code point; split in surrogate halves // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae codePoint -= 0x10000; codeLen = codeUnits.push( (codePoint >> 10) + 0xD800, // highSurrogate (codePoint % 0x400) + 0xDC00 // lowSurrogate ); } if (codeLen >= 0x3fff) { result += String.fromCharCode.apply(null, codeUnits); codeUnits.length = 0; } } return result + String.fromCharCode.apply(null, codeUnits); }; string.rep = function(/*String*/str, /*Integer*/num){ // summary: // Efficiently replicate a string `n` times. // str: // the string to replicate // num: // number of times to replicate the string if(num <= 0 || !str){ return ""; } var buf = []; for(;;){ if(num & 1){ buf.push(str); } if(!(num >>= 1)){ break; } str += str; } return buf.join(""); // String }; string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){ // summary: // Pad a string to guarantee that it is at least `size` length by // filling with the character `ch` at either the start or end of the // string. Pads at the start, by default. // text: // the string to pad // size: // length to provide padding // ch: // character to pad, defaults to '0' // end: // adds padding at the end if true, otherwise pads at start // example: // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++". // | string.pad("Dojo", 10, "+", true); if(!ch){ ch = '0'; } var out = String(text), pad = string.rep(ch, Math.ceil((size - out.length) / ch.length)); return end ? out + pad : pad + out; // String }; string.substitute = function( /*String*/ template, /*Object|Array*/map, /*Function?*/ transform, /*Object?*/ thisObject){ // summary: // Performs parameterized substitutions on a string. Throws an // exception if any parameter is unmatched. // template: // a string with expressions in the form `${key}` to be replaced or // `${key:format}` which specifies a format function. keys are case-sensitive. // The special sequence `${}` can be used escape `$`. // map: // hash to search for substitutions // transform: // a function to process all parameters before substitution takes // place, e.g. mylib.encodeXML // thisObject: // where to look for optional format function; default to the global // namespace // example: // Substitutes two expressions in a string from an Array or Object // | // returns "File 'foo.html' is not found in directory '/temp'." // | // by providing substitution data in an Array // | string.substitute( // | "File '${0}' is not found in directory '${1}'.", // | ["foo.html","/temp"] // | ); // | // | // also returns "File 'foo.html' is not found in directory '/temp'." // | // but provides substitution data in an Object structure. Dotted // | // notation may be used to traverse the structure. // | string.substitute( // | "File '${name}' is not found in directory '${info.dir}'.", // | { name: "foo.html", info: { dir: "/temp" } } // | ); // example: // Use a transform function to modify the values: // | // returns "file 'foo.html' is not found in directory '/temp'." // | string.substitute( // | "${0} is not found in ${1}.", // | ["foo.html","/temp"], // | function(str){ // | // try to figure out the type // | var prefix = (str.charAt(0) == "/") ? "directory": "file"; // | return prefix + " '" + str + "'"; // | } // | ); // example: // Use a formatter // | // returns "thinger -- howdy" // | string.substitute( // | "${0:postfix}", ["thinger"], null, { // | postfix: function(value, key){ // | return value + " -- howdy"; // | } // | } // | ); thisObject = thisObject || kernel.global; transform = transform ? lang.hitch(thisObject, transform) : function(v){ return v; }; return template.replace(/\$\{([^\s\:\}]*)(?:\:([^\s\:\}]+))?\}/g, function(match, key, format){ if (key == ''){ return '$'; } var value = lang.getObject(key, false, map); if(format){ value = lang.getObject(format, false, thisObject).call(thisObject, value, key); } var result = transform(value, key); if (typeof result === 'undefined') { throw new Error('string.substitute could not find key "' + key + '" in template'); } return result.toString(); }); // String }; string.trim = String.prototype.trim ? lang.trim : // aliasing to the native function function(str){ str = str.replace(/^\s+/, ''); for(var i = str.length - 1; i >= 0; i--){ if(/\S/.test(str.charAt(i))){ str = str.substring(0, i + 1); break; } } return str; }; /*===== string.trim = function(str){ // summary: // Trims whitespace from both sides of the string // str: String // String to be trimmed // returns: String // Returns the trimmed string // description: // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript). // The short yet performant version of this function is dojo/_base/lang.trim(), // which is part of Dojo base. Uses String.prototype.trim instead, if available. return ""; // String }; =====*/ return string; });