PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/files/underscore.string/2.3.3/underscore.string.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 673 lines | 556 code | 96 blank | 21 comment | 145 complexity | 09ae9f95958c0d548b4390fd224963a1 MD5 | raw file
  1. // Underscore.string
  2. // (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
  3. // Underscore.string is freely distributable under the terms of the MIT license.
  4. // Documentation: https://github.com/epeli/underscore.string
  5. // Some code is borrowed from MooTools and Alexandru Marasteanu.
  6. // Version '2.3.3'
  7. !function(root, String){
  8. 'use strict';
  9. // Defining helper functions.
  10. var nativeTrim = String.prototype.trim;
  11. var nativeTrimRight = String.prototype.trimRight;
  12. var nativeTrimLeft = String.prototype.trimLeft;
  13. var parseNumber = function(source) { return source * 1 || 0; };
  14. var strRepeat = function(str, qty){
  15. if (qty < 1) return '';
  16. var result = '';
  17. while (qty > 0) {
  18. if (qty & 1) result += str;
  19. qty >>= 1, str += str;
  20. }
  21. return result;
  22. };
  23. var slice = [].slice;
  24. var defaultToWhiteSpace = function(characters) {
  25. if (characters == null)
  26. return '\\s';
  27. else if (characters.source)
  28. return characters.source;
  29. else
  30. return '[' + _s.escapeRegExp(characters) + ']';
  31. };
  32. // Helper for toBoolean
  33. function boolMatch(s, matchers) {
  34. var i, matcher, down = s.toLowerCase();
  35. matchers = [].concat(matchers);
  36. for (i = 0; i < matchers.length; i += 1) {
  37. matcher = matchers[i];
  38. if (!matcher) continue;
  39. if (matcher.test && matcher.test(s)) return true;
  40. if (matcher.toLowerCase() === down) return true;
  41. }
  42. }
  43. var escapeChars = {
  44. lt: '<',
  45. gt: '>',
  46. quot: '"',
  47. amp: '&',
  48. apos: "'"
  49. };
  50. var reversedEscapeChars = {};
  51. for(var key in escapeChars) reversedEscapeChars[escapeChars[key]] = key;
  52. reversedEscapeChars["'"] = '#39';
  53. // sprintf() for JavaScript 0.7-beta1
  54. // http://www.diveintojavascript.com/projects/javascript-sprintf
  55. //
  56. // Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
  57. // All rights reserved.
  58. var sprintf = (function() {
  59. function get_type(variable) {
  60. return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
  61. }
  62. var str_repeat = strRepeat;
  63. var str_format = function() {
  64. if (!str_format.cache.hasOwnProperty(arguments[0])) {
  65. str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
  66. }
  67. return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
  68. };
  69. str_format.format = function(parse_tree, argv) {
  70. var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
  71. for (i = 0; i < tree_length; i++) {
  72. node_type = get_type(parse_tree[i]);
  73. if (node_type === 'string') {
  74. output.push(parse_tree[i]);
  75. }
  76. else if (node_type === 'array') {
  77. match = parse_tree[i]; // convenience purposes only
  78. if (match[2]) { // keyword argument
  79. arg = argv[cursor];
  80. for (k = 0; k < match[2].length; k++) {
  81. if (!arg.hasOwnProperty(match[2][k])) {
  82. throw new Error(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
  83. }
  84. arg = arg[match[2][k]];
  85. }
  86. } else if (match[1]) { // positional argument (explicit)
  87. arg = argv[match[1]];
  88. }
  89. else { // positional argument (implicit)
  90. arg = argv[cursor++];
  91. }
  92. if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
  93. throw new Error(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
  94. }
  95. switch (match[8]) {
  96. case 'b': arg = arg.toString(2); break;
  97. case 'c': arg = String.fromCharCode(arg); break;
  98. case 'd': arg = parseInt(arg, 10); break;
  99. case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
  100. case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
  101. case 'o': arg = arg.toString(8); break;
  102. case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
  103. case 'u': arg = Math.abs(arg); break;
  104. case 'x': arg = arg.toString(16); break;
  105. case 'X': arg = arg.toString(16).toUpperCase(); break;
  106. }
  107. arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
  108. pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
  109. pad_length = match[6] - String(arg).length;
  110. pad = match[6] ? str_repeat(pad_character, pad_length) : '';
  111. output.push(match[5] ? arg + pad : pad + arg);
  112. }
  113. }
  114. return output.join('');
  115. };
  116. str_format.cache = {};
  117. str_format.parse = function(fmt) {
  118. var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
  119. while (_fmt) {
  120. if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
  121. parse_tree.push(match[0]);
  122. }
  123. else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
  124. parse_tree.push('%');
  125. }
  126. else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
  127. if (match[2]) {
  128. arg_names |= 1;
  129. var field_list = [], replacement_field = match[2], field_match = [];
  130. if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  131. field_list.push(field_match[1]);
  132. while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
  133. if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  134. field_list.push(field_match[1]);
  135. }
  136. else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
  137. field_list.push(field_match[1]);
  138. }
  139. else {
  140. throw new Error('[_.sprintf] huh?');
  141. }
  142. }
  143. }
  144. else {
  145. throw new Error('[_.sprintf] huh?');
  146. }
  147. match[2] = field_list;
  148. }
  149. else {
  150. arg_names |= 2;
  151. }
  152. if (arg_names === 3) {
  153. throw new Error('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
  154. }
  155. parse_tree.push(match);
  156. }
  157. else {
  158. throw new Error('[_.sprintf] huh?');
  159. }
  160. _fmt = _fmt.substring(match[0].length);
  161. }
  162. return parse_tree;
  163. };
  164. return str_format;
  165. })();
  166. // Defining underscore.string
  167. var _s = {
  168. VERSION: '2.3.0',
  169. isBlank: function(str){
  170. if (str == null) str = '';
  171. return (/^\s*$/).test(str);
  172. },
  173. stripTags: function(str){
  174. if (str == null) return '';
  175. return String(str).replace(/<\/?[^>]+>/g, '');
  176. },
  177. capitalize : function(str){
  178. str = str == null ? '' : String(str);
  179. return str.charAt(0).toUpperCase() + str.slice(1);
  180. },
  181. chop: function(str, step){
  182. if (str == null) return [];
  183. str = String(str);
  184. step = ~~step;
  185. return step > 0 ? str.match(new RegExp('.{1,' + step + '}', 'g')) : [str];
  186. },
  187. clean: function(str){
  188. return _s.strip(str).replace(/\s+/g, ' ');
  189. },
  190. count: function(str, substr){
  191. if (str == null || substr == null) return 0;
  192. str = String(str);
  193. substr = String(substr);
  194. var count = 0,
  195. pos = 0,
  196. length = substr.length;
  197. while (true) {
  198. pos = str.indexOf(substr, pos);
  199. if (pos === -1) break;
  200. count++;
  201. pos += length;
  202. }
  203. return count;
  204. },
  205. chars: function(str) {
  206. if (str == null) return [];
  207. return String(str).split('');
  208. },
  209. swapCase: function(str) {
  210. if (str == null) return '';
  211. return String(str).replace(/\S/g, function(c){
  212. return c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase();
  213. });
  214. },
  215. escapeHTML: function(str) {
  216. if (str == null) return '';
  217. return String(str).replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; });
  218. },
  219. unescapeHTML: function(str) {
  220. if (str == null) return '';
  221. return String(str).replace(/\&([^;]+);/g, function(entity, entityCode){
  222. var match;
  223. if (entityCode in escapeChars) {
  224. return escapeChars[entityCode];
  225. } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
  226. return String.fromCharCode(parseInt(match[1], 16));
  227. } else if (match = entityCode.match(/^#(\d+)$/)) {
  228. return String.fromCharCode(~~match[1]);
  229. } else {
  230. return entity;
  231. }
  232. });
  233. },
  234. escapeRegExp: function(str){
  235. if (str == null) return '';
  236. return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
  237. },
  238. splice: function(str, i, howmany, substr){
  239. var arr = _s.chars(str);
  240. arr.splice(~~i, ~~howmany, substr);
  241. return arr.join('');
  242. },
  243. insert: function(str, i, substr){
  244. return _s.splice(str, i, 0, substr);
  245. },
  246. include: function(str, needle){
  247. if (needle === '') return true;
  248. if (str == null) return false;
  249. return String(str).indexOf(needle) !== -1;
  250. },
  251. join: function() {
  252. var args = slice.call(arguments),
  253. separator = args.shift();
  254. if (separator == null) separator = '';
  255. return args.join(separator);
  256. },
  257. lines: function(str) {
  258. if (str == null) return [];
  259. return String(str).split("\n");
  260. },
  261. reverse: function(str){
  262. return _s.chars(str).reverse().join('');
  263. },
  264. startsWith: function(str, starts){
  265. if (starts === '') return true;
  266. if (str == null || starts == null) return false;
  267. str = String(str); starts = String(starts);
  268. return str.length >= starts.length && str.slice(0, starts.length) === starts;
  269. },
  270. endsWith: function(str, ends){
  271. if (ends === '') return true;
  272. if (str == null || ends == null) return false;
  273. str = String(str); ends = String(ends);
  274. return str.length >= ends.length && str.slice(str.length - ends.length) === ends;
  275. },
  276. succ: function(str){
  277. if (str == null) return '';
  278. str = String(str);
  279. return str.slice(0, -1) + String.fromCharCode(str.charCodeAt(str.length-1) + 1);
  280. },
  281. titleize: function(str){
  282. if (str == null) return '';
  283. str = String(str).toLowerCase();
  284. return str.replace(/(?:^|\s|-)\S/g, function(c){ return c.toUpperCase(); });
  285. },
  286. camelize: function(str){
  287. return _s.trim(str).replace(/[-_\s]+(.)?/g, function(match, c){ return c ? c.toUpperCase() : ""; });
  288. },
  289. underscored: function(str){
  290. return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
  291. },
  292. dasherize: function(str){
  293. return _s.trim(str).replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
  294. },
  295. classify: function(str){
  296. return _s.titleize(String(str).replace(/[\W_]/g, ' ')).replace(/\s/g, '');
  297. },
  298. humanize: function(str){
  299. return _s.capitalize(_s.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
  300. },
  301. trim: function(str, characters){
  302. if (str == null) return '';
  303. if (!characters && nativeTrim) return nativeTrim.call(str);
  304. characters = defaultToWhiteSpace(characters);
  305. return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
  306. },
  307. ltrim: function(str, characters){
  308. if (str == null) return '';
  309. if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str);
  310. characters = defaultToWhiteSpace(characters);
  311. return String(str).replace(new RegExp('^' + characters + '+'), '');
  312. },
  313. rtrim: function(str, characters){
  314. if (str == null) return '';
  315. if (!characters && nativeTrimRight) return nativeTrimRight.call(str);
  316. characters = defaultToWhiteSpace(characters);
  317. return String(str).replace(new RegExp(characters + '+$'), '');
  318. },
  319. truncate: function(str, length, truncateStr){
  320. if (str == null) return '';
  321. str = String(str); truncateStr = truncateStr || '...';
  322. length = ~~length;
  323. return str.length > length ? str.slice(0, length) + truncateStr : str;
  324. },
  325. /**
  326. * _s.prune: a more elegant version of truncate
  327. * prune extra chars, never leaving a half-chopped word.
  328. * @author github.com/rwz
  329. */
  330. prune: function(str, length, pruneStr){
  331. if (str == null) return '';
  332. str = String(str); length = ~~length;
  333. pruneStr = pruneStr != null ? String(pruneStr) : '...';
  334. if (str.length <= length) return str;
  335. var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
  336. template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
  337. if (template.slice(template.length-2).match(/\w\w/))
  338. template = template.replace(/\s*\S+$/, '');
  339. else
  340. template = _s.rtrim(template.slice(0, template.length-1));
  341. return (template+pruneStr).length > str.length ? str : str.slice(0, template.length)+pruneStr;
  342. },
  343. words: function(str, delimiter) {
  344. if (_s.isBlank(str)) return [];
  345. return _s.trim(str, delimiter).split(delimiter || /\s+/);
  346. },
  347. pad: function(str, length, padStr, type) {
  348. str = str == null ? '' : String(str);
  349. length = ~~length;
  350. var padlen = 0;
  351. if (!padStr)
  352. padStr = ' ';
  353. else if (padStr.length > 1)
  354. padStr = padStr.charAt(0);
  355. switch(type) {
  356. case 'right':
  357. padlen = length - str.length;
  358. return str + strRepeat(padStr, padlen);
  359. case 'both':
  360. padlen = length - str.length;
  361. return strRepeat(padStr, Math.ceil(padlen/2)) + str
  362. + strRepeat(padStr, Math.floor(padlen/2));
  363. default: // 'left'
  364. padlen = length - str.length;
  365. return strRepeat(padStr, padlen) + str;
  366. }
  367. },
  368. lpad: function(str, length, padStr) {
  369. return _s.pad(str, length, padStr);
  370. },
  371. rpad: function(str, length, padStr) {
  372. return _s.pad(str, length, padStr, 'right');
  373. },
  374. lrpad: function(str, length, padStr) {
  375. return _s.pad(str, length, padStr, 'both');
  376. },
  377. sprintf: sprintf,
  378. vsprintf: function(fmt, argv){
  379. argv.unshift(fmt);
  380. return sprintf.apply(null, argv);
  381. },
  382. toNumber: function(str, decimals) {
  383. if (!str) return 0;
  384. str = _s.trim(str);
  385. if (!str.match(/^-?\d+(?:\.\d+)?$/)) return NaN;
  386. return parseNumber(parseNumber(str).toFixed(~~decimals));
  387. },
  388. numberFormat : function(number, dec, dsep, tsep) {
  389. if (isNaN(number) || number == null) return '';
  390. number = number.toFixed(~~dec);
  391. tsep = typeof tsep == 'string' ? tsep : ',';
  392. var parts = number.split('.'), fnums = parts[0],
  393. decimals = parts[1] ? (dsep || '.') + parts[1] : '';
  394. return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + tsep) + decimals;
  395. },
  396. strRight: function(str, sep){
  397. if (str == null) return '';
  398. str = String(str); sep = sep != null ? String(sep) : sep;
  399. var pos = !sep ? -1 : str.indexOf(sep);
  400. return ~pos ? str.slice(pos+sep.length, str.length) : str;
  401. },
  402. strRightBack: function(str, sep){
  403. if (str == null) return '';
  404. str = String(str); sep = sep != null ? String(sep) : sep;
  405. var pos = !sep ? -1 : str.lastIndexOf(sep);
  406. return ~pos ? str.slice(pos+sep.length, str.length) : str;
  407. },
  408. strLeft: function(str, sep){
  409. if (str == null) return '';
  410. str = String(str); sep = sep != null ? String(sep) : sep;
  411. var pos = !sep ? -1 : str.indexOf(sep);
  412. return ~pos ? str.slice(0, pos) : str;
  413. },
  414. strLeftBack: function(str, sep){
  415. if (str == null) return '';
  416. str += ''; sep = sep != null ? ''+sep : sep;
  417. var pos = str.lastIndexOf(sep);
  418. return ~pos ? str.slice(0, pos) : str;
  419. },
  420. toSentence: function(array, separator, lastSeparator, serial) {
  421. separator = separator || ', ';
  422. lastSeparator = lastSeparator || ' and ';
  423. var a = array.slice(), lastMember = a.pop();
  424. if (array.length > 2 && serial) lastSeparator = _s.rtrim(separator) + lastSeparator;
  425. return a.length ? a.join(separator) + lastSeparator + lastMember : lastMember;
  426. },
  427. toSentenceSerial: function() {
  428. var args = slice.call(arguments);
  429. args[3] = true;
  430. return _s.toSentence.apply(_s, args);
  431. },
  432. slugify: function(str) {
  433. if (str == null) return '';
  434. var from = "ąàáäâãåæăćęèéëêìíïîłńòóöôõøśșțùúüûñçżź",
  435. to = "aaaaaaaaaceeeeeiiiilnoooooosstuuuunczz",
  436. regex = new RegExp(defaultToWhiteSpace(from), 'g');
  437. str = String(str).toLowerCase().replace(regex, function(c){
  438. var index = from.indexOf(c);
  439. return to.charAt(index) || '-';
  440. });
  441. return _s.dasherize(str.replace(/[^\w\s-]/g, ''));
  442. },
  443. surround: function(str, wrapper) {
  444. return [wrapper, str, wrapper].join('');
  445. },
  446. quote: function(str, quoteChar) {
  447. return _s.surround(str, quoteChar || '"');
  448. },
  449. unquote: function(str, quoteChar) {
  450. quoteChar = quoteChar || '"';
  451. if (str[0] === quoteChar && str[str.length-1] === quoteChar)
  452. return str.slice(1,str.length-1);
  453. else return str;
  454. },
  455. exports: function() {
  456. var result = {};
  457. for (var prop in this) {
  458. if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue;
  459. result[prop] = this[prop];
  460. }
  461. return result;
  462. },
  463. repeat: function(str, qty, separator){
  464. if (str == null) return '';
  465. qty = ~~qty;
  466. // using faster implementation if separator is not needed;
  467. if (separator == null) return strRepeat(String(str), qty);
  468. // this one is about 300x slower in Google Chrome
  469. for (var repeat = []; qty > 0; repeat[--qty] = str) {}
  470. return repeat.join(separator);
  471. },
  472. naturalCmp: function(str1, str2){
  473. if (str1 == str2) return 0;
  474. if (!str1) return -1;
  475. if (!str2) return 1;
  476. var cmpRegex = /(\.\d+)|(\d+)|(\D+)/g,
  477. tokens1 = String(str1).toLowerCase().match(cmpRegex),
  478. tokens2 = String(str2).toLowerCase().match(cmpRegex),
  479. count = Math.min(tokens1.length, tokens2.length);
  480. for(var i = 0; i < count; i++) {
  481. var a = tokens1[i], b = tokens2[i];
  482. if (a !== b){
  483. var num1 = parseInt(a, 10);
  484. if (!isNaN(num1)){
  485. var num2 = parseInt(b, 10);
  486. if (!isNaN(num2) && num1 - num2)
  487. return num1 - num2;
  488. }
  489. return a < b ? -1 : 1;
  490. }
  491. }
  492. if (tokens1.length === tokens2.length)
  493. return tokens1.length - tokens2.length;
  494. return str1 < str2 ? -1 : 1;
  495. },
  496. levenshtein: function(str1, str2) {
  497. if (str1 == null && str2 == null) return 0;
  498. if (str1 == null) return String(str2).length;
  499. if (str2 == null) return String(str1).length;
  500. str1 = String(str1); str2 = String(str2);
  501. var current = [], prev, value;
  502. for (var i = 0; i <= str2.length; i++)
  503. for (var j = 0; j <= str1.length; j++) {
  504. if (i && j)
  505. if (str1.charAt(j - 1) === str2.charAt(i - 1))
  506. value = prev;
  507. else
  508. value = Math.min(current[j], current[j - 1], prev) + 1;
  509. else
  510. value = i + j;
  511. prev = current[j];
  512. current[j] = value;
  513. }
  514. return current.pop();
  515. },
  516. toBoolean: function(str, trueValues, falseValues) {
  517. if (typeof str === "number") str = "" + str;
  518. if (typeof str !== "string") return !!str;
  519. str = _s.trim(str);
  520. if (boolMatch(str, trueValues || ["true", "1"])) return true;
  521. if (boolMatch(str, falseValues || ["false", "0"])) return false;
  522. }
  523. };
  524. // Aliases
  525. _s.strip = _s.trim;
  526. _s.lstrip = _s.ltrim;
  527. _s.rstrip = _s.rtrim;
  528. _s.center = _s.lrpad;
  529. _s.rjust = _s.lpad;
  530. _s.ljust = _s.rpad;
  531. _s.contains = _s.include;
  532. _s.q = _s.quote;
  533. _s.toBool = _s.toBoolean;
  534. // Exporting
  535. // CommonJS module is defined
  536. if (typeof exports !== 'undefined') {
  537. if (typeof module !== 'undefined' && module.exports)
  538. module.exports = _s;
  539. exports._s = _s;
  540. }
  541. // Register as a named module with AMD.
  542. if (typeof define === 'function' && define.amd)
  543. define('underscore.string', [], function(){ return _s; });
  544. // Integrate with Underscore.js if defined
  545. // or create our own underscore object.
  546. root._ = root._ || {};
  547. root._.string = root._.str = _s;
  548. }(this, String);