PageRenderTime 63ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/files/underscore.string/2.3.1/underscore.string.js

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