diff --git a/lib/array-ext.js b/lib/array-ext.js index 27470a5..e5f6980 100644 --- a/lib/array-ext.js +++ b/lib/array-ext.js @@ -1,98 +1,99 @@ +// batteries +// Copyright 2010 - 2011 Sami Samhuri + +var ArrayExt = +{ at: at +, compact: compact +, first: first +, flatten: flatten +, grep: grep +, last: last +, max: max +, min: min +, pluck: pluck +, sortBy: sortBy +, unique: unique +}; + exports.extendNative = function() { - Object.keys(ArrayExt).forEach(function(k) { - if (Array.prototype[k]) return; // don't overwrite existing members - Array.prototype[k] = function() { - var fn = ArrayExt[k] - , args = [].slice.call(arguments) - ; - args.unshift(this); - return fn.apply(ArrayExt, args); - }; - }); + require('./ext').extend(Array, ArrayExt); }; -var ArrayToString = [].toString(); +require('./object-ext').extend(exports, ArrayExt); -var ArrayExt = exports.ArrayExt = { - -// abbrev +// TODO +// - abbrev +// - pack +// - partition - // [1,2,3,4,5].at(-1) => 5 - at: function(a, i) { - if (i >= 0) return a[i]; - return a[a.length + i]; - } +// [1,2,3,4,5].at(-1) => 5 +function at(a, i) { + if (i >= 0) return a[i]; + return a[a.length + i]; +} - // TODO make this work for non-array objects - , compact: function(a) { - var b = [] - , i = a.length - ; - while (i--) { - if (a[i] !== null && a[i] !== undefined) b[i] = a[i]; - } - return b; - } +// TODO make this work for non-array objects +function compact(a) { + var b = [] + , i = a.length + ; + while (i--) { + if (a[i] !== null && a[i] !== undefined) b[i] = a[i]; + } + return b; +} - , first: function(a) { return a[0]; } - - // Based on underscore.js's flatten - , flatten: function(a) { - return a.reduce(function(initial, elem) { - if (elem && elem.flatten) initial = initial.concat(elem.flatten()); - else initial.push(elem); - return initial; - }); - } +function first(a) { return a[0]; } - , grep: function(a, regex) { - return a.filter(function(v) { return regex.test(v); }); - } +// Based on underscore.js's flatten +function flatten(a) { + return a.reduce(function(initial, elem) { + if (elem && elem.flatten) initial = initial.concat(elem.flatten()); + else initial.push(elem); + return initial; + }); +} - , last: function(a) { return a[a.length-1]; } +function grep(a, regex) { + return a.filter(function(v) { return regex.test(v); }); +} - , max: function(a) { - return a.reduce(function(max, v) { return v > max ? v : max }); - } +function last(a) { return a[a.length-1]; } - , min: function(a) { - return a.reduce(function(min, v) { return v < min ? v : min }); - } +function max(a) { + return a.reduce(function(max, v) { return v > max ? v : max }); +} - // pack - // partition +function min(a) { + return a.reduce(function(min, v) { return v < min ? v : min }); +} - , pluck: function(a /* , key paths, ... */) { - var args = [].slice.call(arguments, 1); - args.unshift(a); - return pluck.apply(null, args); - } +function pluck(a /* , key paths, ... */) { + var args = [].slice.call(arguments, 1); + args.unshift(a); + return pluck.apply(null, args); +} - , sortBy: function(arr, keyPath) { - return arr.slice().sort(function(a, b) { - var propA = drillInto(a) - , propB = drillInto(b); - if (propA < propB) return -1 - if (propA > propB) return 1 - return 0 - }); - } +function sortBy(arr, keyPath) { + return arr.slice().sort(function(a, b) { + var propA = drillInto(a) + , propB = drillInto(b); + if (propA < propB) return -1 + if (propA > propB) return 1 + return 0 + }); +} - , toString: function(a) { - return '[' + ArrayToString.call(a) + ']'; - } - - , unique: function(a) { - var b = [] - , i = 0 - , n = a.length - ; - for (; i < n; ++i) { - if (b.indexOf(a[i]) === -1) b.push(a[i]); - } - return b; - } -}; +function unique(a) { + var b = [] + , i = 0 + , n = a.length + ; + for (; i < n; ++i) { + if (b.indexOf(a[i]) === -1) b.push(a[i]); + } + return b; +} // pluck diff --git a/lib/date-ext.js b/lib/date-ext.js index 472128c..1785fdd 100644 --- a/lib/date-ext.js +++ b/lib/date-ext.js @@ -1,16 +1,14 @@ +// batteries +// Copyright 2010 - 2011 Sami Samhuri + +var DateExt = { format: format }; + exports.extendNative = function() { - Object.keys(DateExt).forEach(function(k) { - if (Date.prototype[k]) return; // don't overwrite existing members - Date.prototype[k] = function() { - var fn = DateExt[k] - , args = [].slice.call(arguments) - ; - args.shift(this); - fn.apply(DateExt, args); - }; - }); + require('./ext').extend(Date, DateExt); }; +require('./object-ext').extend(exports, DateExt); + var Weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; @@ -22,77 +20,77 @@ var Months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', var MonthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +function format(d, fmt) { + return fmt.replace(/%(.)/, function(_, c) { + switch (c) { + case 'A': return Weekdays[d.getDay()]; + case 'a': return WeekdaysShort[d.getDay()]; + case 'B': return Months[d.getMonth()]; + case 'b': // fall through + case 'h': return MonthsShort[d.getMonth()]; + case 'D': return format(d, '%m/%d/%y'); + case 'd': return pad(d.getDate()); + case 'e': return d.getDate(); + case 'F': return format(d, '%Y-%m-%d'); + case 'H': return pad(d.getHours()); + case 'I': + var hour = d.getHours(); + if (hour == 0) hour = 12; + else if (hour > 12) hour -= 12; + return pad(hour); + case 'k': return pad(d.getHours(), ' '); + case 'l': + var hour = d.getHours(); + if (hour == 0) hour = 12; + else if (hour > 12) hour -= 12; + return pad(hour, ' '); + case 'M': return pad(d.getMinutes()); + case 'm': return pad(d.getMonth() + 1); + case 'n': return '\n'; + case 'p': return d.getHours() < 12 ? 'AM' : 'PM'; + case 'R': return format(d, '%H:%M'); + case 'r': return format(d, '%I:%M:%S %p'); + case 'S': return pad(d.getSeconds()); + case 's': return d.getTime(); + case 'T': return format(d, '%H:%M:%S'); + case 't': return '\t'; + case 'u': + var day = d.getDay(); + return day == 0 ? 7 : day; // 1 - 7, Monday is first day of the week + case 'v': return format(d, '%e-%b-%Y'); + case 'w': return d.getDay(); // 0 - 6, Sunday is first day of the week + case 'Y': return d.getFullYear(); + case 'y': + var year = d.getYear(); + return year < 100 ? year : year - 100; + case 'Z': + var tz = d.toString().match(/\((\w+)\)/); + return tz && tz[1] || ''; + case 'z': + var off = d.getTimezoneOffset(); + return (off < 0 ? '-' : '+') + pad(off / 60) + pad(off % 60); + default: return c; + } + }); +} + function pad(n, padding) { padding = padding || '0'; return n < 10 ? (padding + n) : n; } -var DateExt = { - // FIXME write a c extension that uses strftime to do the heavy lifting - format: function(d, fmt) { - return fmt.replace(/%(.)/, function(_, c) { - switch (c) { - case 'A': return Weekdays[d.getDay()]; - case 'a': return WeekdaysShort[d.getDay()]; - case 'B': return Months[d.getMonth()]; - case 'b': // fall through - case 'h': return MonthsShort[d.getMonth()]; - case 'D': return DateExt.format(d, '%m/%d/%y'); - case 'd': return pad(d.getDate()); - case 'e': return d.getDate(); - case 'F': return DateExt.format(d, '%Y-%m-%d'); - case 'H': return pad(d.getHours()); - case 'I': - var hour = d.getHours(); - if (hour == 0) hour = 12; - else if (hour > 12) hour -= 12; - return pad(hour); - case 'k': return pad(d.getHours(), ' '); - case 'l': - var hour = d.getHours(); - if (hour == 0) hour = 12; - else if (hour > 12) hour -= 12; - return pad(hour, ' '); - case 'M': return pad(d.getMinutes()); - case 'm': return pad(d.getMonth() + 1); - case 'n': return '\n'; - case 'p': return d.getHours() < 12 ? 'AM' : 'PM'; - case 'R': return DateExt.format(d, '%H:%M'); - case 'r': return DateExt.format(d, '%I:%M:%S %p'); - case 'S': return pad(d.getSeconds()); - case 's': return d.getTime(); - case 'T': return DateExt.format(d, '%H:%M:%S'); - case 't': return '\t'; - case 'u': - var day = d.getDay(); - return day == 0 ? 7 : day; // 1 - 7, Monday is first day of the week - case 'v': return DateExt.format(d, '%e-%b-%Y'); - case 'w': return d.getDay(); // 0 - 6, Sunday is first day of the week - case 'Y': return d.getFullYear(); - case 'y': - var year = d.getYear(); - return year < 100 ? year : year - 100; - case 'Z': - var tz = d.toString().match(/\((\w+)\)/); - return tz && tz[1] || ''; - case 'z': - var off = d.getTimezoneOffset(); - return (off < 0 ? '-' : '+') + pad(off / 60) + pad(off % 60); - default: return c; - } - }); - }, - month: function(d) { - return Months(d.getMonth()); - }, - shortMonth: function(d) { - return MonthsShort(d.getMonth()); - }, - weekday: function(d) { - return Weekdays(d.getDay()); - }, - shortWeekday: function(d) { - return WeekdaysShort(d.getDay()); - } -}; -exports.DateExt = DateExt; \ No newline at end of file +function month(d) { + return Months(d.getMonth()); +} + +function shortMonth(d) { + return MonthsShort(d.getMonth()); +} + +function weekday(d) { + return Weekdays(d.getDay()); +} + +function shortWeekday(d) { + return WeekdaysShort(d.getDay()); +} diff --git a/lib/ext.js b/lib/ext.js index 17445d1..b8a04a6 100644 --- a/lib/ext.js +++ b/lib/ext.js @@ -1,13 +1,18 @@ +// batteries +// Copyright 2010 - 2011 Sami Samhuri + exports.extend = function(obj, ext) { - // FIXME why doesn't this work when the caller supplies - // a native type's proto for obj? e.g. Array.prototype - Object.keys(ext).forEach(function(k) { - if (obj[k]) return; // don't overwrite existing members - obj[k] = function() { - var fn = ext[k] - , args = [].slice.call(arguments) - ; - fn.apply(null, args); - }; + Object.keys(ext).forEach(function(k) { + if (k in obj.prototype) return; // don't overwrite existing members + var wrapper = function() { + var fn = ext[k] + , args = [].slice.call(arguments) + ; + args.unshift(this); + return fn.apply(this, args); + }; + Object.defineProperty(obj.prototype, k, { + get: function() { return wrapper; } }); + }); }; diff --git a/lib/math-ext.js b/lib/math-ext.js index 80618d6..5226c41 100644 --- a/lib/math-ext.js +++ b/lib/math-ext.js @@ -1,20 +1,26 @@ -exports.extendNative = function() { - Math.sum = sum; - Math.avg = avg; +// batteries +// Copyright 2010 - 2011 Sami Samhuri + +var MathExt = +{ avg: avg +, sum: sum +}; + +exports.extendNative = function() { + Math.avg = avg; + Math.sum = sum; }; -exports.sum = sum; function sum() { - var nums = Array.isArray(arguments[0]) ? arguments[0] : arguments - , i = nums.length - , sum = 0 - ; - while (i--) sum += arguments[i]; - return sum; + var nums = Array.isArray(arguments[0]) ? arguments[0] : arguments + , i = nums.length + , sum = 0 + ; + while (i--) sum += arguments[i]; + return sum; } -exports.avg = avg; function avg() { - var nums = Array.isArray(arguments[0]) ? arguments[0] : arguments; - return sum.call(this, nums) / nums.length; + var nums = Array.isArray(arguments[0]) ? arguments[0] : arguments; + return sum.call(this, nums) / nums.length; } diff --git a/lib/object-ext.js b/lib/object-ext.js index fe885b1..58facd7 100644 --- a/lib/object-ext.js +++ b/lib/object-ext.js @@ -1,10 +1,26 @@ -var ext = exports.ObjectExt = { +// batteries +// Copyright 2010 - 2011 Sami Samhuri - cmp: function(a, b) { - if (a === b) return 0; - if (a < b) return -1; - if (a > b) return 1; - throw new Error('cannot effectively compare values'); - } +var ObjectExt = +{ cmp: cmp +, extend: extend +} +exports.extendNative = function() { + require('./ext').extend(Object, ObjectExt); }; + +extend(module.exports, ObjectExt); + +function cmp(a, b) { + if (a === b) return 0; + if (a < b) return -1; + if (a > b) return 1; + throw new Error('cannot effectively compare values'); +} + +function extend(a, b) { + Object.getOwnPropertyNames(b).forEach(function(k) { + a[k] = b[k]; + }); +} diff --git a/lib/string-ext.js b/lib/string-ext.js index f4873ee..1629202 100644 --- a/lib/string-ext.js +++ b/lib/string-ext.js @@ -1,11 +1,21 @@ -var StringExt = exports.StringExt = { +// batteries +// Copyright 2010 - 2011 Sami Samhuri - cmp: function(a, b) { - if (a === b) return 0; - if (a < b) return -1; - return 1; // a > b - } +// TODO +// - reverse +// - unpack +// - sha1 - // unpack +var StringExt = { cmp: cmp }; +exports.extendNative = function() { + require('./ext').extend(String, StringExt); }; + +require('./object-ext').extend(exports, StringExt); + +function cmp(a, b) { + if (a === b) return 0; + if (a < b) return -1; + return 1; // a > b +}