/** A Javascript utility belt with an emphasis on Functional Programming. @module cint @author Raine Lourie @version v8.2.1 (Fri, 13 Feb 2015 20:24:14 GMT) */ cint = (function() { 'use strict'; /*********************************** * Private Functions ***********************************/ var _last = partialAt(index, 1, -1); /*********************************** * String ***********************************/ /** Performs variable substitution on the string, replacing items in {curly braces}. Same as Lodash's _.template(str, o, { interpolate: /{([\s\S]+?)}/g }). Based on supplant by Douglas Crockford http://javascript.crockford.com/remedial.html @memberOf module:cint# @param {String} str @param {Object|Array} o */ function supplant(str, o) { return str.replace(/{([^{}]*)}/g, function (a, b) { return b in o ? o[b] : a } ) } /** Returns true if the string starts with the given substring. Returns false if the substring is the empty string. @memberOf module:cint# @param {String} str @param {String} sub The substring. */ function startsWith(str, sub){ return str.indexOf(sub) === 0 && sub !== '' } /** Returns the substring before the first instance of the given delimiter. @memberOf module:cint# @param {String} str @param {String} delim */ function before(str, delim) { return str.split(delim)[0] } /** Returns the substring after the first instance of the given delimiter. Returns the whole string if the delimiter is not found. @memberOf module:cint# @param {String} str @param {String} delim */ function after(str, delim) { var delimIndex = str.indexOf(delim) return delimIndex >= 0 ? str.substring(delimIndex+delim.length) : str } /** Returns the substring between the given delimiters. @memberOf module:cint# @param {String} str @param {String} left @param {String} right */ function between(str, left, right) { return before(after(str, left), right) } /** Wraps a string with a left and right. If right omitted, wraps both ends in left. @memberOf module:cint# @param {string} middle @param {string} left @param {string} right */ function bookend(middle, left, right) { return (left || '') + middle + (right || left || '') } /** Returns a single string that repeats the string n times. Optionally joins it with the given delimeter @memberOf module:cint# @param {String} str @param {Number} n @param {String} delim Default: '' */ function repeatString(str, n, delim) { delim = delim || '' return mapNumber(n, function() { return str }).join(delim) } /** Capitalizes the first letter of each word in the given string. @memberOf module:cint# @param {String} str */ function toTitleCase(str) { var capitalizeFirst = function(s) { return s.length ? s[0].toUpperCase() + s.substring(1).toLowerCase() : '' } return str.split(' ').map(capitalizeFirst).join(' ') } /*********************************** * Number ***********************************/ /** Returns the ordinal value (like '1st' or '2nd') of the given integer. @memberOf module:cint# @param {Number} n */ function ordinal(n) { var lastDigit = n%10 return n + ( n >= 11 && n <= 13 ? 'th' : lastDigit === 1 ? 'st' : lastDigit === 2 ? 'nd' : lastDigit === 3 ? 'rd' : 'th') } /** Invokes the given function n times, passing the index for each invocation, and returns an array of the results. @memberOf module:cint# @param {Number} n @param {Function} f */ function mapNumber(n, f) { var results = [] for(var i=0; i { "4": { ideal: 1 }, "3": { past: 1 } "5": { ideal: 1 } "7": { present: 2, past: 1 } } */ function tallyProps(arr) { var tallies = {} for(var i=0; ib, 0 if a==b, or -1 if a b ? 1 : a < b ? -1 : 0 } /** Compares the value of the given property between two objects. @memberOf module:cint# @param {String} property */ function compareProperty(property, a, b) { return compare(a[property], b[property]) } /** Returns a compare function that can be passed to Array.sort to sort in the order of the given array of properties. A property can also be appended with ' ASC' or ' DESC' to control the sort order. @memberOf module:cint# @param {String[]|Object[]} props */ function dynamicCompare(props) { if(!props || !(props instanceof Array)) { throw new Error('props is falsey or not an Array') } return function(a,b) { var len = props.length for(var i=0; i= 0) { var splitVal = props[i].split(' ') aVal = a[splitVal[0]] bVal = b[splitVal[0]] sortDir = splitVal[1].toLowerCase() } else { aVal = a[props[i]] bVal = b[props[i]] sortDir = 'asc' } // this is important so that if the values are equial, it continues to the next sort property if(aVal != bVal) { return sortDir == 'asc' ? compare(aVal,bVal) : compare(bVal,aVal) } } return 0 } } /** Returns true if all the items in a are equal to all the items in b, recursively. @memberOf module:cint# @param a @param b */ function equals(a,b) { if(typeof a !== typeof b) { return false } // compare arrays if(a instanceof Array) { // check if the arrays are the same length if(a.length !== b.length) { return false } // check the equality of each item for(var i=0, l=a.length; i