underscore-plus.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. (function() {
  2. var isEqual, isPlainObject, macModifierKeyMap, nonMacModifierKeyMap, plus, shiftKeyMap, splitKeyPath, _,
  3. __slice = [].slice;
  4. _ = require('underscore');
  5. macModifierKeyMap = {
  6. cmd: '\u2318',
  7. ctrl: '\u2303',
  8. alt: '\u2325',
  9. option: '\u2325',
  10. shift: '\u21e7',
  11. enter: '\u23ce',
  12. left: '\u2190',
  13. right: '\u2192',
  14. up: '\u2191',
  15. down: '\u2193'
  16. };
  17. nonMacModifierKeyMap = {
  18. cmd: 'Cmd',
  19. ctrl: 'Ctrl',
  20. alt: 'Alt',
  21. option: 'Alt',
  22. shift: 'Shift',
  23. enter: 'Enter',
  24. left: 'Left',
  25. right: 'Right',
  26. up: 'Up',
  27. down: 'Down'
  28. };
  29. shiftKeyMap = {
  30. '~': '`',
  31. '_': '-',
  32. '+': '=',
  33. '|': '\\',
  34. '{': '[',
  35. '}': ']',
  36. ':': ';',
  37. '"': '\'',
  38. '<': ',',
  39. '>': '.',
  40. '?': '/'
  41. };
  42. splitKeyPath = function(keyPath) {
  43. var char, i, keyPathArray, startIndex, _i, _len;
  44. startIndex = 0;
  45. keyPathArray = [];
  46. if (keyPath == null) {
  47. return keyPathArray;
  48. }
  49. for (i = _i = 0, _len = keyPath.length; _i < _len; i = ++_i) {
  50. char = keyPath[i];
  51. if (char === '.' && (i === 0 || keyPath[i - 1] !== '\\')) {
  52. keyPathArray.push(keyPath.substring(startIndex, i));
  53. startIndex = i + 1;
  54. }
  55. }
  56. keyPathArray.push(keyPath.substr(startIndex, keyPath.length));
  57. return keyPathArray;
  58. };
  59. isPlainObject = function(value) {
  60. return _.isObject(value) && !_.isArray(value);
  61. };
  62. plus = {
  63. adviseBefore: function(object, methodName, advice) {
  64. var original;
  65. original = object[methodName];
  66. return object[methodName] = function() {
  67. var args;
  68. args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  69. if (advice.apply(this, args) !== false) {
  70. return original.apply(this, args);
  71. }
  72. };
  73. },
  74. camelize: function(string) {
  75. if (string) {
  76. return string.replace(/[_-]+(\w)/g, function(m) {
  77. return m[1].toUpperCase();
  78. });
  79. } else {
  80. return '';
  81. }
  82. },
  83. capitalize: function(word) {
  84. if (!word) {
  85. return '';
  86. }
  87. if (word.toLowerCase() === 'github') {
  88. return 'GitHub';
  89. } else {
  90. return word[0].toUpperCase() + word.slice(1);
  91. }
  92. },
  93. compactObject: function(object) {
  94. var key, newObject, value;
  95. newObject = {};
  96. for (key in object) {
  97. value = object[key];
  98. if (value != null) {
  99. newObject[key] = value;
  100. }
  101. }
  102. return newObject;
  103. },
  104. dasherize: function(string) {
  105. if (!string) {
  106. return '';
  107. }
  108. string = string[0].toLowerCase() + string.slice(1);
  109. return string.replace(/([A-Z])|(_)/g, function(m, letter) {
  110. if (letter) {
  111. return "-" + letter.toLowerCase();
  112. } else {
  113. return "-";
  114. }
  115. });
  116. },
  117. deepClone: function(object) {
  118. if (_.isArray(object)) {
  119. return object.map(function(value) {
  120. return plus.deepClone(value);
  121. });
  122. } else if (_.isObject(object) && !_.isFunction(object)) {
  123. return plus.mapObject(object, (function(_this) {
  124. return function(key, value) {
  125. return [key, plus.deepClone(value)];
  126. };
  127. })(this));
  128. } else {
  129. return object;
  130. }
  131. },
  132. deepExtend: function(target) {
  133. var i, key, object, result, _i, _len, _ref;
  134. result = target;
  135. i = 0;
  136. while (++i < arguments.length) {
  137. object = arguments[i];
  138. if (isPlainObject(result) && isPlainObject(object)) {
  139. _ref = Object.keys(object);
  140. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  141. key = _ref[_i];
  142. result[key] = plus.deepExtend(result[key], object[key]);
  143. }
  144. } else {
  145. result = plus.deepClone(object);
  146. }
  147. }
  148. return result;
  149. },
  150. deepContains: function(array, target) {
  151. var object, _i, _len;
  152. if (array == null) {
  153. return false;
  154. }
  155. for (_i = 0, _len = array.length; _i < _len; _i++) {
  156. object = array[_i];
  157. if (_.isEqual(object, target)) {
  158. return true;
  159. }
  160. }
  161. return false;
  162. },
  163. endsWith: function(string, suffix) {
  164. if (suffix == null) {
  165. suffix = '';
  166. }
  167. if (string) {
  168. return string.indexOf(suffix, string.length - suffix.length) !== -1;
  169. } else {
  170. return false;
  171. }
  172. },
  173. escapeAttribute: function(string) {
  174. if (string) {
  175. return string.replace(/"/g, '&quot;').replace(/\n/g, '').replace(/\\/g, '-');
  176. } else {
  177. return '';
  178. }
  179. },
  180. escapeRegExp: function(string) {
  181. if (string) {
  182. return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  183. } else {
  184. return '';
  185. }
  186. },
  187. humanizeEventName: function(eventName, eventDoc) {
  188. var event, namespace, namespaceDoc, _ref;
  189. _ref = eventName.split(':'), namespace = _ref[0], event = _ref[1];
  190. if (event == null) {
  191. return plus.undasherize(namespace);
  192. }
  193. namespaceDoc = plus.undasherize(namespace);
  194. if (eventDoc == null) {
  195. eventDoc = plus.undasherize(event);
  196. }
  197. return "" + namespaceDoc + ": " + eventDoc;
  198. },
  199. humanizeKey: function(key, platform) {
  200. var modifierKeyMap;
  201. if (platform == null) {
  202. platform = process.platform;
  203. }
  204. if (!key) {
  205. return key;
  206. }
  207. modifierKeyMap = platform === 'darwin' ? macModifierKeyMap : nonMacModifierKeyMap;
  208. if (modifierKeyMap[key]) {
  209. return modifierKeyMap[key];
  210. } else if (key.length === 1 && (shiftKeyMap[key] != null)) {
  211. return [modifierKeyMap.shift, shiftKeyMap[key]];
  212. } else if (key.length === 1 && key === key.toUpperCase() && key.toUpperCase() !== key.toLowerCase()) {
  213. return [modifierKeyMap.shift, key.toUpperCase()];
  214. } else if (key.length === 1 || /f[0-9]{1,2}/.test(key)) {
  215. return key.toUpperCase();
  216. } else {
  217. if (platform === 'darwin') {
  218. return key;
  219. } else {
  220. return plus.capitalize(key);
  221. }
  222. }
  223. },
  224. humanizeKeystroke: function(keystroke, platform) {
  225. var humanizedKeystrokes, index, key, keys, keystrokes, splitKeystroke, _i, _j, _len, _len1;
  226. if (platform == null) {
  227. platform = process.platform;
  228. }
  229. if (!keystroke) {
  230. return keystroke;
  231. }
  232. keystrokes = keystroke.split(' ');
  233. humanizedKeystrokes = [];
  234. for (_i = 0, _len = keystrokes.length; _i < _len; _i++) {
  235. keystroke = keystrokes[_i];
  236. keys = [];
  237. splitKeystroke = keystroke.split('-');
  238. for (index = _j = 0, _len1 = splitKeystroke.length; _j < _len1; index = ++_j) {
  239. key = splitKeystroke[index];
  240. if (key === '' && splitKeystroke[index - 1] === '') {
  241. key = '-';
  242. }
  243. if (key) {
  244. keys.push(plus.humanizeKey(key, platform));
  245. }
  246. }
  247. keys = _.uniq(_.flatten(keys));
  248. if (platform === 'darwin') {
  249. keys = keys.join('');
  250. } else {
  251. keys = keys.join('+');
  252. }
  253. humanizedKeystrokes.push(keys);
  254. }
  255. return humanizedKeystrokes.join(' ');
  256. },
  257. isSubset: function(potentialSubset, potentialSuperset) {
  258. return _.every(potentialSubset, function(element) {
  259. return _.include(potentialSuperset, element);
  260. });
  261. },
  262. losslessInvert: function(hash) {
  263. var inverted, key, value;
  264. inverted = {};
  265. for (key in hash) {
  266. value = hash[key];
  267. if (inverted[value] == null) {
  268. inverted[value] = [];
  269. }
  270. inverted[value].push(key);
  271. }
  272. return inverted;
  273. },
  274. mapObject: function(object, iterator) {
  275. var key, newObject, value, _i, _len, _ref, _ref1;
  276. newObject = {};
  277. _ref = Object.keys(object);
  278. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  279. key = _ref[_i];
  280. _ref1 = iterator(key, object[key]), key = _ref1[0], value = _ref1[1];
  281. newObject[key] = value;
  282. }
  283. return newObject;
  284. },
  285. multiplyString: function(string, n) {
  286. var finalString, i;
  287. finalString = "";
  288. i = 0;
  289. while (i < n) {
  290. finalString += string;
  291. i++;
  292. }
  293. return finalString;
  294. },
  295. pluralize: function(count, singular, plural) {
  296. if (count == null) {
  297. count = 0;
  298. }
  299. if (plural == null) {
  300. plural = singular + 's';
  301. }
  302. if (count === 1) {
  303. return "" + count + " " + singular;
  304. } else {
  305. return "" + count + " " + plural;
  306. }
  307. },
  308. remove: function(array, element) {
  309. var index;
  310. index = array.indexOf(element);
  311. if (index >= 0) {
  312. array.splice(index, 1);
  313. }
  314. return array;
  315. },
  316. setValueForKeyPath: function(object, keyPath, value) {
  317. var key, keys;
  318. keys = splitKeyPath(keyPath);
  319. while (keys.length > 1) {
  320. key = keys.shift();
  321. if (object[key] == null) {
  322. object[key] = {};
  323. }
  324. object = object[key];
  325. }
  326. if (value != null) {
  327. return object[keys.shift()] = value;
  328. } else {
  329. return delete object[keys.shift()];
  330. }
  331. },
  332. hasKeyPath: function(object, keyPath) {
  333. var key, keys, _i, _len;
  334. keys = splitKeyPath(keyPath);
  335. for (_i = 0, _len = keys.length; _i < _len; _i++) {
  336. key = keys[_i];
  337. if (!object.hasOwnProperty(key)) {
  338. return false;
  339. }
  340. object = object[key];
  341. }
  342. return true;
  343. },
  344. spliceWithArray: function(originalArray, start, length, insertedArray, chunkSize) {
  345. var chunkStart, _i, _ref, _results;
  346. if (chunkSize == null) {
  347. chunkSize = 100000;
  348. }
  349. if (insertedArray.length < chunkSize) {
  350. return originalArray.splice.apply(originalArray, [start, length].concat(__slice.call(insertedArray)));
  351. } else {
  352. originalArray.splice(start, length);
  353. _results = [];
  354. for (chunkStart = _i = 0, _ref = insertedArray.length; chunkSize > 0 ? _i <= _ref : _i >= _ref; chunkStart = _i += chunkSize) {
  355. _results.push(originalArray.splice.apply(originalArray, [start + chunkStart, 0].concat(__slice.call(insertedArray.slice(chunkStart, chunkStart + chunkSize)))));
  356. }
  357. return _results;
  358. }
  359. },
  360. sum: function(array) {
  361. var elt, sum, _i, _len;
  362. sum = 0;
  363. for (_i = 0, _len = array.length; _i < _len; _i++) {
  364. elt = array[_i];
  365. sum += elt;
  366. }
  367. return sum;
  368. },
  369. uncamelcase: function(string) {
  370. var result;
  371. if (!string) {
  372. return '';
  373. }
  374. result = string.replace(/([A-Z])|_+/g, function(match, letter) {
  375. if (letter == null) {
  376. letter = '';
  377. }
  378. return " " + letter;
  379. });
  380. return plus.capitalize(result.trim());
  381. },
  382. undasherize: function(string) {
  383. if (string) {
  384. return string.split('-').map(plus.capitalize).join(' ');
  385. } else {
  386. return '';
  387. }
  388. },
  389. underscore: function(string) {
  390. if (!string) {
  391. return '';
  392. }
  393. string = string[0].toLowerCase() + string.slice(1);
  394. return string.replace(/([A-Z])|-+/g, function(match, letter) {
  395. if (letter == null) {
  396. letter = '';
  397. }
  398. return "_" + (letter.toLowerCase());
  399. });
  400. },
  401. valueForKeyPath: function(object, keyPath) {
  402. var key, keys, _i, _len;
  403. keys = splitKeyPath(keyPath);
  404. for (_i = 0, _len = keys.length; _i < _len; _i++) {
  405. key = keys[_i];
  406. object = object[key];
  407. if (object == null) {
  408. return;
  409. }
  410. }
  411. return object;
  412. },
  413. isEqual: function(a, b, aStack, bStack) {
  414. if (_.isArray(aStack) && _.isArray(bStack)) {
  415. return isEqual(a, b, aStack, bStack);
  416. } else {
  417. return isEqual(a, b);
  418. }
  419. },
  420. isEqualForProperties: function() {
  421. var a, b, properties, property, _i, _len;
  422. a = arguments[0], b = arguments[1], properties = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
  423. for (_i = 0, _len = properties.length; _i < _len; _i++) {
  424. property = properties[_i];
  425. if (!_.isEqual(a[property], b[property])) {
  426. return false;
  427. }
  428. }
  429. return true;
  430. }
  431. };
  432. isEqual = function(a, b, aStack, bStack) {
  433. var aCtor, aCtorValid, aElement, aKeyCount, aValue, bCtor, bCtorValid, bKeyCount, bValue, equal, i, key, stackIndex, _i, _len;
  434. if (aStack == null) {
  435. aStack = [];
  436. }
  437. if (bStack == null) {
  438. bStack = [];
  439. }
  440. if (a === b) {
  441. return _.isEqual(a, b);
  442. }
  443. if (_.isFunction(a) || _.isFunction(b)) {
  444. return _.isEqual(a, b);
  445. }
  446. stackIndex = aStack.length;
  447. while (stackIndex--) {
  448. if (aStack[stackIndex] === a) {
  449. return bStack[stackIndex] === b;
  450. }
  451. }
  452. aStack.push(a);
  453. bStack.push(b);
  454. equal = false;
  455. if (_.isFunction(a != null ? a.isEqual : void 0)) {
  456. equal = a.isEqual(b, aStack, bStack);
  457. } else if (_.isFunction(b != null ? b.isEqual : void 0)) {
  458. equal = b.isEqual(a, bStack, aStack);
  459. } else if (_.isArray(a) && _.isArray(b) && a.length === b.length) {
  460. equal = true;
  461. for (i = _i = 0, _len = a.length; _i < _len; i = ++_i) {
  462. aElement = a[i];
  463. if (!isEqual(aElement, b[i], aStack, bStack)) {
  464. equal = false;
  465. break;
  466. }
  467. }
  468. } else if (_.isRegExp(a) && _.isRegExp(b)) {
  469. equal = _.isEqual(a, b);
  470. } else if (_.isElement(a) && _.isElement(b)) {
  471. equal = a === b;
  472. } else if (_.isObject(a) && _.isObject(b)) {
  473. aCtor = a.constructor;
  474. bCtor = b.constructor;
  475. aCtorValid = _.isFunction(aCtor) && aCtor instanceof aCtor;
  476. bCtorValid = _.isFunction(bCtor) && bCtor instanceof bCtor;
  477. if (aCtor !== bCtor && !(aCtorValid && bCtorValid)) {
  478. equal = false;
  479. } else {
  480. aKeyCount = 0;
  481. equal = true;
  482. for (key in a) {
  483. aValue = a[key];
  484. if (!_.has(a, key)) {
  485. continue;
  486. }
  487. aKeyCount++;
  488. if (!(_.has(b, key) && isEqual(aValue, b[key], aStack, bStack))) {
  489. equal = false;
  490. break;
  491. }
  492. }
  493. if (equal) {
  494. bKeyCount = 0;
  495. for (key in b) {
  496. bValue = b[key];
  497. if (_.has(b, key)) {
  498. bKeyCount++;
  499. }
  500. }
  501. equal = aKeyCount === bKeyCount;
  502. }
  503. }
  504. } else {
  505. equal = _.isEqual(a, b);
  506. }
  507. aStack.pop();
  508. bStack.pop();
  509. return equal;
  510. };
  511. module.exports = _.extend({}, _, plus);
  512. }).call(this);