pax_global_header00006660000000000000000000000064124017761700014517gustar00rootroot0000000000000052 comment=54cd910d28355f7217396abfbf29ca98dd0ec433 utilities-1.0.4/000077500000000000000000000000001240177617000135345ustar00rootroot00000000000000utilities-1.0.4/.gitignore000066400000000000000000000002421240177617000155220ustar00rootroot00000000000000v8.log *.swp *.swo auth_info.js dist .idea/ tags nbproject/ spec/browser/autogen_suite.js node_modules tmtags *.DS_Store examples/*/log/* .log npm-debug.log doc/ utilities-1.0.4/.travis.yml000066400000000000000000000002121240177617000156400ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.8" before_install: - npm update -g npm script: ./node_modules/jake/bin/cli.js test --trace utilities-1.0.4/Jakefile000066400000000000000000000016341240177617000151750ustar00rootroot00000000000000 testTask('Utilities', function () { this.testFiles.include('test/*.js'); // Can't reliably test ports on travis if(process.env.CI) { this.testFiles.exclude('test/network.js'); } }); namespace('doc', function () { task('generate', ['doc:clobber'], function () { var cmd = '../node-jsdoc-toolkit/app/run.js -n -r=100 ' + '-t=../node-jsdoc-toolkit/templates/codeview -d=./doc/ ./lib'; console.log('Generating docs ...'); jake.exec([cmd], function () { console.log('Done.'); complete(); }); }, {async: true}); task('clobber', function () { var cmd = 'rm -fr ./doc/**'; jake.exec([cmd], function () { console.log('Clobbered old docs.'); complete(); }); }, {async: true}); }); desc('Generate docs for Utilities'); task('doc', ['doc:generate']); publishTask('utilities', [ 'Jakefile' , 'README.md' , 'package.json' , 'lib/**' , 'test/**' ]); utilities-1.0.4/Makefile000066400000000000000000000020561240177617000151770ustar00rootroot00000000000000# # Utilities: A classic collection of JavaScript utilities # Copyright 2112 Matthew Eernisse (mde@fleegix.org) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # .PHONY: all build install clean uninstall PREFIX=/usr/local DESTDIR= all: build build: @echo 'Utilities built.' install: @mkdir -p $(DESTDIR)$(PREFIX)/lib/node_modules/utilities && \ cp -R ./* $(DESTDIR)$(PREFIX)/lib/node_modules/utilities/ && \ echo 'Utilities installed.' clean: @true uninstall: @rm -rf $(DESTDIR)$(PREFIX)/lib/node_modules/utilities && \ echo 'Utilities uninstalled.' utilities-1.0.4/README.md000066400000000000000000000002611240177617000150120ustar00rootroot00000000000000utilities ========= [![Build Status](https://travis-ci.org/mde/utilities.png?branch=master)](https://travis-ci.org/mde/utilities) A classic collection of JavaScript utilities utilities-1.0.4/lib/000077500000000000000000000000001240177617000143025ustar00rootroot00000000000000utilities-1.0.4/lib/array.js000066400000000000000000000046051240177617000157630ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** @name array @namespace array */ var array = new (function () { /** @name array#humanize @public @function @return {String} A string containing the array elements in a readable format @description Creates a string containing the array elements in a readable format @param {Array} array The array to humanize */ this.humanize = function (a) { var array = a.slice(); // If array only has one item then just return it if (array.length <= 1) { return String(array); } var last = array.pop() , items = array.join(', '); return items + ' and ' + last; }; /** @name array#included @public @function @return {Array/Boolean} If `item` is included the `array` is returned otherwise false @description Checks if an `item` is included in an `array` @param {Any} item The item to look for @param {Array} array The array to check */ this.included = function (item, array) { var result = array.indexOf(item); if (result === -1) { return false; } else { return array; } }; /** @name array#include @public @function @return {Boolean} Return true if the item is included in the array @description Checks if an `item` is included in an `array` @param {Array} array The array to check @param {Any} item The item to look for */ this.include = function (array, item) { var res = -1; if (typeof array.indexOf == 'function') { res = array.indexOf(item); } else { for (var i = 0, ii = array.length; i < ii; i++) { if (array[i] == item) { res = i; break; } } } return res > -1; }; })(); module.exports = array; utilities-1.0.4/lib/async.js000066400000000000000000000171631240177617000157650ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var async = {}; /* AsyncChain -- performs a list of asynchronous calls in a desired order. Optional "last" method can be set to run after all the items in the chain have completed. // Example usage var asyncChain = new async.AsyncChain([ { func: app.trainToBangkok, args: [geddy, neil, alex], callback: null, // No callback for this action }, { func: fs.readdir, args: [geddy.config.dirname + '/thailand/express'], callback: function (err, result) { if (err) { // Bail out completely arguments.callee.chain.abort(); } else if (result.theBest) { // Don't run the next item in the chain; go directly // to the 'last' method. arguments.callee.chain.shortCircuit(); } else { // Otherwise do some other stuff and // then go to the next link } } }, { func: child_process.exec, args: ['ls ./'], callback: this.hitTheStops } ]); // Function to exec after all the links in the chain finish asyncChain.last = function () { // Do some final stuff }; // Start the async-chain asyncChain.run(); */ async.execNonBlocking = function (func) { if (typeof process != 'undefined' && typeof process.nextTick == 'function') { process.nextTick(func); } else { setTimeout(func, 0); } }; async.AsyncBase = new (function () { this.init = function (chain) { var item; this.chain = []; this.currentItem = null; this.shortCircuited = false; this.shortCircuitedArgs = undefined; this.aborted = false; for (var i = 0; i < chain.length; i++) { item = chain[i]; this.addItem(item); } }; this.addItem = function(item) { this.chain.push(new async.AsyncCall( item.func, item.args || [], item.callback, item.context)); }; // alias this.push = this.addItem; this.runItem = function (item) { // Reference to the current item in the chain -- used // to look up the callback to execute with execCallback this.currentItem = item; // Scopage var _this = this; // Pass the arguments passed to the current async call // to the callback executor, execute it in the correct scope var executor = function () { _this.execCallback.apply(_this, arguments); }; // Append the callback executor to the end of the arguments // Node helpfully always has the callback func last var args = item.args.concat(executor); var func = item.func; // Run the async call func.apply(item.context, args); }; this.next = function () { if (this.chain.length) { this.runItem(this.chain.shift()); } else { this.last(); } }; this.execCallback = function () { // Look up the callback, if any, specified for this async call var callback = this.currentItem.callback; // If there's a callback, do it if (callback && typeof callback == 'function') { // Allow access to the chain from inside the callback by setting // callback.chain = this, and then using arguments.callee.chain callback.chain = this; callback.apply(this.currentItem.context, arguments); } this.currentItem.finished = true; // If one of the async callbacks called chain.shortCircuit, // skip to the 'last' function for the chain if (this.shortCircuited) { this.last.apply(null, this.shortCircuitedArgs); } // If one of the async callbacks called chain.abort, // bail completely out else if (this.aborted) { return; } // Otherwise run the next item, if any, in the chain // Let's try not to block if we don't have to else { // Scopage var _this = this; async.execNonBlocking(function () { _this.next.call(_this); }); } } // Short-circuit the chain, jump straight to the 'last' function this.shortCircuit = function () { this.shortCircuitedArgs = arguments; this.shortCircuited = true; } // Stop execution of the chain, bail completely out this.abort = function () { this.aborted = true; } // Kick off the chain by grabbing the first item and running it this.run = this.next; // Function to run when the chain is done -- default is a no-op this.last = function () {}; })(); async.AsyncChain = function (chain) { this.init(chain); }; async.AsyncChain.prototype = async.AsyncBase; async.AsyncGroup = function (group) { var item; var callback; var args; this.group = []; this.outstandingCount = 0; for (var i = 0; i < group.length; i++) { item = group[i]; this.group.push(new async.AsyncCall( item.func, item.args, item.callback, item.context)); this.outstandingCount++; } }; /* Simpler way to group async calls -- doesn't ensure completion order, but still has a "last" method called when the entire group of calls have completed. */ async.AsyncGroup.prototype = new function () { this.run = function () { var _this = this , group = this.group , item , createItem = function (item, args) { return function () { item.func.apply(item.context, args); }; } , createCallback = function (item) { return function () { if (item.callback) { item.callback.apply(null, arguments); } _this.finish.call(_this); } }; for (var i = 0; i < group.length; i++) { item = group[i]; callback = createCallback(item); args = item.args.concat(callback); // Run the async call async.execNonBlocking(createItem(item, args)); } }; this.finish = function () { this.outstandingCount--; if (!this.outstandingCount) { this.last(); }; }; this.last = function () {}; }; var _createSimpleAsyncCall = function (func, context) { return { func: func , args: [] , callback: function () {} , context: context }; }; async.SimpleAsyncChain = function (funcs, context) { var chain = []; for (var i = 0, ii = funcs.length; i < ii; i++) { chain.push(_createSimpleAsyncCall(funcs[i], context)); } this.init(chain); }; async.SimpleAsyncChain.prototype = async.AsyncBase; async.AsyncCall = function (func, args, callback, context) { this.func = func; this.args = args; this.callback = callback || null; this.context = context || null; }; async.Initializer = function (steps, callback) { var self = this; this.steps = {}; this.callback = callback; // Create an object-literal of the steps to tick off steps.forEach(function (step) { self.steps[step] = false; }); }; async.Initializer.prototype = new (function () { this.complete = function (step) { var steps = this.steps; // Tick this step off steps[step] = true; // Iterate the steps -- if any are not done, bail out for (var p in steps) { if (!steps[p]) { return false; } } // If all steps are done, run the callback this.callback(); }; })(); module.exports = async; utilities-1.0.4/lib/core.js000066400000000000000000000072101240177617000155700ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var core = new (function () { var _mix = function (targ, src, merge, includeProto) { for (var p in src) { // Don't copy stuff from the prototype if (src.hasOwnProperty(p) || includeProto) { if (merge && // Assumes the source property is an Object you can // actually recurse down into (typeof src[p] == 'object') && (src[p] !== null) && !(src[p] instanceof Array)) { // Create the source property if it doesn't exist // Double-equal to undefined includes both null and undefined if (targ[p] == undefined) { targ[p] = {}; } _mix(targ[p], src[p], merge, includeProto); // Recurse } // If it's not a merge-copy, just set and forget else { targ[p] = src[p]; } } } }; /* * Mix in the properties on an object to another object * yam.mixin(target, source, [source,] [source, etc.] [merge-flag]); * 'merge' recurses, to merge object sub-properties together instead * of just overwriting with the source object. */ this.mixin = function () { var args = Array.prototype.slice.apply(arguments), merge = false, targ, sources; if (args.length > 2) { if (typeof args[args.length - 1] == 'boolean') { merge = args.pop(); } } targ = args.shift(); sources = args; for (var i = 0, ii = sources.length; i < ii; i++) { _mix(targ, sources[i], merge); } return targ; }; this.enhance = function () { var args = Array.prototype.slice.apply(arguments), merge = false, targ, sources; if (args.length > 2) { if (typeof args[args.length - 1] == 'boolean') { merge = args.pop(); } } targ = args.shift(); sources = args; for (var i = 0, ii = sources.length; i < ii; i++) { _mix(targ, sources[i], merge, true); } return targ; }; // Idea to add invalid number & Date from Michael J. Ryan, // http://frugalcoder.us/post/2010/02/15/js-is-empty.aspx this.isEmpty = function (val) { // Empty string, null or undefined (these two are double-equal) if (val === '' || val == undefined) { return true; } // Invalid numerics if (typeof val == 'number' && isNaN(val)) { return true; } // Invalid Dates if (val instanceof Date && isNaN(val.getTime())) { return true; } return false; }; /* binds a function to an object */ this.bind = function () { var args = Array.prototype.slice.call(arguments) , ctxt = args.shift() , fn = args.shift(); if (typeof fn === 'function') { if (typeof Function.bind === 'function') { return fn.bind.apply(ctxt, args); } else { return fn.apply(ctxt, args); } } // in IE, native methods are not functions so they cannot be bound, // and don't need to be else { return fn; } } })(); module.exports = core; utilities-1.0.4/lib/date.js000066400000000000000000000717631240177617000155730ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var string = require('./string') , date , log = require('./log'); /** @name date @namespace date */ date = new (function () { var _this = this , _date = new Date(); var _US_DATE_PAT = /^(\d{1,2})(?:\-|\/|\.)(\d{1,2})(?:\-|\/|\.)(\d{4})/; var _DATETIME_PAT = /^(\d{4})(?:\-|\/|\.)(\d{1,2})(?:\-|\/|\.)(\d{1,2})(?:T| )?(\d{2})?(?::)?(\d{2})?(?::)?(\d{2})?(?:\.)?(\d+)?(?: *)?(Z|[+-]\d{4}|[+-]\d{2}:\d{2}|[+-]\d{2})?/; // TODO Add am/pm parsing instead of dumb, 24-hour clock. var _TIME_PAT = /^(\d{1,2})?(?::)?(\d{2})?(?::)?(\d{2})?(?:\.)?(\d+)?$/; var _dateMethods = [ 'FullYear' , 'Month' , 'Date' , 'Hours' , 'Minutes' , 'Seconds' , 'Milliseconds' ]; var _isArray = function (obj) { return obj && typeof obj === 'object' && typeof obj.length === 'number' && typeof obj.splice === 'function' && !(obj.propertyIsEnumerable('length')); }; this.weekdayLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; this.weekdayShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; this.monthLong = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; this.monthShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; this.meridiem = { 'AM': 'AM', 'PM': 'PM' } // compat this.meridian = this.meridiem /** @name date#supportedFormats @public @object @description List of supported strftime formats */ this.supportedFormats = { // abbreviated weekday name according to the current locale 'a': function (dt) { return _this.weekdayShort[dt.getDay()]; }, // full weekday name according to the current locale 'A': function (dt) { return _this.weekdayLong[dt.getDay()]; }, // abbreviated month name according to the current locale 'b': function (dt) { return _this.monthShort[dt.getMonth()]; }, 'h': function (dt) { return _this.strftime(dt, '%b'); }, // full month name according to the current locale 'B': function (dt) { return _this.monthLong[dt.getMonth()]; }, // preferred date and time representation for the current locale 'c': function (dt) { return _this.strftime(dt, '%a %b %d %T %Y'); }, // century number (the year divided by 100 and truncated // to an integer, range 00 to 99) 'C': function (dt) { return _this.calcCentury(dt.getFullYear());; }, // day of the month as a decimal number (range 01 to 31) 'd': function (dt) { return string.lpad(dt.getDate(), '0', 2); }, // same as %m/%d/%y 'D': function (dt) { return _this.strftime(dt, '%m/%d/%y') }, // day of the month as a decimal number, a single digit is // preceded by a space (range ' 1' to '31') 'e': function (dt) { return string.lpad(dt.getDate(), ' ', 2); }, // month as a decimal number, a single digit is // preceded by a space (range ' 1' to '12') 'f': function () { return _this.strftimeNotImplemented('f'); }, // same as %Y-%m-%d 'F': function (dt) { return _this.strftime(dt, '%Y-%m-%d'); }, // like %G, but without the century. 'g': function () { return _this.strftimeNotImplemented('g'); }, // The 4-digit year corresponding to the ISO week number // (see %V). This has the same format and value as %Y, // except that if the ISO week number belongs to the // previous or next year, that year is used instead. 'G': function () { return _this.strftimeNotImplemented('G'); }, // hour as a decimal number using a 24-hour clock (range // 00 to 23) 'H': function (dt) { return string.lpad(dt.getHours(), '0', 2); }, // hour as a decimal number using a 12-hour clock (range // 01 to 12) 'I': function (dt) { return string.lpad( _this.hrMil2Std(dt.getHours()), '0', 2); }, // day of the year as a decimal number (range 001 to 366) 'j': function (dt) { return string.lpad( _this.calcDays(dt), '0', 3); }, // Hour as a decimal number using a 24-hour clock (range // 0 to 23 (space-padded)) 'k': function (dt) { return string.lpad(dt.getHours(), ' ', 2); }, // Hour as a decimal number using a 12-hour clock (range // 1 to 12 (space-padded)) 'l': function (dt) { return string.lpad( _this.hrMil2Std(dt.getHours()), ' ', 2); }, // month as a decimal number (range 01 to 12) 'm': function (dt) { return string.lpad((dt.getMonth()+1), '0', 2); }, // minute as a decimal number 'M': function (dt) { return string.lpad(dt.getMinutes(), '0', 2); }, // Linebreak 'n': function () { return '\n'; }, // either `am' or `pm' according to the given time value, // or the corresponding strings for the current locale 'p': function (dt) { return _this.getMeridian(dt.getHours()); }, // time in a.m. and p.m. notation 'r': function (dt) { return _this.strftime(dt, '%I:%M:%S %p'); }, // time in 24 hour notation 'R': function (dt) { return _this.strftime(dt, '%H:%M'); }, // second as a decimal number 'S': function (dt) { return string.lpad(dt.getSeconds(), '0', 2); }, // Tab char 't': function () { return '\t'; }, // current time, equal to %H:%M:%S 'T': function (dt) { return _this.strftime(dt, '%H:%M:%S'); }, // weekday as a decimal number [1,7], with 1 representing // Monday 'u': function (dt) { return _this.convertOneBase(dt.getDay()); }, // week number of the current year as a decimal number, // starting with the first Sunday as the first day of the // first week 'U': function () { return _this.strftimeNotImplemented('U'); }, // week number of the year (Monday as the first day of the // week) as a decimal number [01,53]. If the week containing // 1 January has four or more days in the new year, then it // is considered week 1. Otherwise, it is the last week of // the previous year, and the next week is week 1. 'V': function () { return _this.strftimeNotImplemented('V'); }, // week number of the current year as a decimal number, // starting with the first Monday as the first day of the // first week 'W': function () { return _this.strftimeNotImplemented('W'); }, // day of the week as a decimal, Sunday being 0 'w': function (dt) { return dt.getDay(); }, // preferred date representation for the current locale // without the time 'x': function (dt) { return _this.strftime(dt, '%D'); }, // preferred time representation for the current locale // without the date 'X': function (dt) { return _this.strftime(dt, '%T'); }, // year as a decimal number without a century (range 00 to // 99) 'y': function (dt) { return _this.getTwoDigitYear(dt.getFullYear()); }, // year as a decimal number including the century 'Y': function (dt) { return string.lpad(dt.getFullYear(), '0', 4); }, // time zone or name or abbreviation 'z': function () { return _this.strftimeNotImplemented('z'); }, 'Z': function () { return _this.strftimeNotImplemented('Z'); }, // Literal percent char '%': function (dt) { return '%'; } }; /** @name date#getSupportedFormats @public @function @description return the list of formats in a string @return {String} The list of supported formats */ this.getSupportedFormats = function () { var str = ''; for (var i in this.supportedFormats) { str += i; } return str; } this.supportedFormatsPat = new RegExp('%[' + this.getSupportedFormats() + ']{1}', 'g'); /** @name date#strftime @public @function @return {String} The `dt` formated with the given `format` @description Formats the given date with the strftime formated @param {Date} dt the date object to format @param {String} format the format to convert the date to */ this.strftime = function (dt, format) { if (!dt) { return '' } var d = dt; var pats = []; var dts = []; var str = format; var key; // Allow either Date obj or UTC stamp d = typeof dt == 'number' ? new Date(dt) : dt; // Grab all instances of expected formats into array while (pats = this.supportedFormatsPat.exec(format)) { dts.push(pats[0]); } // Process any hits for (var i = 0; i < dts.length; i++) { key = dts[i].replace(/%/, ''); str = str.replace('%' + key, this.supportedFormats[key](d)); } return str; }; this.strftimeNotImplemented = function (s) { throw('this.strftime format "' + s + '" not implemented.'); }; /** @name date#calcCentury @public @function @return {String} The century for the given date @description Find the century for the given `year` @param {Number} year The year to find the century for */ this.calcCentury = function (year) { if(!year) { year = _date.getFullYear(); } var ret = parseInt((year / 100) + 1); year = year.toString(); // If year ends in 00 subtract one, because it's still the century before the one // it divides to if (year.substring(year.length - 2) === '00') { ret--; } return ret.toString(); }; /** @name date#calcDays @public @function @return {Number} The number of days so far for the given date @description Calculate the day number in the year a particular date is on @param {Date} dt The date to use */ this.calcDays = function (dt) { var first = new Date(dt.getFullYear(), 0, 1); var diff = 0; var ret = 0; first = first.getTime(); diff = (dt.getTime() - first); ret = parseInt(((((diff/1000)/60)/60)/24))+1; return ret; }; /** * Adjust from 0-6 base week to 1-7 base week * @param d integer for day of week * @return Integer day number for 1-7 base week */ this.convertOneBase = function (d) { return d == 0 ? 7 : d; }; this.getTwoDigitYear = function (yr) { // Add a millenium to take care of years before the year 1000, // (e.g, the year 7) since we're only taking the last two digits // If we overshoot, it doesn't matter var millenYear = yr + 1000; var str = millenYear.toString(); str = str.substr(2); // Get the last two digits return str }; /** @name date#getMeridiem @public @function @return {String} Return 'AM' or 'PM' based on hour in 24-hour format @description Return 'AM' or 'PM' based on hour in 24-hour format @param {Number} h The hour to check */ this.getMeridiem = function (h) { return h > 11 ? this.meridiem.PM : this.meridiem.AM; }; // Compat this.getMeridian = this.getMeridiem; /** @name date#hrMil2Std @public @function @return {String} Return a 12 hour version of the given time @description Convert a 24-hour formatted hour to 12-hour format @param {String} hour The hour to convert */ this.hrMil2Std = function (hour) { var h = typeof hour == 'number' ? hour : parseInt(hour); var str = h > 12 ? h - 12 : h; str = str == 0 ? 12 : str; return str; }; /** @name date#hrStd2Mil @public @function @return {String} Return a 24 hour version of the given time @description Convert a 12-hour formatted hour with meridian flag to 24-hour format @param {String} hour The hour to convert @param {Boolean} pm hour is PM then this should be true */ this.hrStd2Mil = function (hour, pm) { var h = typeof hour == 'number' ? hour : parseInt(hour); var str = ''; // PM if (pm) { str = h < 12 ? (h+12) : h; } // AM else { str = h == 12 ? 0 : h; } return str; }; // Constants for use in this.add var dateParts = { YEAR: 'year' , MONTH: 'month' , DAY: 'day' , HOUR: 'hour' , MINUTE: 'minute' , SECOND: 'second' , MILLISECOND: 'millisecond' , QUARTER: 'quarter' , WEEK: 'week' , WEEKDAY: 'weekday' }; // Create a map for singular/plural lookup, e.g., day/days var datePartsMap = {}; for (var p in dateParts) { datePartsMap[dateParts[p]] = dateParts[p]; datePartsMap[dateParts[p] + 's'] = dateParts[p]; } this.dateParts = dateParts; /** @name date#add @public @function @return {Date} Incremented date @description Add to a Date in intervals of different size, from milliseconds to years @param {Date} dt Date (or timestamp Number), date to increment @param {String} interv a constant representing the interval, e.g. YEAR, MONTH, DAY. See this.dateParts @param {Number} incr how much to add to the date */ this.add = function (dt, interv, incr) { if (typeof dt == 'number') { dt = new Date(dt); } function fixOvershoot() { if (sum.getDate() < dt.getDate()) { sum.setDate(0); } } var key = datePartsMap[interv]; var sum = new Date(dt); switch (key) { case dateParts.YEAR: sum.setFullYear(dt.getFullYear()+incr); // Keep increment/decrement from 2/29 out of March fixOvershoot(); break; case dateParts.QUARTER: // Naive quarter is just three months incr*=3; // fallthrough... case dateParts.MONTH: sum.setMonth(dt.getMonth()+incr); // Reset to last day of month if you overshoot fixOvershoot(); break; case dateParts.WEEK: incr*=7; // fallthrough... case dateParts.DAY: sum.setDate(dt.getDate() + incr); break; case dateParts.WEEKDAY: //FIXME: assumes Saturday/Sunday weekend, but even this is not fixed. // There are CLDR entries to localize this. var dat = dt.getDate(); var weeks = 0; var days = 0; var strt = 0; var trgt = 0; var adj = 0; // Divide the increment time span into weekspans plus leftover days // e.g., 8 days is one 5-day weekspan / and two leftover days // Can't have zero leftover days, so numbers divisible by 5 get // a days value of 5, and the remaining days make up the number of weeks var mod = incr % 5; if (mod == 0) { days = (incr > 0) ? 5 : -5; weeks = (incr > 0) ? ((incr-5)/5) : ((incr+5)/5); } else { days = mod; weeks = parseInt(incr/5); } // Get weekday value for orig date param strt = dt.getDay(); // Orig date is Sat / positive incrementer // Jump over Sun if (strt == 6 && incr > 0) { adj = 1; } // Orig date is Sun / negative incrementer // Jump back over Sat else if (strt == 0 && incr < 0) { adj = -1; } // Get weekday val for the new date trgt = strt + days; // New date is on Sat or Sun if (trgt == 0 || trgt == 6) { adj = (incr > 0) ? 2 : -2; } // Increment by number of weeks plus leftover days plus // weekend adjustments sum.setDate(dat + (7*weeks) + days + adj); break; case dateParts.HOUR: sum.setHours(sum.getHours()+incr); break; case dateParts.MINUTE: sum.setMinutes(sum.getMinutes()+incr); break; case dateParts.SECOND: sum.setSeconds(sum.getSeconds()+incr); break; case dateParts.MILLISECOND: sum.setMilliseconds(sum.getMilliseconds()+incr); break; default: // Do nothing break; } return sum; // Date }; /** @name date#diff @public @function @return {Number} number of (interv) units apart that the two dates are @description Get the difference in a specific unit of time (e.g., number of months, weeks, days, etc.) between two dates. @param {Date} date1 First date to check @param {Date} date2 Date to compate `date1` with @param {String} interv a constant representing the interval, e.g. YEAR, MONTH, DAY. See this.dateParts */ this.diff = function (date1, date2, interv) { // date1 // Date object or Number equivalent // // date2 // Date object or Number equivalent // // interval // A constant representing the interval, e.g. YEAR, MONTH, DAY. See this.dateParts. // Accept timestamp input if (typeof date1 == 'number') { date1 = new Date(date1); } if (typeof date2 == 'number') { date2 = new Date(date2); } var yeaDiff = date2.getFullYear() - date1.getFullYear(); var monDiff = (date2.getMonth() - date1.getMonth()) + (yeaDiff * 12); var msDiff = date2.getTime() - date1.getTime(); // Millisecs var secDiff = msDiff/1000; var minDiff = secDiff/60; var houDiff = minDiff/60; var dayDiff = houDiff/24; var weeDiff = dayDiff/7; var delta = 0; // Integer return value var key = datePartsMap[interv]; switch (key) { case dateParts.YEAR: delta = yeaDiff; break; case dateParts.QUARTER: var m1 = date1.getMonth(); var m2 = date2.getMonth(); // Figure out which quarter the months are in var q1 = Math.floor(m1/3) + 1; var q2 = Math.floor(m2/3) + 1; // Add quarters for any year difference between the dates q2 += (yeaDiff * 4); delta = q2 - q1; break; case dateParts.MONTH: delta = monDiff; break; case dateParts.WEEK: // Truncate instead of rounding // Don't use Math.floor -- value may be negative delta = parseInt(weeDiff); break; case dateParts.DAY: delta = dayDiff; break; case dateParts.WEEKDAY: var days = Math.round(dayDiff); var weeks = parseInt(days/7); var mod = days % 7; // Even number of weeks if (mod == 0) { days = weeks*5; } else { // Weeks plus spare change (< 7 days) var adj = 0; var aDay = date1.getDay(); var bDay = date2.getDay(); weeks = parseInt(days/7); mod = days % 7; // Mark the date advanced by the number of // round weeks (may be zero) var dtMark = new Date(date1); dtMark.setDate(dtMark.getDate()+(weeks*7)); var dayMark = dtMark.getDay(); // Spare change days -- 6 or less if (dayDiff > 0) { switch (true) { // Range starts on Sat case aDay == 6: adj = -1; break; // Range starts on Sun case aDay == 0: adj = 0; break; // Range ends on Sat case bDay == 6: adj = -1; break; // Range ends on Sun case bDay == 0: adj = -2; break; // Range contains weekend case (dayMark + mod) > 5: adj = -2; break; default: // Do nothing break; } } else if (dayDiff < 0) { switch (true) { // Range starts on Sat case aDay == 6: adj = 0; break; // Range starts on Sun case aDay == 0: adj = 1; break; // Range ends on Sat case bDay == 6: adj = 2; break; // Range ends on Sun case bDay == 0: adj = 1; break; // Range contains weekend case (dayMark + mod) < 0: adj = 2; break; default: // Do nothing break; } } days += adj; days -= (weeks*2); } delta = days; break; case dateParts.HOUR: delta = houDiff; break; case dateParts.MINUTE: delta = minDiff; break; case dateParts.SECOND: delta = secDiff; break; case dateParts.MILLISECOND: delta = msDiff; break; default: // Do nothing break; } // Round for fractional values and DST leaps return Math.round(delta); // Number (integer) }; /** @name date#parse @public @function @return {Date} a JavaScript Date object @description Convert various sorts of strings to JavaScript Date objects @param {String} val The string to convert to a Date */ this.parse = function (val, options) { var dt , opts = options || {} , matches , reordered , off , posOff , offHours , offMinutes , offSeconds , curr , stamp , utc; // Yay, we have a date, use it as-is if (val instanceof Date || typeof val.getFullYear == 'function') { dt = val; } // Timestamp? else if (typeof val == 'number') { dt = new Date(val); } // String or Array else { // Value preparsed, looks like [yyyy, mo, dd, hh, mi, ss, ms, (offset?)] if (_isArray(val)) { matches = val; matches.unshift(null); matches[8] = null; } // Oh, crap, it's a string -- parse this bitch else if (typeof val == 'string') { matches = val.match(_DATETIME_PAT); // Stupid US-only format? if (!matches) { matches = val.match(_US_DATE_PAT); if (matches) { reordered = [matches[0], matches[3], matches[1], matches[2]]; // Pad the results to the same length as ISO8601 reordered[8] = null; matches = reordered; } } // Time-stored-in-Date hack? if (!matches) { matches = val.match(_TIME_PAT); if (matches) { reordered = [matches[0], 0, 1, 0, matches[1], matches[2], matches[3], matches[4], null]; matches = reordered; } } } // Sweet, the regex actually parsed it into something useful if (matches) { matches.shift(); // First match is entire match, DO NOT WANT off = matches.pop(); // If there's an offset (or the 'Z' non-offset offset), use UTC // methods to set everything if (off) { if (off == 'Z') { utc = true; offSeconds = 0; } else { utc = false; // Convert from extended to basic if necessary off = off.replace(/:/g, ''); // '+0000' will still be zero if (parseInt(off, 10) === 0) { utc = true; } else { posOff = off.indexOf('+') === 0; // Strip plus or minus off = off.substr(1); offHours = parseInt(off.substr(0, 2), 10); offMinutes = off.substr(2, 2); if (offMinutes) { offMinutes = parseInt(offMinutes, 10); } else { offMinutes = 0; } offSeconds = off.substr(4, 2); if (offSeconds) { offSeconds = parseInt(offSeconds, 10); } else { offSeconds = 0; } offSeconds += (offMinutes * 60) offSeconds += (offHours * 60 * 60); if (!posOff) { offSeconds = 0 - offSeconds; } } } } dt = new Date(0); // Stupid zero-based months matches[1] = parseInt(matches[1], 10) - 1; // Specific offset, iterate the array and set each date property // using UTC setters, then adjust time using offset if (off) { for (var i = matches.length - 1; i > -1; i--) { curr = parseInt(matches[i], 10) || 0; dt['setUTC' + _dateMethods[i]](curr); } // Add any offset dt.setSeconds(dt.getSeconds() - offSeconds); } // Otherwise we know nothing about the offset, just iterate the // array and set each date property using regular setters else { var lastValIndex; for (var i = matches.length - 1; i > -1; i--) { if (matches[i]) { curr = parseInt(matches[i], 10); if (typeof lastValIndex == 'undefined') { lastValIndex = i; } } else { curr = 0; } dt['set' + _dateMethods[i]](curr); } if (opts.setMax) { for (var i = lastValIndex + 1, ii = matches.length; i < ii; i++) { switch (i) { case 3: dt['set' + _dateMethods[i]](23); break; case 4: case 5: dt['set' + _dateMethods[i]](59); break; case 6: dt.setMilliseconds(999); break; } } } } } // Shit, last-ditch effort using Date.parse else { stamp = Date.parse(val); // Failures to parse yield NaN if (!isNaN(stamp)) { dt = new Date(stamp); } } } return dt || null; }; /** @name date#relativeTime @public @function @return {String} A string describing the amount of time ago the passed-in Date is @description Convert a Date to an English sentence representing how long ago the Date was @param {Date} dt The Date to to convert to a relative time string @param {Object} [opts] @param {Boolean} [opts.abbreviated=false] Use short strings (e.g., '<1m') for the relative-time string */ this.relativeTime = function (dt, options) { var opts = options || {} , now = opts.now || new Date() , abbr = opts.abbreviated || false , format = opts.format || '%F %T' // Diff in seconds , diff = (now.getTime() - dt.getTime()) / 1000 , ret , num , hour = 60*60 , day = 24*hour , week = 7*day , month = 30*day; switch (true) { case diff < 60: ret = abbr ? '<1m' : 'less than a minute ago'; break; case diff < 120: ret = abbr ? '1m' : 'about a minute ago'; break; case diff < (45*60): num = parseInt((diff / 60), 10); ret = abbr ? num + 'm' : num + ' minutes ago'; break; case diff < (2*hour): ret = abbr ? '1h' : 'about an hour ago'; break; case diff < (1*day): num = parseInt((diff / hour), 10); ret = abbr ? num + 'h' : 'about ' + num + ' hours ago'; break; case diff < (2*day): ret = abbr ? '1d' : 'one day ago'; break; case diff < (7*day): num = parseInt((diff / day), 10); ret = abbr ? num + 'd' : 'about ' + num + ' days ago'; break; case diff < (11*day): ret = abbr ? '1w': 'one week ago'; break; case diff < (1*month): num = Math.round(diff / week); ret = abbr ? num + 'w' : 'about ' + num + ' weeks ago'; break; default: ret = date.strftime(dt, format); break; } return ret; }; /** @name date#toISO8601 @public @function @return {String} A string describing the amount of time ago @description Convert a Date to an ISO8601-formatted string @param {Date} dt The Date to to convert to an ISO8601 string */ var _pad = function (n) { return n < 10 ? '0' + n : n; }; this.toISO8601 = function (dt, options) { var opts = options || {} , off = dt.getTimezoneOffset() , offHours , offMinutes , str = this.strftime(dt, '%F') + 'T' + this.strftime(dt, '%T') + '.' + string.lpad(dt.getMilliseconds(), '0', 3); if (opts.tz) { // Pos and neg numbers are both truthy; only // zero is falsy if (off && !opts.utc) { str += off > 0 ? '-' : '+'; offHours = parseInt(off / 60, 10); str += string.lpad(offHours, '0', 2); offMinutes = off % 60; if (offMinutes) { str += string.lpad(offMinutes, '0', 2); } } else { str += 'Z'; } } return str; }; // Alias this.toIso8601 = this.toISO8601; this.toUTC = function (dt) { return new Date( dt.getUTCFullYear() , dt.getUTCMonth() , dt.getUTCDate() , dt.getUTCHours() , dt.getUTCMinutes() , dt.getUTCSeconds() , dt.getUTCMilliseconds()); }; })(); module.exports = date; utilities-1.0.4/lib/event_buffer.js000066400000000000000000000061011240177617000173100ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* This is a very simple buffer for a predetermined set of events. It is unbounded. It forwards all arguments to any outlet emitter attached with sync(). Example: var source = new Stream() , dest = new EventEmitter() , buff = new EventBuffer(source) , data = ''; dest.on('data', function (d) { data += d; }); source.writeable = true; source.readable = true; source.emit('data', 'abcdef'); source.emit('data', '123456'); buff.sync(dest); */ /** @name EventBuffer @namespace EventBuffer @constructor */ var EventBuffer = function (src, events) { // By default, we service the default stream events var self = this , streamEvents = ['data', 'end', 'error', 'close', 'fd', 'drain', 'pipe']; this.events = events || streamEvents; this.emitter = src; this.eventBuffer = []; this.outlet = null; this.events.forEach(function (name) { self.emitter.addListener(name, function () { self.proxyEmit(name, arguments); }); }); }; EventBuffer.prototype = new (function () { /** @name EventBuffer#proxyEmit @public @function @description Emit an event by name and arguments or add it to the buffer if no outlet is set @param {String} name The name to use for the event @param {Array} args An array of arguments to emit */ this.proxyEmit = function (name, args) { if (this.outlet) { this.emit(name, args); } else { this.eventBuffer.push({name: name, args: args}); } }; /** @name EventBuffer#emit @public @function @description Emit an event by name and arguments @param {String} name The name to use for the event @param {Array} args An array of arguments to emit */ this.emit = function (name, args) { // Prepend name to args var outlet = this.outlet; Array.prototype.splice.call(args, 0, 0, name); outlet.emit.apply(outlet, args); }; /** @name EventBuffer#sync @public @function @description Flush the buffer and continue piping new events to the outlet @param {Object} outlet The emitter to send events to */ this.sync = function (outlet) { var buffer = this.eventBuffer , bufferItem; this.outlet = outlet; while ((bufferItem = buffer.shift())) { this.emit(bufferItem.name, bufferItem.args); } }; })(); EventBuffer.prototype.constructor = EventBuffer; module.exports.EventBuffer = EventBuffer; utilities-1.0.4/lib/file.js000066400000000000000000000345221240177617000155650ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var fs = require('fs') , path = require('path') , DEFAULT_INCLUDE_PAT = /\.(js|coffee|css|less|scss)$/ , DEFAULT_EXCLUDE_PAT = /\.git|node_modules/ , logger; var logger = new (function () { var out; try { out = require('./log'); } catch (e) { out = console; } this.log = function (o) { out.log(o); }; })(); /** @name file @namespace file */ var fileUtils = new (function () { var _copyFile , _copyDir , _readDir , _rmDir , _watch; // Recursively copy files and directories _copyFile = function (fromPath, toPath, opts) { var from = path.normalize(fromPath) , to = path.normalize(toPath) , options = opts || {} , fromStat , toStat , destExists , destDoesNotExistErr , content , filename , dirContents , targetDir; fromStat = fs.statSync(from); try { //console.dir(to + ' destExists'); toStat = fs.statSync(to); destExists = true; } catch(e) { //console.dir(to + ' does not exist'); destDoesNotExistErr = e; destExists = false; } // Destination dir or file exists, copy into (directory) // or overwrite (file) if (destExists) { // If there's a rename-via-copy file/dir name passed, use it. // Otherwise use the actual file/dir name filename = options.rename || path.basename(from); // Copying a directory if (fromStat.isDirectory()) { dirContents = fs.readdirSync(from); targetDir = path.join(to, filename); // We don't care if the target dir already exists try { fs.mkdirSync(targetDir, options.mode || parseInt(755, 8)); } catch(e) { if (e.code != 'EEXIST') { throw e; } } for (var i = 0, ii = dirContents.length; i < ii; i++) { //console.log(dirContents[i]); _copyFile(path.join(from, dirContents[i]), targetDir); } } // Copying a file else { content = fs.readFileSync(from); // Copy into dir if (toStat.isDirectory()) { //console.log('copy into dir ' + to); fs.writeFileSync(path.join(to, filename), content); } // Overwrite file else { //console.log('overwriting ' + to); fs.writeFileSync(to, content); } } } // Dest doesn't exist, can't create it else { throw destDoesNotExistErr; } }; _copyDir = function (from, to, opts) { var createDir = opts.createDir; }; // Return the contents of a given directory _readDir = function (dirPath) { var dir = path.normalize(dirPath) , paths = [] , ret = [dir] , msg; try { paths = fs.readdirSync(dir); } catch (e) { msg = 'Could not read path ' + dir + '\n'; if (e.stack) { msg += e.stack; } throw new Error(msg); } paths.forEach(function (p) { var curr = path.join(dir, p); var stat = fs.statSync(curr); if (stat.isDirectory()) { ret = ret.concat(_readDir(curr)); } else { ret.push(curr); } }); return ret; }; // Remove the given directory _rmDir = function (dirPath) { var dir = path.normalize(dirPath) , paths = []; paths = fs.readdirSync(dir); paths.forEach(function (p) { var curr = path.join(dir, p); var stat = fs.statSync(curr); if (stat.isDirectory()) { _rmDir(curr); } else { try { fs.unlinkSync(curr); } catch(e) { if (e.code === 'EPERM') { fs.chmodSync(curr, parseInt(666, 8)); fs.unlinkSync(curr); } else { throw e; } } } }); fs.rmdirSync(dir); }; // Recursively watch files with a callback _watch = function () { var args = Array.prototype.slice.call(arguments) , filePath , opts , callback , inclPat , exclPat , createWatcher; filePath = args.shift(); callback = args.pop(); opts = args.pop() || {}; inclPat = opts.includePattern || DEFAULT_INCLUDE_PAT; exclPat = opts.excludePattern || DEFAULT_EXCLUDE_PAT; opts.level = opts.level || 1; createWatcher = function (watchPath) { if (!exclPat.test(watchPath)) { fs.watch(watchPath, function (ev, p) { if (inclPat.test(p) && !exclPat.test(p)) { callback(path.join(watchPath, p)); } }); } }; fs.stat(filePath, function (err, stats) { if (err) { return false; } // Watch files at the top level if (stats.isFile() && opts.level == 1) { createWatcher(filePath); opts.level++; } else if (stats.isDirectory()) { createWatcher(filePath); opts.level++; fs.readdir(filePath, function (err, files) { if (err) { return log.fatal(err); } for (var f in files) { _watch(path.join(filePath, files[f]), opts, callback); } }); } }); }; /** @name file#cpR @public @function @description Copies a directory/file to a destination @param {String} fromPath The source path to copy from @param {String} toPath The destination path to copy to @param {Object} opts Options to use @param {Boolean} [opts.silent] If false then will log the command */ this.cpR = function (fromPath, toPath, options) { var from = path.normalize(fromPath) , to = path.normalize(toPath) , toStat , doesNotExistErr , paths , filename , opts = options || {}; if (!opts.silent) { logger.log('cp -r ' + fromPath + ' ' + toPath); } if (from == to) { throw new Error('Cannot copy ' + from + ' to itself.'); } // Handle rename-via-copy try { toStat = fs.statSync(to); } catch(e) { doesNotExistErr = e; // Get abs path so it's possible to check parent dir if (!this.isAbsolute(to)) { to = path.join(process.cwd() , to); } // Save the file/dir name filename = path.basename(to); // See if a parent dir exists, so there's a place to put the /// renamed file/dir (resets the destination for the copy) to = path.dirname(to); try { toStat = fs.statSync(to); } catch(e) {} if (toStat && toStat.isDirectory()) { // Set the rename opt to pass to the copy func, will be used // as the new file/dir name opts.rename = filename; //console.log('filename ' + filename); } else { throw doesNotExistErr; } } _copyFile(from, to, opts); }; /** @name file#mkdirP @public @function @description Create the given directory(ies) using the given mode permissions @param {String} dir The directory to create @param {Number} mode The mode to give the created directory(ies)(Default: 0755) */ this.mkdirP = function (dir, mode) { var dirPath = path.normalize(dir) , paths = dirPath.split(/\/|\\/) , currPath = '' , next; if (paths[0] == '' || /^[A-Za-z]+:/.test(paths[0])) { currPath = paths.shift() || '/'; currPath = path.join(currPath, paths.shift()); //console.log('basedir'); } while ((next = paths.shift())) { if (next == '..') { currPath = path.join(currPath, next); continue; } currPath = path.join(currPath, next); try { //console.log('making ' + currPath); fs.mkdirSync(currPath, mode || parseInt(755, 8)); } catch(e) { if (e.code != 'EEXIST') { throw e; } } } }; /** @name file#readdirR @public @function @return {Array} Returns the contents as an Array, can be configured via opts.format @description Reads the given directory returning it's contents @param {String} dir The directory to read @param {Object} opts Options to use @param {String} [opts.format] Set the format to return(Default: Array) */ this.readdirR = function (dir, opts) { var options = opts || {} , format = options.format || 'array' , ret; ret = _readDir(dir); return format == 'string' ? ret.join('\n') : ret; }; /** @name file#rmRf @public @function @description Deletes the given directory/file @param {String} p The path to delete, can be a directory or file @param {Object} opts Options to use @param {String} [opts.silent] If false then logs the command */ this.rmRf = function (p, options) { var stat , opts = options || {}; if (!opts.silent) { logger.log('rm -rf ' + p); } try { stat = fs.statSync(p); if (stat.isDirectory()) { _rmDir(p); } else { fs.unlinkSync(p); } } catch (e) {} }; /** @name file#isAbsolute @public @function @return {Boolean/String} If it's absolute the first character is returned otherwise false @description Checks if a given path is absolute or relative @param {String} p Path to check */ this.isAbsolute = function (p) { var match = /^[A-Za-z]+:\\|^\//.exec(p); if (match && match.length) { return match[0]; } return false; }; /** @name file#absolutize @public @function @return {String} Returns the absolute path for the given path @description Returns the absolute path for the given path @param {String} p The path to get the absolute path for */ this.absolutize = function (p) { if (this.isAbsolute(p)) { return p; } else { return path.join(process.cwd(), p); } }; /** Given a patern, return the base directory of it (ie. the folder that will contain all the files matching the path). eg. file.basedir('/test/**') => '/test/' Path ending by '/' are considerd as folder while other are considerd as files, eg.: file.basedir('/test/a/') => '/test/a' file.basedir('/test/a') => '/test' The returned path always end with a '/' so we have: file.basedir(file.basedir(x)) == file.basedir(x) */ this.basedir = function (pathParam) { var basedir = '' , parts , part , pos = 0 , p = pathParam || ''; // If the path has a leading asterisk, basedir is the current dir if (p.indexOf('*') == 0 || p.indexOf('**') == 0) { return '.'; } // always consider .. at the end as a folder and not a filename if (/(?:^|\/|\\)\.\.$/.test(p.slice(-3))) { p += '/'; } parts = p.split(/\\|\//); for (var i = 0, l = parts.length - 1; i < l; i++) { part = parts[i]; if (part.indexOf('*') > -1 || part.indexOf('**') > -1) { break; } pos += part.length + 1; basedir += part + p[pos - 1]; } if (!basedir) { basedir = '.'; } // Strip trailing slashes if (!(basedir == '\\' || basedir == '/')) { basedir = basedir.replace(/\\$|\/$/, ''); } return basedir; }; /** @name file#searchParentPath @public @function @description Search for a directory/file in the current directory and parent directories @param {String} p The path to search for @param {Function} callback The function to call once the path is found */ this.searchParentPath = function (location, beginPath, callback) { if (typeof beginPath === 'function' && !callback) { callback = beginPath; beginPath = process.cwd(); } var cwd = beginPath || process.cwd(); if (!location) { // Return if no path is given return; } var relPath = '' , i = 5 // Only search up to 5 directories , pathLoc , pathExists; while (--i >= 0) { pathLoc = path.join(cwd, relPath, location); pathExists = this.existsSync(pathLoc); if (pathExists) { callback && callback(undefined, pathLoc); break; } else { // Dir could not be found if (i === 0) { callback && callback(new Error("Path \"" + pathLoc + "\" not found"), undefined); break; } // Add a relative parent directory relPath += '../'; // Switch to relative parent directory process.chdir(path.join(cwd, relPath)); } } }; /** @name file#watch @public @function @description Watch a given path then calls the callback once a change occurs @param {String} path The path to watch @param {Function} callback The function to call when a change occurs */ this.watch = function () { _watch.apply(this, arguments); }; // Compatibility for fs.exists(0.8) and path.exists(0.6) this.exists = (typeof fs.exists === 'function') ? fs.exists : path.exists; // Compatibility for fs.existsSync(0.8) and path.existsSync(0.6) this.existsSync = (typeof fs.existsSync === 'function') ? fs.existsSync : path.existsSync; /** @name file#requireLocal @public @function @return {Object} The given module is returned @description Require a local module from the node_modules in the current directory @param {String} module The module to require @param {String} message An option message to throw if the module doesn't exist */ this.requireLocal = function (module, message) { var dep; // Try to require in the application directory try { dep = require(path.join(process.cwd(), 'node_modules', module)); } catch(err) { if (message) { throw new Error(message); } throw new Error('Module "' + module + '" could not be found as a ' + 'local module. Please install it by doing "npm install ' + module + '"'); } return dep; }; })(); module.exports = fileUtils; utilities-1.0.4/lib/i18n.js000066400000000000000000000037001240177617000154170ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var core = require('./core') , i18n; var DEFAULT_LOCALE = 'en-us'; i18n = new (function () { var _defaultLocale = DEFAULT_LOCALE , _strings = {}; this.getText = function (key, opts, locale) { var currentLocale = locale || _defaultLocale , currentLocaleStrings = _strings[currentLocale] || {} , defaultLocaleStrings = _strings[_defaultLocale] || {} , str = currentLocaleStrings[key] || defaultLocaleStrings[key] || "[[" + key + "]]"; for (var p in opts) { str = str.replace(new RegExp('\\{' + p + '\\}', 'g'), opts[p]); } return str; }; this.getDefaultLocale = function () { return _defaultLocale; }; this.setDefaultLocale = function (locale) { _defaultLocale = locale; }; this.loadLocale = function (locale, strings) { _strings[locale] = _strings[locale] || {}; core.mixin(_strings[locale], strings); }; })(); i18n.I18n = function (locale) { var _locale = locale || i18n.getDefaultLocale(); this.getLocale = function (locale) { return _locale; }; this.setLocale = function (locale) { _locale = locale; }; this.getText = function (key, opts, locale) { return i18n.getText(key, opts || {}, locale || _locale); }; this.t = this.getText; }; module.exports = i18n; utilities-1.0.4/lib/index.js000066400000000000000000000033551240177617000157550ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var utils = {} // Core methods , core = require('./core') // Namespaces with methods , string = require('./string') , file = require('./file') , async = require('./async') , i18n = require('./i18n') , uri = require('./uri') , array = require('./array') , object = require('./object') , date = require('./date') , request = require('./request') , log = require('./log') , network = require('./network') // Third-party -- remove this if possible , inflection = require('./inflection') // Constructors , EventBuffer = require('./event_buffer').EventBuffer , XML = require('./xml').XML , SortedCollection = require('./sorted_collection').SortedCollection; core.mixin(utils, core); utils.string = string; utils.file = file; utils.async = async; utils.i18n = i18n; utils.uri = uri; utils.array = array; utils.object = object; utils.date = date; utils.request = request; utils.log = log; utils.network = network; utils.inflection = inflection; utils.SortedCollection = SortedCollection; utils.EventBuffer = EventBuffer; utils.XML = XML; module.exports = utils; utilities-1.0.4/lib/inflection.js000066400000000000000000000177521240177617000170060ustar00rootroot00000000000000/* * Copyright (c) 2010 George Moschovitis, http://www.gmosx.com * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * A port of the Rails/ActiveSupport Inflector class * http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html */ /** @name inflection @namespace inflection */ var inflection = new (function () { /** @name inflection#inflections @public @object @description A list of rules and replacements for different inflection types */ this.inflections = { plurals: [] , singulars: [] , uncountables: [] }; var self = this , setInflection , setPlural , setSingular , setUncountable , setIrregular; // Add a new inflection rule/replacement to the beginning of the array for the // inflection type setInflection = function (type, rule, replacement) { self.inflections[type].unshift([rule, replacement]); }; // Add a new plural inflection rule setPlural = function (rule, replacement) { setInflection('plurals', rule, replacement); }; // Add a new singular inflection rule setSingular = function (rule, replacement) { setInflection('singulars', rule, replacement); }; // Add a new irregular word to the inflection list, by a given singular and plural inflection setIrregular = function (singular, plural) { if (singular.substr(0, 1).toUpperCase() == plural.substr(0, 1).toUpperCase()) { setPlural(new RegExp("(" + singular.substr(0, 1) + ")" + singular.substr(1) + "$", "i"), '$1' + plural.substr(1)); setPlural(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$", "i"), '$1' + plural.substr(1)); setSingular(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$", "i"), '$1' + singular.substr(1)); } else { setPlural(new RegExp(singular.substr(0, 1).toUpperCase() + singular.substr(1) + "$"), plural.substr(0, 1).toUpperCase() + plural.substr(1)); setPlural(new RegExp(singular.substr(0, 1).toLowerCase() + singular.substr(1) + "$"), plural.substr(0, 1).toLowerCase() + plural.substr(1)); setPlural(new RegExp(plural.substr(0, 1).toUpperCase() + plural.substr(1) + "$"), plural.substr(0, 1).toUpperCase() + plural.substr(1)); setPlural(new RegExp(plural.substr(0, 1).toLowerCase() + plural.substr(1) + "$"), plural.substr(0, 1).toLowerCase() + plural.substr(1)); setSingular(new RegExp(plural.substr(0, 1).toUpperCase() + plural.substr(1) + "$"), singular.substr(0, 1).toUpperCase() + singular.substr(1)); setSingular(new RegExp(plural.substr(0, 1).toLowerCase() + plural.substr(1) + "$"), singular.substr(0, 1).toLowerCase() + singular.substr(1)); } }; // Add a new word to the uncountable inflection list setUncountable = function (word) { self.inflections.uncountables[word] = true; }; // Create inflections (function () { setPlural(/$/, "s"); setPlural(/s$/i, "s"); setPlural(/(ax|test)is$/i, "$1es"); setPlural(/(octop|vir)us$/i, "$1i"); setPlural(/(octop|vir)i$/i, "$1i"); setPlural(/(alias|status)$/i, "$1es"); setPlural(/(bu)s$/i, "$1ses"); setPlural(/(buffal|tomat)o$/i, "$1oes"); setPlural(/([ti])a$/i, "$1a"); setPlural(/([ti])um$/i, "$1a"); setPlural(/sis$/i, "ses"); setPlural(/ses$/i, "ses"); setPlural(/(?:([^f])fe|([lr])f)$/i, "$1$2ves"); setPlural(/(hive)$/i, "$1s"); setPlural(/([^aeiouy]|qu)y$/i, "$1ies"); setPlural(/(x|ch|ss|sh)$/i, "$1es"); setPlural(/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"); setPlural(/([m|l])ouse$/i, "$1ice"); setPlural(/([m|l])ice$/i, "$1ice"); setPlural(/^(ox)$/i, "$1en"); setPlural(/^(ox)en$/i, "$1en"); setPlural(/(quiz)$/i, "$1zes"); setSingular(/s$/i, "") setSingular(/ss$/i, "ss") setSingular(/(n)ews$/i, "$1ews") setSingular(/([ti])um$/i, "$1um") setSingular(/([ti])a$/i, "$1um") setSingular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis") setSingular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)sis$/i, "$1$2sis") setSingular(/(^analy)ses$/i, "$1sis") setSingular(/(^analy)sis$/i, "$1sis") setSingular(/([^f])ves$/i, "$1fe") setSingular(/(hive)s$/i, "$1") setSingular(/(tive)s$/i, "$1") setSingular(/([lr])ves$/i, "$1f") setSingular(/([^aeiouy]|qu)ies$/i, "$1y") setSingular(/(s)eries$/i, "$1eries") setSingular(/(m)ovies$/i, "$1ovie") setSingular(/(x|ch|ss|sh)es$/i, "$1") setSingular(/([m|l])ice$/i, "$1ouse") setSingular(/([m|l])ouse$/i, "$1ouse") setSingular(/(bus)es$/i, "$1") setSingular(/(bus)$/i, "$1") setSingular(/(o)es$/i, "$1") setSingular(/(shoe)s$/i, "$1") setSingular(/(cris|ax|test)es$/i, "$1is") setSingular(/(cris|ax|test)is$/i, "$1is") setSingular(/(octop|vir)i$/i, "$1us") setSingular(/(octop|vir)us$/i, "$1us") setSingular(/(alias|status)es$/i, "$1") setSingular(/(alias|status)$/i, "$1") setSingular(/^(ox)en/i, "$1") setSingular(/(vert|ind)ices$/i, "$1ex") setSingular(/(matr)ices$/i, "$1ix") setSingular(/(quiz)zes$/i, "$1") setSingular(/(database)s$/i, "$1") setIrregular("person", "people"); setIrregular("man", "men"); setIrregular("child", "children"); setIrregular("sex", "sexes"); setIrregular("move", "moves"); setIrregular("cow", "kine"); setUncountable("equipment"); setUncountable("information"); setUncountable("rice"); setUncountable("money"); setUncountable("species"); setUncountable("series"); setUncountable("fish"); setUncountable("sheep"); setUncountable("jeans"); })(); /** @name inflection#parse @public @function @return {String} The inflection of the word from the type given @description Parse a word from the given inflection type @param {String} type A type of the inflection to use @param {String} word the word to parse */ this.parse = function (type, word) { var lowWord = word.toLowerCase() , inflections = this.inflections[type]; if (this.inflections.uncountables[lowWord]) { return word; } var i = -1; while (++i < inflections.length) { var rule = inflections[i][0] , replacement = inflections[i][1]; if (rule.test(word)) { return word.replace(rule, replacement) } } return word; }; /** @name inflection#pluralize @public @function @return {String} The plural inflection for the given word @description Create a plural inflection for a word @param {String} word the word to create a plural version for */ this.pluralize = function (word) { return this.parse('plurals', word); }; /** @name inflection#singularize @public @function @return {String} The singular inflection for the given word @description Create a singular inflection for a word @param {String} word the word to create a singular version for */ this.singularize = function (word) { return this.parse('singulars', word); }; })(); module.exports = inflection; utilities-1.0.4/lib/log.js000066400000000000000000000020611240177617000154200ustar00rootroot00000000000000var util = require('util') , log , _logger , _levels , _serialize , _output; _levels = { 'debug': 'log' , 'log' : 'log' , 'info': 'info' , 'notice': 'info' , 'warning': 'warn' , 'warn': 'warn' , 'error': 'error' , 'critical': 'error' , 'alert': 'error' , 'emergency': 'error' }; _serialize = function (obj) { var out; if (typeof obj == 'string') { out = obj; } else { out = util.inspect(obj); } return out; }; _output = function (obj, level) { var out = _serialize(obj); if (_logger) { _logger[level](out); } else { console[_levels[level]](out); } }; log = function (obj) { _output(obj, 'info'); }; log.registerLogger = function (logger) { // Malkovitch, Malkovitch if (logger === log) { return; } _logger = logger; }; (function () { var level; for (var p in _levels) { (function (p) { level = _levels[p]; log[p] = function (obj) { _output(obj, p); }; })(p); } // Also handle 'access', not an actual level log.access = log.info; })(); module.exports = log; utilities-1.0.4/lib/network.js000066400000000000000000000034021240177617000163300ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var network , net = require('net'); /** @name network @namespace network */ network = new (function () { /** @name network#isPortOpen @public @function @description Checks if the given port in the given host is open @param {Number} port number @param {String} host @param {Function} callback Callback function -- should be in the format of function(err, result) {} */ this.isPortOpen = function (port, host, callback) { if (typeof host === 'function' && !callback) { callback = host; host = 'localhost'; } var isOpen = false , connection , error; connection = net.createConnection(port, host, function () { isOpen = true; connection.end(); }); connection.on('error', function (err) { // We ignore 'ECONNREFUSED' as it simply indicates the port isn't open. // Anything else is reported if(err.code !== 'ECONNREFUSED') { error = err; } }); connection.setTimeout(400, function () { connection.end(); }); connection.on('close', function () { callback && callback(error, isOpen); }); }; })(); module.exports = network;utilities-1.0.4/lib/object.js000066400000000000000000000061451240177617000161140ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** @name object @namespace object */ var object = new (function () { /** @name object#merge @public @function @return {Object} Returns the merged object @description Merge merges `otherObject` into `object` and takes care of deep merging of objects @param {Object} object Object to merge into @param {Object} otherObject Object to read from */ this.merge = function (object, otherObject) { var obj = object || {} , otherObj = otherObject || {} , key, value; for (key in otherObj) { value = otherObj[key]; // Check if a value is an Object, if so recursively add it's key/values if (typeof value === 'object' && !(value instanceof Array)) { // Update value of object to the one from otherObj obj[key] = this.merge(obj[key], value); } // Value is anything other than an Object, so just add it else { obj[key] = value; } } return obj; }; /** @name object#reverseMerge @public @function @return {Object} Returns the merged object @description ReverseMerge merges `object` into `defaultObject` @param {Object} object Object to read from @param {Object} defaultObject Object to merge into */ this.reverseMerge = function (object, defaultObject) { // Same as `merge` except `defaultObject` is the object being changed // - this is useful if we want to easily deal with default object values return this.merge(defaultObject, object); }; /** @name object#isEmpty @public @function @return {Boolean} Returns true if empty false otherwise @description isEmpty checks if an Object is empty @param {Object} object Object to check if empty */ this.isEmpty = function (object) { // Returns true if a object is empty false if not for (var i in object) { return false; } return true; }; /** @name object#toArray @public @function @return {Array} Returns an array of objects each including the original key and value @description Converts an object to an array of objects each including the original key/value @param {Object} object Object to convert */ this.toArray = function (object) { // Converts an object into an array of objects with the original key, values array = []; for (var i in object) { array.push({ key: i, value: object[i] }); } return array; }; })(); module.exports = object; utilities-1.0.4/lib/request.js000066400000000000000000000111171240177617000163310ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var http = require('http') , https = require('https') , url = require('url') , uri = require('./uri') , log = require('./log') , core = require('./core'); /** @name request @namespace request @public @function @description Sends requests to the given url sending any data if the method is POST or PUT @param {Object} opts The options to use for the request @param {String} [opts.url] The URL to send the request to @param {String} [opts.method=GET] The method to use for the request @param {Object} [opts.headers] Headers to send on requests @param {String} [opts.data] Data to send on POST and PUT requests @param {String} [opts.dataType] The type of data to send @param {Function} callback the function to call after, args are `error, data` */ var request = function (opts, callback) { var client , options = opts || {} , parsed = url.parse(options.url) , path , requester = parsed.protocol == 'http:' ? http : https , method = (options.method && options.method.toUpperCase()) || 'GET' , headers = core.mixin({}, options.headers || {}) , data = options.data , contentLength , port , clientOpts; if (parsed.port) { port = parsed.port; } else { port = parsed.protocol == 'http:' ? '80' : '443'; } path = parsed.pathname; if (method == 'POST' || method == 'PUT') { // Handle the payload and content-length if (data) { // JSON data if (options.dataType == 'json') { if (typeof data == 'object') { data = JSON.stringify(data); } headers['Content-Type'] = headers['Content-Type'] || headers['content-type'] || 'application/json'; } // Form data else { if (typeof data == 'object') { data = uri.paramify(data); } // FIXME: What is the prefix for form-urlencoded? headers['Content-Type'] = headers['Content-Type'] || headers['content-type'] || 'form-urlencoded'; } contentLength = Buffer.byteLength(data); } else { contentLength = 0 } headers['Content-Length'] = contentLength; if (parsed.search) { path += parsed.search; } } else { if (data) { // Data is an object, parse into querystring if (typeof data == 'object') { data = uri.paramify(data); } // Create querystring or append to existing if (parsed.search) { path += parsed.search; // search includes question mark path += '&' + data; } else { path += '?' + data; } } } clientOpts = { host: parsed.hostname , port: port , method: method , agent: false , path: path , headers: headers }; client = requester.request(clientOpts); client.addListener('response', function (resp) { var data = ''; resp.addListener('data', function (chunk) { data += chunk.toString(); }); resp.addListener('end', function () { var stat = resp.statusCode , contentType , err; // Successful response if ((stat > 199 && stat < 300) || stat == 304) { if (data) { contentType = resp.headers['Content-Type']; if (contentType == 'application/json' || contentType == 'text/json' || uri.getFileExtension(parsed.pathname) == 'json') { try { data = JSON.parse(data); } catch (e) { return callback(e, null); } } callback(null, data); } else { callback(null, null); } } // Something failed else { err = new Error(data); err.statusCode = resp.statusCode; callback(err, null); } }); }); client.addListener('error', function (e) { callback(e, null); }); if ((method == 'POST' || method == 'PUT') && data) { client.write(data); } client.end(); }; module.exports = request; utilities-1.0.4/lib/sorted_collection.js000066400000000000000000000361201240177617000203550ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** @name SortedCollection @namespace SortedCollection @constructor */ var SortedCollection = function (d) { this.count = 0; this.items = {}; // Hash keys and their values this.order = []; // Array for sort order if (d) { this.defaultValue = d; }; }; SortedCollection.prototype = new (function () { /** @name SortedCollection#addItem @public @function @return {Any} The given val is returned @description Adds a new key/value to the collection @param {String} key The key for the collection item @param {Any} val The value for the collection item */ this.addItem = function (key, val) { if (typeof key != 'string') { throw('Hash only allows string keys.'); } return this.setByKey(key, val); }; /** @name SortedCollection#getItem @public @function @return {Any} The value for the given identifier is returned @description Retrieves the value for the given identifier that being a key or index @param {String/Number} p The identifier to look in the collection for, being a key or index */ this.getItem = function (p) { if (typeof p == 'string') { return this.getByKey(p); } else if (typeof p == 'number') { return this.getByIndex(p); } }; /** @name SortedCollection#setItem @public @function @return {Any} The given val is returned @description Sets the item in the collection with the given val, overwriting the existsing item if identifier is an index @param {String/Number} p The identifier set in the collection, being either a key or index @param {Any} val The value for the collection item */ this.setItem = function (p, val) { if (typeof p == 'string') { return this.setByKey(p, val); } else if (typeof p == 'number') { return this.setByIndex(p, val); } }; /** @name SortedCollection#removeItem @public @function @return {Boolean} Returns true if the item has been removed, false otherwise @description Removes the item for the given identifier @param {String/Number} p The identifier to delete the item for, being a key or index */ this.removeItem = function (p) { if (typeof p == 'string') { return this.removeByKey(p); } else if (typeof p == 'number') { return this.removeByIndex(p); } }; /** @name SortedCollection#getByKey @public @function @return {Any} The value for the given key item is returned @description Retrieves the value for the given key @param {String} key The key for the item to lookup */ this.getByKey = function (key) { return this.items[key]; }; /** @name SortedCollection#setByKey @public @function @return {Any} The given val is returned @description Sets a item by key assigning the given val @param {String} key The key for the item @param {Any} val The value to set for the item */ this.setByKey = function (key, val) { var v = null; if (typeof val == 'undefined') { v = this.defaultValue; } else { v = val; } if (typeof this.items[key] == 'undefined') { this.order[this.count] = key; this.count++; } this.items[key] = v; return this.items[key]; }; /** @name SortedCollection#removeByKey @public @function @return {Boolean} If the item was removed true is returned, false otherwise @description Removes a collection item by key @param {String} key The key for the item to remove */ this.removeByKey = function (key) { if (typeof this.items[key] != 'undefined') { var pos = null; delete this.items[key]; // Remove the value // Find the key in the order list for (var i = 0; i < this.order.length; i++) { if (this.order[i] == key) { pos = i; } } this.order.splice(pos, 1); // Remove the key this.count--; // Decrement the length return true; } else { return false; } }; /** @name SortedCollection#getByIndex @public @function @return {Any} The value for the given index item is returned @description Retrieves the value for the given index @param {Number} ind The index to lookup for the item */ this.getByIndex = function (ind) { return this.items[this.order[ind]]; }; /** @name SortedCollection#setByIndex @public @function @return {Any} The given val is returned @description Sets a item by index assigning the given val @param {Number} ind The index for the item @param {Any} val The value to set for the item */ this.setByIndex = function (ind, val) { if (ind < 0 || ind >= this.count) { throw('Index out of bounds. Hash length is ' + this.count); } this.items[this.order[ind]] = val; return this.items[this.order[ind]]; }; /** @name SortedCollection#removeByIndex @public @function @return {Boolean} If the item was removed true is returned, false otherwise @description Removes a collection item by index @param {Number} ind The index for the item to remove */ this.removeByIndex = function (ind) { var ret = this.items[this.order[ind]]; if (typeof ret != 'undefined') { delete this.items[this.order[ind]] this.order.splice(ind, 1); this.count--; return true; } else { return false; } }; /** @name SortedCollection#hasKey @public @function @return {Boolean} Returns true if the item exists, false otherwise @description Checks if a key item exists in the collection @param {String} key The key to look for in the collection */ this.hasKey = function (key) { return typeof this.items[key] != 'undefined'; }; /** @name SortedCollection#hasValue @public @function @return {Boolean} Returns true if a key with the given value exists, false otherwise @description Checks if a key item in the collection has a given val @param {Any} val The value to check for in the collection */ this.hasValue = function (val) { for (var i = 0; i < this.order.length; i++) { if (this.items[this.order[i]] == val) { return true; } } return false; }; /** @name SortedCollection#allKeys @public @function @return {String} Returns all the keys in a string @description Joins all the keys into a string @param {String} str The string to use between each key */ this.allKeys = function (str) { return this.order.join(str); }; /** @name SortedCollection#replaceKey @public @function @description Joins all the keys into a string @param {String} oldKey The key item to change @param {String} newKey The key item to change the name to */ this.replaceKey = function (oldKey, newKey) { // If item for newKey exists, nuke it if (this.hasKey(newKey)) { this.removeItem(newKey); } this.items[newKey] = this.items[oldKey]; delete this.items[oldKey]; for (var i = 0; i < this.order.length; i++) { if (this.order[i] == oldKey) { this.order[i] = newKey; } } }; /** @name SortedCollection#insertAtIndex @public @function @return {Boolean} Returns true if the item was set at the given index @description Inserts a key/value at a specific index in the collection @param {Number} ind The index to set the item at @param {String} key The key to use at the item index @param {Any} val The value to set for the item */ this.insertAtIndex = function (ind, key, val) { this.order.splice(ind, 0, key); this.items[key] = val; this.count++; return true; }; /** @name SortedCollection#insertAfterKey @public @function @return {Boolean} Returns true if the item was set for the given key @description Inserts a key/value item after the given reference key in the collection @param {String} refKey The key to insert the new item after @param {String} key The key for the new item @param {Any} val The value to set for the item */ this.insertAfterKey = function (refKey, key, val) { var pos = this.getPosition(refKey); return this.insertAtIndex(pos, key, val); }; /** @name SortedCollection#getPosition @public @function @return {Number} Returns the index for the item of the given key @description Retrieves the index of the key item @param {String} key The key to get the index for */ this.getPosition = function (key) { var order = this.order; if (typeof order.indexOf == 'function') { return order.indexOf(key); } else { for (var i = 0; i < order.length; i++) { if (order[i] == key) { return i;} } } }; /** @name SortedCollection#each @public @function @return {Boolean} @description Loops through the collection and calls the given function @param {Function} func The function to call for each collection item, the arguments are the key and value for the current item @param {Object} opts The options to use @param {Boolean} [opts.keyOnly] Only give the function the key @param {Boolean} [opts.valueOnly] Only give the function the value */ this.each = function (func, opts) { var options = opts || {} , order = this.order; for (var i = 0, ii = order.length; i < ii; i++) { var key = order[i]; var val = this.items[key]; if (options.keyOnly) { func(key); } else if (options.valueOnly) { func(val); } else { func(val, key); } } return true; }; /** @name SortedCollection#eachKey @public @function @return {Boolean} @description Loops through the collection and calls the given function @param {Function} func The function to call for each collection item, only giving the key to the function */ this.eachKey = function (func) { return this.each(func, { keyOnly: true }); }; /** @name SortedCollection#eachValue @public @function @return {Boolean} @description Loops through the collection and calls the given function @param {Function} func The function to call for each collection item, only giving the value to the function */ this.eachValue = function (func) { return this.each(func, { valueOnly: true }); }; /** @name SortedCollection#clone @public @function @return {Object} Returns a new SortedCollection with the data of the current one @description Creates a cloned version of the current collection and returns it */ this.clone = function () { var coll = new SortedCollection() , key , val; for (var i = 0; i < this.order.length; i++) { key = this.order[i]; val = this.items[key]; coll.setItem(key, val); } return coll; }; /** @name SortedCollection#concat @public @function @description Join a given collection with the current one @param {Object} hNew A SortedCollection to join from */ this.concat = function (hNew) { for (var i = 0; i < hNew.order.length; i++) { var key = hNew.order[i]; var val = hNew.items[key]; this.setItem(key, val); } }; /** @name SortedCollection#push @public @function @return {Number} Returns the count of items @description Appends a new item to the collection @param {String} key The key to use for the item @param {Any} val The value to use for the item */ this.push = function (key, val) { this.insertAtIndex(this.count, key, val); return this.count; }; /** @name SortedCollection#pop @public @function @return {Any} Returns the value for the last item in the collection @description Pops off the last item in the collection and returns it's value */ this.pop = function () { var pos = this.count-1; var ret = this.items[this.order[pos]]; if (typeof ret != 'undefined') { this.removeByIndex(pos); return ret; } else { return; } }; /** @name SortedCollection#unshift @public @function @return {Number} Returns the count of items @description Prepends a new item to the beginning of the collection @param {String} key The key to use for the item @param {Any} val The value to use for the item */ this.unshift = function (key, val) { this.insertAtIndex(0, key, val); return this.count; }; /** @name SortedCollection#shift @public @function @return {Number} Returns the removed items value @description Removes the first item in the list and returns it's value */ this.shift = function () { var pos = 0; var ret = this.items[this.order[pos]]; if (typeof ret != 'undefined') { this.removeByIndex(pos); return ret; } else { return; } }; /** @name SortedCollection#splice @public @function @description Removes items from index to the given max and then adds the given collections items @param {Number} index The index to start at when removing items @param {Number} numToRemove The number of items to remove before adding the new items @param {Object} hash the collection of items to add */ this.splice = function (index, numToRemove, hash) { var _this = this; // Removal if (numToRemove > 0) { // Items var limit = index + numToRemove; for (var i = index; i < limit; i++) { delete this.items[this.order[i]]; } // Order this.order.splice(index, numToRemove); } // Adding if (hash) { // Items for (var i in hash.items) { this.items[i] = hash.items[i]; } // Order var args = hash.order; args.unshift(0); args.unshift(index); this.order.splice.apply(this.order, args); } this.count = this.order.length; }; this.sort = function (c) { var arr = []; // Assumes vals are comparable scalars var comp = function (a, b) { return c(a.val, b.val); } for (var i = 0; i < this.order.length; i++) { var key = this.order[i]; arr[i] = { key: key, val: this.items[key] }; } arr.sort(comp); this.order = []; for (var i = 0; i < arr.length; i++) { this.order.push(arr[i].key); } }; this.sortByKey = function (comp) { this.order.sort(comp); }; /** @name SortedCollection#reverse @public @function @description Reverse the collection item list */ this.reverse = function () { this.order.reverse(); }; })(); module.exports.SortedCollection = SortedCollection; utilities-1.0.4/lib/string.js000066400000000000000000000574451240177617000161650ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var core = require('./core') , inflection = require('./inflection') , string; /** @name string @namespace string */ string = new (function () { // Regexes for trimming, and character maps for escaping var _LTR = /^\s+/ , _RTR = /\s+$/ , _TR = /^\s+|\s+$/g , _NL = /\n|\r|\r\n/g , _CHARS = { '&': '&' , '<': '<' , '>': '>' , '"': '"' , '\'': ''' } , _UUID_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('') , _buildEscape , _buildEscapeTest; // Builds the escape/unescape methods using a // map of characters _buildEscape = function (direction) { return function (str) { var string = str; // If string is NaN, null or undefined then provide an empty default if((typeof string === 'undefined') || string === null || (!string && isNaN(string))) { string = ''; } string = string.toString(); var from, to, p; for (p in _CHARS) { from = direction == 'to' ? p : _CHARS[p]; to = direction == 'to' ? _CHARS[p] : p; string = string.replace(new RegExp(from, 'gm'), to); } return string; } }; // Builds a method that tests for any escapable // characters, useful for avoiding double-scaping if // you're not sure if a string has already been escaped _buildEscapeTest = function (direction) { return function (string) { var pat = '' , p; for (p in _CHARS) { pat += direction == 'to' ? p : _CHARS[p]; pat += '|'; } pat = pat.substr(0, pat.length - 1); pat = new RegExp(pat, "gm"); return pat.test(string) } }; // Escape special XMl chars this.escapeXML = _buildEscape('to'); // Unescape XML chars to literal representation this.unescapeXML = _buildEscape('from'); // Test if a string includes special chars // that need escaping this.needsEscape = _buildEscapeTest('to'); // Test if a string includes escaped chars // that need unescaping this.needsUnescape = _buildEscapeTest('from'); /** @name string#escapeRegExpChars @public @function @return {String} A string of escaped characters @description Escapes regex control-characters in strings used to build regexes dynamically @param {String} string The string of chars to escape */ this.escapeRegExpChars = (function () { var specials = [ '^', '$', '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ]; var sRE = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); return function (string) { var str = string || ''; str = String(str); return str.replace(sRE, '\\$1'); }; }).call(this); /** @name string#toArray @public @function @return {Array} Returns an array of characters @description Converts a string to an array @param {String} string The string to convert */ this.toArray = function (string) { var str = string || '' , arr = [] , i = -1; str = String(str); while (++i < str.length) { arr.push(str.substr(i, 1)); } return arr; }; /** @name string#reverse @public @function @return {String} Returns the `string` reversed @description Reverses a string @param {String} string The string to reverse */ this.reverse = function (string) { var str = string || ''; str = String(str); return this.toArray(str).reverse().join(''); }; /** @name string#ltrim @public @function @return {String} Returns the trimmed string @description Ltrim trims `char` from the left of a `string` and returns it if no `char` is given it will trim spaces @param {String} string The string to trim @param {String} character The character to trim */ this.ltrim = function (string, character) { var str = string || '' , pat = character ? new RegExp('^' + character + '+') : _LTR; str = String(str); return str.replace(pat, ''); }; /** @name string#rtrim @public @function @return {String} Returns the trimmed string @description Rtrim trims `char` from the right of a `string` and returns it if no `char` is given it will trim spaces @param {String} string The string to trim @param {String} character The character to trim */ this.rtrim = function (string, character) { var str = string || '' , pat = character ? new RegExp(character + '+$') : _RTR; str = String(str); return str.replace(pat, ''); }; // Alias this.chomp = this.rtrim; /** @name string#trim @public @function @return {String} Returns the trimmed string @description Trim trims `char` from the left and right of a `string` and returns it if no `char` is given it will trim spaces @param {String} string The string to trim @param {String} character The character to trim */ this.trim = function (string, character) { var str = string || '' , pat = character ? new RegExp('^' + character + '+|' + character + '+$', 'g') : _TR; str = String(str); return str.replace(pat, ''); }; /** @name string#chop @public @function @description Returns a new String with the last character removed. If the string ends with \r\n, both characters are removed. Applying chop to an empty string returns an empty string. @param {String} string to return with the last character removed. */ this.chop = function (string) { var index , str = string || ''; str = String(str); if (str.length) { // Special-case for \r\n index = str.indexOf('\r\n'); if (index == str.length - 2) { return str.substring(0, index); } return str.substring(0, str.length - 1); } else { return ''; } }; /** @name string#lpad @public @function @return {String} Returns the padded string @description Lpad adds `char` to the left of `string` until the length of `string` is more than `width` @param {String} string The string to pad @param {String} character The character to pad with @param {Number} width the width to pad to */ this.lpad = function (string, character, width) { var str = string || '' , width; str = String(str); // Should width be string.length + 1? or the same to be safe width = parseInt(width, 10) || str.length; character = character || ' '; while (str.length < width) { str = character + str; } return str; }; /** @name string#rpad @public @function @return {String} Returns the padded string @description Rpad adds `char` to the right of `string` until the length of `string` is more than `width` @param {String} string The string to pad @param {String} character The character to pad with @param {Number} width the width to pad to */ this.rpad = function (string, character, width) { var str = string || '' , width; str = String(str); // Should width be string.length + 1? or the same to be safe width = parseInt(width, 10) || str.length; character = character || ' '; while (str.length < width) { str += character; } return str; }; /** @name string#pad @public @function @return {String} Returns the padded string @description Pad adds `char` to the left and right of `string` until the length of `string` is more than `width` @param {String} string The string to pad @param {String} character The character to pad with @param {Number} width the width to pad to */ this.pad = function (string, character, width) { var str = string || '' , width; str = String(str); // Should width be string.length + 1? or the same to be safe width = parseInt(width, 10) || str.length; character = character || ' '; while (str.length < width) { str = character + str + character; } return str; }; /** @name string#truncate @public @function @return {String} Returns the truncated string @description Truncates a given `string` after a specified `length` if `string` is longer than `length`. The last characters will be replaced with an `omission` for a total length not exceeding `length`. If `callback` is given it will fire if `string` is truncated. @param {String} string The string to truncate @param {Integer/Object} options Options for truncation, If options is an Integer it will be length @param {Integer} [options.length=string.length] Length the output string will be @param {Integer} [options.len] Alias for `length` @param {String} [options.omission='...'] Replace last characters with an omission @param {String} [options.ellipsis='...'] Alias for `omission` @param {String/RegExp} [options.seperator] Break the truncated test at the nearest `seperator` @param {Function} callback Callback is called only if a truncation is done */ this.truncate = function (string, options, callback) { var str = string || '' , stringLen , opts , stringLenWithOmission , last , ignoreCase , multiLine , stringToWorkWith , lastIndexOf , nextStop , result , returnString; str = String(str); stringLen = str.length // If `options` is a number, assume it's the length and // create a options object with length if (typeof options === 'number') { opts = { length: options }; } else { opts = options || {}; } // Set `opts` defaults opts.length = opts.length || stringLen; opts.omission = opts.omission || opts.ellipsis || '...'; stringLenWithOmission = opts.length - opts.omission.length; // Set the index to stop at for `string` if (opts.seperator) { if (opts.seperator instanceof RegExp) { // If `seperator` is a regex if (opts.seperator.global) { opts.seperator = opts.seperator; } else { ignoreCase = opts.seperator.ignoreCase ? 'i' : '' multiLine = opts.seperator.multiLine ? 'm' : ''; opts.seperator = new RegExp(opts.seperator.source, 'g' + ignoreCase + multiLine); } stringToWorkWith = str.substring(0, stringLenWithOmission + 1) lastIndexOf = -1 nextStop = 0 while ((result = opts.seperator.exec(stringToWorkWith))) { lastIndexOf = result.index; opts.seperator.lastIndex = ++nextStop; } last = lastIndexOf; } else { // Seperator is a String last = str.lastIndexOf(opts.seperator, stringLenWithOmission); } // If the above couldn't be found, they'll default to -1 so, // we need to just set it as `stringLenWithOmission` if (last === -1) { last = stringLenWithOmission; } } else { last = stringLenWithOmission; } if (stringLen < opts.length) { return str; } else { returnString = str.substring(0, last) + opts.omission; returnString += callback && typeof callback === 'function' ? callback() : ''; return returnString; } }; /** @name string#truncateHTML @public @function @return {String} Returns the HTML safe truncated string @description Truncates a given `string` inside HTML tags after a specified `length` if string` is longer than `length`. The last characters will be replaced with an `omission` for a total length not exceeding `length`. If `callback` is given it will fire if `string` is truncated. If `once` is true only the first string in the first HTML tags will be truncated leaving the others as they were @param {String} string The string to truncate @param {Integer/Object} options Options for truncation, If options is an Integer it will be length all Object options are the same as `truncate` @param {Boolean} [options.once=false] If true, it will only be truncated once, otherwise the truncation will loop through all text inside HTML tags @param {Function} callback Callback is called only if a truncation is done */ this.truncateHTML = function (string, options, callback) { var str = string || '' , returnString = '' , opts = options; str = String(str); // If `options` is a number assume it's the length and create a options object with length if (typeof opts === 'number') { var num = opts; opts = {}; opts.length = num; } else opts = opts || {}; // Set `default` options for HTML specifics opts.once = opts.once || false; var pat = /(<[^>]*>)/ // Patter for matching HTML tags , arr = [] // Holds the HTML tags and content seperately , truncated = false , result = pat.exec(str) , item , firstPos , lastPos , i; // Gather the HTML tags and content into the array while (result) { firstPos = result.index; lastPos = pat.lastIndex; if (firstPos !== 0) { // Should be content not HTML tags arr.push(str.substring(0, firstPos)); // Slice content from string str = str.slice(firstPos); } arr.push(result[0]); // Push HTML tags str = str.slice(result[0].length); // Re-run the pattern on the new string result = pat.exec(str); } if (str !== '') { arr.push(str); } // Loop through array items appending the tags to the string, // - and truncating the text then appending it to content i = -1; while (++i < arr.length) { item = arr[i]; switch (true) { // Closing tag case item.indexOf(''); }; /** @name string#snakeize @public @function @return {String} The string in a snake_case version @description Snakeize converts camelCase and CamelCase strings to snake_case strings @param {String} string The string to convert to snake_case @param {String} separ='_' The seperator to use */ this.snakeize = (function () { // Only create regexes once on initial load var repl = /([A-Z]+)/g , lead = /^_/g; return function (string, separ) { var str = string || '' , sep = separ || '_' , leading = separ ? new RegExp('^' + sep, 'g') : lead; str = String(str); return str.replace(repl, sep + '$1').toLowerCase(). replace(leading, ''); }; }).call(this); // Aliases /** @name string#underscorize @public @function @return {String} The string in a underscorized version @description Underscorize returns the given `string` converting camelCase and snakeCase to underscores @param {String} string The string to underscorize */ this.underscorize = this.snakeize; this.underscoreize = this.snakeize; this.decamelize = this.snakeize; /** @name string#camelize @public @function @return {String} The string in a camelCase version @description Camelize takes a string and optional options and returns a camelCase version of the given `string` @param {String} string The string to convert to camelCase @param {Object} options @param {Boolean} [options.initialCap] If initialCap is true the returned string will have a capitalized first letter @param {Boolean} [options.leadingUnderscore] If leadingUnderscore os true then if an underscore exists at the beggining of the string, it will stay there. Otherwise it'll be removed. */ this.camelize = (function () { // Only create regex once on initial load var repl = /[-_](\w)/g; return function (string, options) { var str = string || '' , ret , config = { initialCap: false , leadingUnderscore: false } , opts = options || {}; str = String(str); // Backward-compat if (typeof opts == 'boolean') { config = { initialCap: true }; } else { core.mixin(config, opts); } ret = str.replace(repl, function (m, m1) { return m1.toUpperCase(); }); if (config.leadingUnderscore & str.indexOf('_') === 0) { ret = '_' + this.decapitalize(ret); } // If initialCap is true capitalize it ret = config.initialCap ? this.capitalize(ret) : this.decapitalize(ret); return ret; }; }).call(this); /** @name string#decapitalize @public @function @return {String} The string with the first letter decapitalized @description Decapitalize returns the given string with the first letter uncapitalized. @param {String} string The string to decapitalize */ this.decapitalize = function (string) { var str = string || ''; str = String(str); return str.substr(0, 1).toLowerCase() + str.substr(1); }; /** @name string#capitalize @public @function @return {String} The string with the first letter capitalized @description capitalize returns the given string with the first letter capitalized. @param {String} string The string to capitalize */ this.capitalize = function (string) { var str = string || ''; str = String(str); return str.substr(0, 1).toUpperCase() + str.substr(1); }; /** @name string#dasherize @public @function @return {String} The string in a dashed version @description Dasherize returns the given `string` converting camelCase and snakeCase to dashes or replace them with the `replace` character. @param {String} string The string to dasherize @param {String} replace='-' The character to replace with */ this.dasherize = function (string, replace) { var repl = replace || '-' return this.snakeize(string, repl); }; /** @name string#include @public @function @return {Boolean} Returns true if the string is found in the string to search @description Searches for a particular string in another string @param {String} searchIn The string to search for the other string in @param {String} searchFor The string to search for */ this.include = function (searchIn, searchFor) { var str = searchFor; if (!str && typeof string != 'string') { return false; } str = String(str); return (searchIn.indexOf(str) > -1); }; /* * getInflections(name, initialCap) * * Inflection returns an object that contains different inflections * created from the given `name` */ /** @name string#getInflections @public @function @return {Object} A Object containing multiple different inflects for the given `name` @description Inflection returns an object that contains different inflections created from the given `name` @param {String} name The string to create inflections from */ this.getInflections = function (name) { if (!name) { return; } var self = this // Use plural version to fix possible mistakes(e,g,. thingie instead of thingy) , normalizedName = this.snakeize(inflection.pluralize(name)) , nameSingular = inflection.singularize(normalizedName) , namePlural = inflection.pluralize(normalizedName); return { // For filepaths or URLs filename: { // neil_peart singular: nameSingular // neil_pearts , plural: namePlural } // Constructor names , constructor: { // NeilPeart singular: self.camelize(nameSingular, {initialCap: true}) // NeilPearts , plural: self.camelize(namePlural, {initialCap: true}) } , property: { // neilPeart singular: self.camelize(nameSingular) // neilPearts , plural: self.camelize(namePlural) } }; }; /** @name string#getInflection @public @function @return {Object} A Object containing multiple different inflects for the given `name` @description Inflection returns an object that contains different inflections created from the given `name` @param {String} name The string to create inflections from */ this.getInflection = function (name, key, pluralization) { var infl = this.getInflections(name); return infl[key][pluralization]; }; // From Math.uuid.js, https://github.com/broofa/node-uuid // Robert Kieffer (robert@broofa.com), MIT license this.uuid = function (length, radix) { var chars = _UUID_CHARS , uuid = [] , r , i; radix = radix || chars.length; if (length) { // Compact form i = -1; while (++i < length) { uuid[i] = chars[0 | Math.random()*radix]; } } else { // rfc4122, version 4 form // rfc4122 requires these characters uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 i = -1; while (++i < 36) { if (!uuid[i]) { r = 0 | Math.random()*16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } } return uuid.join(''); }; /** @name string#stripTags @public @function @return {String} A String with HTML tags removed. @description Strips HTML tags from a string. @param {String} The string to strip HTML tags from @param {String|Array} A String or Array containing allowed tags. e.g. "

" */ this.stripTags = function(string, allowed) { // taken from http://phpjs.org/functions/strip_tags/ var allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase () var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, comments = //gi; return string.replace(comments, '').replace(tags, function ($0, $1) { return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; }); } })(); module.exports = string; utilities-1.0.4/lib/uri.js000066400000000000000000000200171240177617000154370ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var uri , string = require('./string') , mixin = require('./core').mixin; /** @name uri @namespace uri */ uri = new (function () { var _isArray = function (obj) { return obj && typeof obj === 'object' && typeof obj.length === 'number' && typeof obj.splice === 'function' && !(obj.propertyIsEnumerable('length')); }; /** @name uri#getFileExtension @public @function @return {String} Returns the file extension for a given path @description Gets the file extension for a path and returns it @param {String} path The path to get the extension for */ this.getFileExtension = function (path) { var match; if (path) { match = /.+\.(\w{2,4}$)/.exec(path); } return (match && match[1]) || ''; }; /** @name uri#paramify @public @function @return {String} Returns a querystring contains the given values @description Convert a JS Object to a querystring (key=val&key=val). Values in arrays will be added as multiple parameters @param {Object} obj An Object containing only scalars and arrays @param {Object} o The options to use for formatting @param {Boolean} [o.consolidate=false] take values from elements that can return multiple values (multi-select, checkbox groups) and collapse into a single, comman-delimited value. @param {Boolean} [o.includeEmpty=false] include keys in the string for all elements, even they have no value set (e.g., even if elemB has no value: elemA=foo&elemB=&elemC=bar). Note that some false-y values are always valid even without this option, [0, '']. This option extends coverage to [null, undefined, NaN] @param {Boolean} [o.snakeize=false] change param names from camelCase to snake_case. @param {Boolean} [o.escapeVals=false] escape the values for XML entities. @param {Boolean} [o.index=false] use numeric indices for arrays */ this.paramify = function (obj, o) { var opts = o || {}, _opts, str = '', key, val, isValid, itemArray, arr = [], arrVal, prefix = opts.prefix || '', self = this; function getParamName(key) { if (opts.prefix) { return prefix + '[' + key + ']'; } else { return key; } } for (var p in obj) { if (Object.prototype.hasOwnProperty.call(obj, p)) { val = obj[p]; // This keeps valid falsy values like false and 0 // It's duplicated in the array block below. Could // put it in a function but don't want the overhead isValid = !( val === null || val === undefined || (typeof val === 'number' && isNaN(val)) ); key = opts.snakeize ? string.snakeize(p) : p; if (isValid) { // Multiple vals -- array if (_isArray(val) && val.length) { itemArray = []; for (var i = 0, ii = val.length; i < ii; i++) { arrVal = val[i]; // This keeps valid falsy values like false and 0 isValid = !( arrVal === null || arrVal === undefined || (typeof arrVal === 'number' && isNaN(arrVal)) ); // for index mode, which works recursive // objects and array must not be encoded if (opts.index && typeof arrVal === 'object') { itemArray[i] = arrVal; } else { itemArray[i] = isValid ? encodeURIComponent(arrVal) : ''; if (opts.escapeVals) { itemArray[i] = string.escapeXML(itemArray[i]); } } } // Consolidation mode -- single value joined on comma if (opts.consolidate) { arr.push(getParamName(key) + '=' + itemArray.join(',')); } // Indexed mode -- multiple, same-named params with numeric indices else if (opts.index) { // {foo: [1, 2, 3]} => 'foo[0]=1&foo[1]=2&foo[2]=3' itemArray.forEach(function(item, i) { // recursion of arrays if (_isArray(item) && item.length) { _opts = mixin(opts, {}); item.forEach(function(_item, ii) { if (typeof _item === 'object') { _opts.prefix = getParamName(key) + '[' + i + '][' + ii + ']'; arr.push(self.paramify(_item, _opts)); } else { arr.push(getParamName(key) + '[' + i + '][' + ii + ']=' + _item); } }); } // recursion of object in array else if (typeof item === 'object') { _opts = mixin(opts, {}); _opts.prefix = getParamName(key) + '[' + i + ']'; arr.push(self.paramify(item, _opts)); } // primitive else { arr.push(getParamName(key) + '[' + i + ']=' + item); } }); } // Normal mode -- multiple, same-named params with each val else { // {foo: [1, 2, 3]} => 'foo=1&foo=2&foo=3' // Add into results array, as this just ends up getting // joined on ampersand at the end anyhow arr.push(getParamName(key) + '=' + itemArray.join('&' + getParamName(key) + '=')); } } // Object -- recursion else if (typeof val === 'object') { _opts = mixin(opts, {}); _opts.prefix = getParamName(key); arr.push(this.paramify(val, _opts)); } // Single val -- string else { if (opts.escapeVals) { val = string.escapeXML(val); } arr.push(getParamName(key) + '=' + encodeURIComponent(val)); } str += '&'; } else { if (opts.includeEmpty) { arr.push(getParamName(key) + '='); } } } } return arr.join('&'); }; /** @name uri#objectify @public @function @return {Object} JavaScript key/val object with the values from the querystring @description Convert the values in a query string (key=val&key=val) to an Object @param {String} str The querystring to convert to an object @param {Object} o The options to use for formatting @param {Boolean} [o.consolidate=true] Convert multiple instances of the same key into an array of values instead of overwriting */ this.objectify = function (str, o) { var opts = o || {}; var d = {}; var consolidate = typeof opts.consolidate == 'undefined' ? true : opts.consolidate; if (str) { var arr = str.split('&'); for (var i = 0; i < arr.length; i++) { var pair = arr[i].split('='); var name = pair[0]; var val = decodeURIComponent(pair[1] || ''); // "We've already got one!" -- arrayize if the flag // is set if (typeof d[name] != 'undefined' && consolidate) { if (typeof d[name] == 'string') { d[name] = [d[name]]; } d[name].push(val); } // Otherwise just set the value else { d[name] = val; } } } return d; }; })(); module.exports = uri; utilities-1.0.4/lib/xml.js000066400000000000000000000175531240177617000154530ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var core = require('./core') , inflection = require('./inflection') /** @name xml @namespace xml */ exports.XML = new (function () { // Default indention level var indentLevel = 4 , tagFromType , obj2xml; tagFromType = function (item, prev) { var ret , type , types; if (item instanceof Array) { ret = 'array'; } else { types = ['string', 'number', 'boolean', 'object']; for (var i = 0, ii = types.length; i < ii; i++) { type = types[i]; if (typeof item == type) { ret = type; } } } if (prev && ret != prev) { return 'record' } else { return ret; } }; obj2xml = function (o, opts) { var name = opts.name , level = opts.level , arrayRoot = opts.arrayRoot , pack , item , n , currentIndent = (new Array(level * indentLevel)).join(' ') , nextIndent = (new Array((level + 1) * indentLevel)).join(' ') , xml = ''; switch (typeof o) { case 'string': case 'number': case 'boolean': xml = o.toString(); break; case 'object': // Arrays if (o instanceof Array) { // Pack the processed version of each item into an array that // can be turned into a tag-list with a `join` method below // As the list gets iterated, if all items are the same type, // that's the tag-name for the individual tags. If the items are // a mixed, the tag-name is 'record' pack = []; for (var i = 0, ii = o.length; i < ii; i++) { item = o[i]; if (!name) { // Pass any previous tag-name, so it's possible to know if // all items are the same type, or it's mixed types n = tagFromType(item, n); } pack.push(obj2xml(item, { name: name , level: level + 1 , arrayRoot: arrayRoot })); } // If this thing is attached to a named property on an object, // use the name for the containing tag-name if (name) { n = name; } // If this is a top-level item, wrap in a top-level containing tag if (level == 0) { xml += currentIndent + '<' + inflection.pluralize(n) + ' type="array">\n' } xml += nextIndent + '<' + n + '>' + pack.join('\n' + nextIndent + '<' + n + '>') + '\n'; // If this is a top-level item, close the top-level containing tag if (level == 0) { xml += currentIndent + ''; } } // Generic objects else { n = name || 'object'; // If this is a top-level item, wrap in a top-level containing tag if (level == 0) { xml += currentIndent + '<' + n; // Lookahead hack to allow tags to have attributes for (var p in o) { if (p.indexOf('attr:') == 0) { xml += ' ' + p.replace(/^attr:/, '') + '="' + o[p] + '"' } } xml += '>\n'; } for (var p in o) { item = o[p]; // Data properties only if (typeof item == 'function') { continue; } // No attr hack properties if (p.indexOf('attr:') == 0) { continue; } xml += nextIndent; if (p == '#cdata') { xml += '\n'; } else { // Complex values, going to have items with multiple tags // inside if (typeof item == 'object') { if (item instanceof Array) { if (arrayRoot) { xml += '<' + p + ' type="array">\n' } } else { xml += '<' + p; // Lookahead hack to allow tags to have attributes for (var q in item) { if (q.indexOf('attr:') == 0) { xml += ' ' + q.replace(/^attr:/, '') + '="' + item[q] + '"' } } xml += '>\n'; } } // Scalars, just a value and closing tag else { xml += '<' + p + '>' } xml += obj2xml(item, { name: p , level: level + 1 , arrayRoot: arrayRoot }); // Objects and Arrays, need indentation before closing tag if (typeof item == 'object') { if (item instanceof Array) { if (arrayRoot) { xml += nextIndent; xml += '\n'; } } else { xml += nextIndent; xml += '\n'; } } // Scalars, just close else { xml += '\n'; } } } // If this is a top-level item, close the top-level containing tag if (level == 0) { xml += currentIndent + '\n'; } } break; default: // No default } return xml; } /* * XML configuration * */ this.config = { whitespace: true , name: null , fragment: false , level: 0 , arrayRoot: true }; /** @name xml#setIndentLevel @public @function @return {Number} Return the given `level` @description SetIndentLevel changes the indent level for XML.stringify and returns it @param {Number} level The indent level to use */ this.setIndentLevel = function (level) { if(!level) { return; } return indentLevel = level; }; /** @name xml#stringify @public @function @return {String} Return the XML entities of the given `obj` @description Stringify returns an XML representation of the given `obj` @param {Object} obj The object containing the XML entities to use @param {Object} opts @param {Boolean} [opts.whitespace=true] Don't insert indents and newlines after xml entities @param {String} [opts.name=typeof obj] Use custom name as global namespace @param {Boolean} [opts.fragment=false] If true no header fragment is added to the top @param {Number} [opts.level=0] Remove this many levels from the output @param {Boolean} [opts.arrayRoot=true] */ this.stringify = function (obj, opts) { var config = core.mixin({}, this.config) , xml = ''; core.mixin(config, (opts || {})); if (!config.whitespace) { indentLevel = 0; } if (!config.fragment) { xml += '\n'; } xml += obj2xml(obj, { name: config.name , level: config.level , arrayRoot: config.arrayRoot }); if (!config.whitespace) { xml = xml.replace(/>\n/g, '>'); } return xml; }; })(); utilities-1.0.4/package.json000066400000000000000000000007711240177617000160270ustar00rootroot00000000000000{ "name": "utilities", "description": "A classic collection of JavaScript utilities", "keywords": [ "utilities", "utils", "jake", "geddy" ], "version": "1.0.4", "author": "Matthew Eernisse (http://fleegix.org)", "main": "./lib/index.js", "scripts": { "test": "jake test" }, "repository": { "type": "git", "url": "git://github.com/mde/utilities.git" }, "devDependencies": { "jake": "latest" }, "engines": { "node": "*" } }utilities-1.0.4/test/000077500000000000000000000000001240177617000145135ustar00rootroot00000000000000utilities-1.0.4/test/array.js000066400000000000000000000037241240177617000161750ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var assert = require('assert') , array = require('../lib/array') , tests; tests = { 'test basic humanize for array': function () { var actual = array.humanize(["array", "array", "array"]) , expected = "array, array and array"; assert.equal(expected, actual); } , 'test humanize with two items for array': function () { var actual = array.humanize(["array", "array"]) , expected = "array and array"; assert.equal(expected, actual); } , 'test humanize with a single item for array': function () { var actual = array.humanize(["array"]) , expected = "array"; assert.equal(expected, actual); } , 'test humanize with no items for array': function () { var actual = array.humanize([]) , expected = ""; assert.equal(expected, actual); } , 'test basic include for array': function () { var test = ["array"] , actual = array.include(test, "array"); assert.equal(true, actual); } , 'test false include for array': function () { var test = ["array"] , actual = array.include(test, 'nope'); assert.equal(false, actual); } , 'test false boolean include for array': function () { var test = ["array", false] , actual = array.include(test, false); assert.equal(true, actual); } }; module.exports = tests; utilities-1.0.4/test/core.js000066400000000000000000000051611240177617000160040ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var assert = require('assert') , core = require('../lib/core') , tests; tests = { 'simple mixin for core': function () { var expected = {secret: 'asdf', geddy: 'geddyKey'} , result = core.mixin({secret: 'asdf'}, {geddy: 'geddyKey'}); assert.deepEqual(expected, result); } , 'mixin with overiding key for core': function () { var expected = {secret: 'geddySecret', geddy: 'geddyKey'} , result = core.mixin({secret: 'asdf'}, {geddy: 'geddyKey', secret: 'geddySecret'}); assert.deepEqual(expected, result); } , 'simple enhance for core': function () { var expected = {secret: 'asdf', geddy: 'geddyKey'} , result = core.enhance({secret: 'asdf'}, {geddy: 'geddyKey'}); assert.deepEqual(expected, result); } , 'enhance with overiding key for core': function () { var expected = {secret: 'geddySecret', geddy: 'geddyKey'} , result = core.enhance({secret: 'asdf'}, {geddy: 'geddyKey', secret: 'geddySecret'}); assert.deepEqual(expected, result); } , 'isEmpty, empty string (true)': function () { assert.ok(core.isEmpty('')); } , 'isEmpty, null (true)': function () { assert.ok(core.isEmpty(null)); } , 'isEmpty, undefined (true)': function () { assert.ok(core.isEmpty(null)); } , 'isEmpty, NaN (true)': function () { assert.ok(core.isEmpty(NaN)); } , 'isEmpty, invalid Date (true)': function () { assert.ok(core.isEmpty(new Date(NaN))); } , 'isEmpty, zero (false)': function () { assert.ok(!core.isEmpty(0)); } , 'bind': function () { function bar() {} function foo() { assert.equal(this.name, 'bar'); } var fooBoundToBar = core.bind(bar, foo); fooBoundToBar(); } , 'bind, arguments': function () { function bar() {} function foo(arg) { assert.equal(this.name, 'bar'); assert.equal(arg, 'cats'); } var fooBoundToBarWithCats = core.bind(bar, foo, 'cats'); fooBoundToBarWithCats(); } }; module.exports = tests; utilities-1.0.4/test/date.js000066400000000000000000000046421240177617000157740ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var date = require('../lib/date') , assert = require('assert') , tests = {} , _date = new Date(); tests = { 'test strftime for date': function () { var data = date.strftime(_date, "%w") , actual = _date.getDay(); assert.equal(actual, data); } , 'test calcCentury using current year for date': function () { var data = date.calcCentury() , actual = '21'; assert.equal(actual, data); } , 'test calcCentury using 20th century year for date': function () { var data = date.calcCentury(2000) , actual = '20'; assert.equal(actual, data); } , 'test calcCentury using 1st century year for date': function () { var data = date.calcCentury(10) , actual = '1'; assert.equal(actual, data); } , 'test getMeridiem for date': function () { var data = date.getMeridiem(_date.getHours()) , actual = (_date.getHours() > 11) ? 'PM' : 'AM'; assert.equal(actual, data); } , 'test parse UTC': function () { var dt = date.parse('1970-01-01T00:00:00Z'); assert.equal(0, dt.getTime()); } , 'test parse with offset basic': function () { var dt; dt = date.parse('1970-01-01T00:00:00-0100'); assert.equal(3600000, dt.getTime()); dt = date.parse('1970-01-01T00:00:00+0100'); assert.equal(-3600000, dt.getTime()); } , 'test parse with offset extended': function () { var dt; dt = date.parse('1970-01-01T00:00:00-01:00'); assert.equal(3600000, dt.getTime()); dt = date.parse('1970-01-01T00:00:00+01:00'); assert.equal(-3600000, dt.getTime()); } , 'test parse floating (i.e., local offset)': function () { var dt; dt = date.parse('1970-01-01T00:00:00'); assert.equal(dt.getTime() / 1000 / 60, dt.getTimezoneOffset()); } }; module.exports = tests; utilities-1.0.4/test/event_buffer.js000066400000000000000000000026071240177617000175300ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var Stream = require('stream').Stream , EventEmitter = require('events').EventEmitter , EventBuffer = require('../lib/event_buffer.js').EventBuffer , assert = require('assert') , tests; tests = { 'test basic event buffer functionality': function () { var source = new Stream() , dest = new EventEmitter() , buff = new EventBuffer(source) , data = ''; dest.on('data', function (d) { data += d; }); source.writeable = true; source.readable = true; source.emit('data', 'abcdef'); source.emit('data', '123456'); buff.sync(dest); assert.equal('abcdef123456', data); source.emit('data', '---'); assert.equal('abcdef123456---', data); } }; module.exports = tests; utilities-1.0.4/test/file.js000066400000000000000000000142541240177617000157760ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var assert = require('assert') , fs = require('fs') , path = require('path') , file = require('../lib/file') , existsSync = fs.existsSync || path.existsSync , tests; tests = { 'before': function () { process.chdir('./test'); } , 'after': function () { process.chdir('../'); } , 'test mkdirP': function () { var expected = [ ['foo'] , ['foo', 'bar'] , ['foo', 'bar', 'baz'] , ['foo', 'bar', 'baz', 'qux'] ] , res; file.mkdirP('foo/bar/baz/qux'); res = file.readdirR('foo'); for (var i = 0, ii = res.length; i < ii; i++) { assert.equal(path.join.apply(path, expected[i]), res[i]); } file.rmRf('foo', {silent: true}); } , 'test rmRf': function () { file.mkdirP('foo/bar/baz/qux', {silent: true}); file.rmRf('foo/bar', {silent: true}); res = file.readdirR('foo'); assert.equal(1, res.length); assert.equal('foo', res[0]); fs.rmdirSync('foo'); } , 'test cpR with same name and different directory': function () { file.mkdirP('foo', {silent: true}); fs.writeFileSync('foo/bar.txt', 'w00t'); file.cpR('foo', 'bar', {silent: true}); assert.ok(existsSync('bar/bar.txt')); file.rmRf('foo', {silent: true}); file.rmRf('bar', {silent: true}); } , 'test cpR with same to and from will throw': function () { assert.throws(function () { file.cpR('foo.txt', 'foo.txt', {silent: true}); }); } , 'test cpR rename via copy in directory': function () { file.mkdirP('foo', {silent: true}); fs.writeFileSync('foo/bar.txt', 'w00t'); file.cpR('foo/bar.txt', 'foo/baz.txt', {silent: true}); assert.ok(existsSync('foo/baz.txt')); file.rmRf('foo', {silent: true}); } , 'test cpR rename via copy in base': function () { fs.writeFileSync('bar.txt', 'w00t'); file.cpR('bar.txt', 'baz.txt', {silent: true}); assert.ok(existsSync('baz.txt')); file.rmRf('bar.txt', {silent: true}); file.rmRf('baz.txt', {silent: true}); } , 'test readdirR': function () { var expected = [ ['foo'] , ['foo', 'bar'] , ['foo', 'bar', 'baz'] , ['foo', 'bar', 'baz', 'qux'] ] , res; file.mkdirP('foo/bar/baz/qux', {silent: true}); res = file.readdirR('foo'); for (var i = 0, ii = res.length; i < ii; i++) { assert.equal(path.join.apply(path, expected[i]), res[i]); } file.rmRf('foo', {silent: true}); } , 'test isAbsolute with Unix absolute path': function () { var p = '/foo/bar/baz'; assert.equal('/', file.isAbsolute(p)); } , 'test isAbsolute with Unix relative path': function () { var p = 'foo/bar/baz'; assert.equal(false, file.isAbsolute(p)); } , 'test isAbsolute with Win absolute path': function () { var p = 'C:\\foo\\bar\\baz'; assert.equal('C:\\', file.isAbsolute(p)); } , 'test isAbsolute with Win relative path': function () { var p = 'foo\\bar\\baz'; assert.equal(false, file.isAbsolute(p)); } , 'test absolutize with Unix absolute path': function () { var expected = '/foo/bar/baz' , actual = file.absolutize('/foo/bar/baz'); assert.equal(expected, actual); } , 'test absolutize with Win absolute path': function () { var expected = 'C:\\foo\\bar\\baz' , actual = file.absolutize('C:\\foo\\bar\\baz'); assert.equal(expected, actual); } , 'test absolutize with relative path': function () { var expected = process.cwd() , actual = ''; // We can't just create two different tests here // because file.absolutize uses process.cwd() // to get absolute path which is platform // specific if (process.platform === 'win32') { expected += '\\foo\\bar\\baz' actual = file.absolutize('foo\\bar\\baz') } else { expected += '/foo/bar/baz' actual = file.absolutize('foo/bar/baz'); } assert.equal(expected, actual); } , 'test basedir with Unix absolute path': function () { var p = '/foo/bar/baz'; assert.equal('/foo/bar', file.basedir(p)); } , 'test basedir with Win absolute path': function () { var p = 'C:\\foo\\bar\\baz'; assert.equal('C:\\foo\\bar', file.basedir(p)); } , 'test basedir with Unix root path': function () { var p = '/'; assert.equal('/', file.basedir(p)); } , 'test basedir with Unix absolute path and double-asterisk': function () { var p = '/**/foo/bar/baz'; assert.equal('/', file.basedir(p)); } , 'test basedir with leading double-asterisk': function () { var p = '**/foo'; assert.equal('.', file.basedir(p)); } , 'test basedir with leading asterisk': function () { var p = '*.js'; assert.equal('.', file.basedir(p)); } , 'test basedir with leading dot-slash and double-asterisk': function () { var p = './**/foo'; assert.equal('.', file.basedir(p)); } , 'test basedir with leading dirname and double-asterisk': function () { var p = 'a/**/*.js'; assert.equal('a', file.basedir(p)); } , 'test basedir with leading dot-dot-slash and double-asterisk': function () { var p = '../../test/**/*.js'; assert.equal('../../test', file.basedir(p)); } , 'test basedir with single-asterisk in dirname': function () { var p = 'a/test*/file'; assert.equal('a', file.basedir(p)); } , 'test basedir with single filename': function () { var p = 'filename'; assert.equal('.', file.basedir(p)); } , 'test basedir with empty path': function () { var p = ''; assert.equal('.', file.basedir(p)); assert.equal('.', file.basedir()); } }; module.exports = tests; utilities-1.0.4/test/i18n.js000066400000000000000000000033071240177617000156330ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var i18n = require('../lib/i18n') , assert = require('assert') , tests , inst = {}; tests = { 'before': function () { i18n.loadLocale('en-us', {foo: 'FOO', bar: 'BAR', baz: 'BAZ'}); i18n.loadLocale('ja-jp', {foo: 'フー', bar: 'バー'}); inst.en = new i18n.I18n('en-us'); inst.jp = new i18n.I18n('ja-jp'); inst.de = new i18n.I18n('de-de'); } , 'test default-locale fallback, defined strings': function () { var expected = 'BAZ' , actual = inst.jp.t('baz'); assert.equal(expected, actual); } , 'test default-locale fallback, no defined strings': function () { var expected = 'BAZ' , actual = inst.de.t('baz'); assert.equal(expected, actual); } , 'test key lookup, default-locale': function () { var expected = 'FOO' , actual = inst.en.t('foo'); assert.equal(expected, actual); } , 'test key lookup, non-default-locale': function () { var expected = 'フー' , actual = inst.jp.t('foo'); assert.equal(expected, actual); } }; module.exports = tests; utilities-1.0.4/test/inflection.js000066400000000000000000000333601240177617000172100ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var inflection = require('../lib/inflection') , assert = require('assert') , esInflections , sInflections , iesInflections , vesInflections , icesInflections , renInflections , oesInflections , iInflections , genInflections , irregularInflections , noInflections , tests; /** * Most test inflections are from Ruby on Rails: * https://github.com/rails/rails/blob/master/activesupport/test/inflector_test_cases.rb * * Ruby on Rails is MIT licensed: http://www.opensource.org/licenses/MIT */ esInflections = [ ["search", "searches"] , ["switch", "switches"] , ["fix", "fixes"] , ["box", "boxes"] , ["process", "processes"] , ["address", "addresses"] , ["wish", "wishes"] , ["status", "statuses"] , ["alias", "aliases"] , ["basis", "bases"] , ["diagnosis", "diagnoses"] , ["bus", "buses"] ]; sInflections = [ ["stack", "stacks"] , ["shoe", "shoes"] , ["status_code", "status_codes"] , ["case", "cases"] , ["edge", "edges"] , ["archive", "archives"] , ["experience", "experiences"] , ["day", "days"] , ["comment", "comments"] , ["foobar", "foobars"] , ["newsletter", "newsletters"] , ["old_news", "old_news"] , ["perspective", "perspectives"] , ["diagnosis_a", "diagnosis_as"] , ["horse", "horses"] , ["prize", "prizes"] ]; iesInflections = [ ["category", "categories"] , ["query", "queries"] , ["ability", "abilities"] , ["agency", "agencies"] ]; vesInflections = [ ["wife", "wives"] , ["safe", "saves"] , ["half", "halves"] , ["elf", "elves"] , ["dwarf", "dwarves"] ]; icesInflections = [ ["index", "indices"] , ["vertex", "vertices"] , ["matrix", "matrices"] ]; renInflections = [ ["node_child", "node_children"] , ["child", "children"] ]; oesInflections = [ ["buffalo", "buffaloes"] , ["tomato", "tomatoes"] ]; iInflections = [ ["octopus", "octopi"] , ["virus", "viri"] ]; genInflections = [ ["salesperson", "salespeople"] , ["person", "people"] , ["spokesman", "spokesmen"] , ["man", "men"] , ["woman", "women"] ]; irregularInflections = [ ["datum", "data"] , ["medium", "media"] , ["ox", "oxen"] , ["cow", "kine"] , ["mouse", "mice"] , ["louse", "lice"] , ["axis", "axes"] , ["testis", "testes"] , ["crisis", "crises"] , ["analysis", "analyses"] , ["quiz", "quizzes"] ]; noInflections = [ ["fish", "fish"] , ["news", "news"] , ["series", "series"] , ["species", "species"] , ["rice", "rice"] , ["information", "information"] , ["equipment", "equipment"] ]; tests = { 'test es plural words for inflection': function () { var i = esInflections.length , value; while (--i >= 0) { value = esInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test es singular words for inflection': function () { var i = esInflections.length , value; while (--i >= 0) { value = esInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test es plural words for inflection consistency': function() { var i = esInflections.length , value; while (--i >= 0) { value = esInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test es singular words for inflection consistency': function() { var i = esInflections.length , value; while (--i >= 0) { value = esInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test s plural words for inflection': function () { var i = sInflections.length , value; while (--i >= 0) { value = sInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test s singular words for inflection': function () { var i = sInflections.length , value; while (--i >= 0) { value = sInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test s plural words for inflection consistency': function () { var i = sInflections.length , value; while (--i >= 0) { value = sInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test s singular words for inflection consistency': function () { var i = sInflections.length , value; while (--i >= 0) { value = sInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test ies plural words for inflection': function () { var i = iesInflections.length , value; while (--i >= 0) { value = iesInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test ies singular words for inflection': function () { var i = iesInflections.length , value; while (--i >= 0) { value = iesInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test ies plural words for inflection consistency': function () { var i = iesInflections.length , value; while (--i >= 0) { value = iesInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test ies singular words for inflection consistency': function () { var i = iesInflections.length , value; while (--i >= 0) { value = iesInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test ves plural words for inflection': function () { var i = vesInflections.length , value; while (--i >= 0) { value = vesInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test ves singular words for inflection': function () { var i = vesInflections.length , value; while (--i >= 0) { value = vesInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test ves plural words for inflection consistency': function () { var i = vesInflections.length , value; while (--i >= 0) { value = vesInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test ves singular words for inflection consistency': function () { var i = vesInflections.length , value; while (--i >= 0) { value = vesInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test ices plural words for inflection': function () { var i = icesInflections.length , value; while (--i >= 0) { value = icesInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test ices singular words for inflection': function () { var i = icesInflections.length , value; while (--i >= 0) { value = icesInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test ices plural words for inflection consistency': function () { var i = icesInflections.length , value; while (--i >= 0) { value = icesInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test ices singular words for inflection consistency': function () { var i = icesInflections.length , value; while (--i >= 0) { value = icesInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test ren plural words for inflection': function () { var i = renInflections.length , value; while (--i >= 0) { value = renInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test ren singular words for inflection': function () { var i = renInflections.length , value; while (--i >= 0) { value = renInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test ren plural words for inflection consistency': function () { var i = renInflections.length , value; while (--i >= 0) { value = renInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test ren singular words for inflection consistency': function () { var i = renInflections.length , value; while (--i >= 0) { value = renInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test oes plural words for inflection': function () { var i = oesInflections.length , value; while (--i >= 0) { value = oesInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test oes singular words for inflection': function () { var i = oesInflections.length , value; while (--i >= 0) { value = oesInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test oes plural words for inflection consistency': function () { var i = oesInflections.length , value; while (--i >= 0) { value = oesInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test oes singular words for inflection consistency': function () { var i = oesInflections.length , value; while (--i >= 0) { value = oesInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test i plural words for inflection': function () { var i = iInflections.length , value; while (--i >= 0) { value = iInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test i singular words for inflection': function () { var i = iInflections.length , value; while (--i >= 0) { value = iInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test i plural words for inflection consistency': function () { var i = iInflections.length , value; while (--i >= 0) { value = iInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test i singular words for inflection consistency': function () { var i = iInflections.length , value; while (--i >= 0) { value = iInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test gender and people plural words for inflection': function () { var i = genInflections.length , value; while (--i >= 0) { value = genInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test gender and people singular words for inflection': function () { var i = genInflections.length , value; while (--i >= 0) { value = genInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test gender and people plural words for inflection consistency': function () { var i = genInflections.length , value; while (--i >= 0) { value = genInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test gender and people singular words for inflection consistency': function () { var i = genInflections.length , value; while (--i >= 0) { value = genInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test irregular plural words for inflection': function () { var i = irregularInflections.length , value; while (--i >= 0) { value = irregularInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test irregular singular words for inflection': function () { var i = irregularInflections.length , value; while (--i >= 0) { value = irregularInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test irregular plural words for inflection consistency': function () { var i = irregularInflections.length , value; while (--i >= 0) { value = irregularInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test irregular singular words for inflection consistency': function () { var i = irregularInflections.length , value; while (--i >= 0) { value = irregularInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } , 'test no change plural words for inflection': function () { var i = noInflections.length , value; while (--i >= 0) { value = noInflections[i]; assert.equal(value[1], inflection.pluralize(value[0])) } } , 'test no change singular words for inflection': function () { var i = noInflections.length , value; while (--i >= 0) { value = noInflections[i]; assert.equal(value[0], inflection.singularize(value[1])) } } , 'test no change plural words for inflection consistency': function () { var i = noInflections.length , value; while (--i >= 0) { value = noInflections[i]; assert.equal(value[1], inflection.pluralize(value[1])) } } , 'test no change singular words for inflection consistency': function () { var i = noInflections.length , value; while (--i >= 0) { value = noInflections[i]; assert.equal(value[0], inflection.singularize(value[0])) } } }; module.exports = tests; utilities-1.0.4/test/logging.js000066400000000000000000000034771240177617000165120ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var assert = require('assert') , logger = require('../lib/log') , tests; tests = { 'test basic logging': function () { var oldLog = console.log; console.log = function (str) { assert.equal(str, "basic log"); }; logger.log("basic log"); console.log = oldLog; } , 'test info logging': function () { var oldinfoLog = console.info; console.info = function (str) { assert.equal(str, "info log"); }; logger.info("info log"); console.info = oldinfoLog; } , 'test warning logging': function () { var oldwarnLog = console.warn; console.warn = function (str) { assert.equal(str, "warn log"); }; logger.warn("warn log"); console.warn = oldwarnLog; } , 'test error logging': function () { var oldErrorLog = console.error; console.error = function (str) { assert.equal(str, "error log"); }; logger.error("error log"); console.error = oldErrorLog; } } module.exports = tests;utilities-1.0.4/test/network.js000066400000000000000000000015261240177617000165460ustar00rootroot00000000000000var assert = require('assert') , sys = require('sys') , net = require('net') , network = require('../lib/network') , tests; tests = { 'test a port is open': function (next) { var expected = false , port = 49152; network.isPortOpen(port, null, function (err, isOpen) { assert.ifError(err); assert.equal(expected, isOpen); next(); }); } , 'test a port is closed': function (next) { var expected = true , port = 49153 , server = net.createServer(); server.listen(port, function () { network.isPortOpen(port, null, function (err, isOpen) { assert.ifError(err); assert.equal(expected, isOpen); server.close(function () { next(); }); }); }); } } module.exports = tests;utilities-1.0.4/test/object.js000066400000000000000000000051611240177617000163220ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var object = require('../lib/object') , array = require('../lib/array') , assert = require('assert') , tests = {} , checkObjects; tests = { 'test merge in object': function () { var expected = {user: 'geddy', key: 'key'} , actual = object.merge({user: 'geddy'}, {key: 'key'}); assert.deepEqual(actual, expected); } , 'test merge with overwriting keys in object': function () { var expected = {user: 'geddy', key: 'key'} , actual = object.merge({user: 'geddy', key: 'geddyKey'}, {key: 'key'}); assert.deepEqual(actual, expected); } , 'test merge with objects as keys': function () { var expected = {user: {name: 'geddy', password: 'random', key: 'key'}, key: 'key'} , actual = object.merge({key: 'key'}, {user: {name: 'geddy', password: 'random', key: 'key'}}); assert.deepEqual(actual, expected); } , 'test reverseMerge in object': function () { var expected = {user: 'geddy', key: 'key'} , actual = object.reverseMerge({user: 'geddy'}, {key: 'key'}); assert.deepEqual(actual, expected); } , 'test reverseMerge with keys overwriting default in object': function () { var expected = {user: 'geddy', key: 'geddyKey'} , actual = object.reverseMerge({user: 'geddy', key: 'geddyKey'}, {key: 'key'}); assert.deepEqual(actual, expected); } , 'test reverseMerge with objects as keys': function () { var expected = {user: {name: 'geddy', password: 'random', key: 'key'}, key: 'key'} , actual = object.merge({user: {name: 'geddy', password: 'random', key: 'key'}}, {key: 'key'}); assert.deepEqual(actual, expected); } , 'test isEmpty with non empty object in object': function () { var expected = false , actual = object.isEmpty({user: 'geddy'}); assert.equal(actual, expected); } , 'test isEmpty with empty object in object': function () { var expected = true , actual = object.isEmpty({}); assert.equal(actual, expected); } }; module.exports = tests; utilities-1.0.4/test/sorted_collection.js000066400000000000000000000063061240177617000205710ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var SortedCollection = require('../lib/sorted_collection').SortedCollection , assert = require('assert') , tests; tests = { 'test no default value': function () { // Set up a collection, no default value for new items var c = new SortedCollection(); // Add some items c.addItem('testA', 'AAAA'); c.addItem('testB', 'BBBB'); c.addItem('testC', 'CCCC'); // Test count assert.equal(3, c.count); // Test getItem by string key var item = c.getItem('testC'); assert.equal('CCCC', item); // Test getItem by index number var item = c.getItem(1); assert.equal('BBBB', item); // Test setItem by string key c.setItem('testA', 'aaaa'); var item = c.getItem('testA'); assert.equal('aaaa', item); // Test setItem by index number c.setItem(2, 'cccc'); var item = c.getItem(2); assert.equal('cccc', item); } , 'test default value': function () { // Set up a collection, default value for new items is 'foo' var c = new SortedCollection('foo'); // Add an item with no value -- should get // default value c.addItem('testA'); // Add some items with empty/falsey values -- // should be set to desired values c.addItem('testB', null); c.addItem('testC', false); // Test getItem for default value var item = c.getItem('testA'); assert.equal('foo', item); var item = c.getItem('testB'); assert.equal(null, item); var item = c.getItem('testC'); assert.equal(false, item); } , 'test each': function () { var c = new SortedCollection() , str = ''; // Add an item with no value -- should get // default value c.addItem('a', 'A'); c.addItem('b', 'B'); c.addItem('c', 'C'); c.addItem('d', 'D'); c.each(function (val, key) { str += val + key; }); assert.equal('AaBbCcDd', str); } , 'test removing an item': function () { var c = new SortedCollection() , str = ''; // Add an item with no value -- should get // default value c.addItem('a', 'A'); c.addItem('b', 'B'); c.addItem('c', 'C'); c.addItem('d', 'D'); assert.equal(4, c.count); omg = c.removeItem('c'); assert.equal(3, c.count); c.each(function (val, key) { str += val + key; }); assert.equal('AaBbDd', str); } , 'test clone': function () { var c = new SortedCollection() , copy; c.addItem('a', 'A'); c.addItem('b', 'B'); copy = c.clone(); assert.equal(2, copy.count); assert.equal('A', copy.getItem('a')); } }; module.exports = tests; utilities-1.0.4/test/string.js000066400000000000000000000300231240177617000163550ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var assert = require('assert') , string = require('../lib/string') , tests; tests = { 'test basic escapeXML for string': function () { var expected = '<html></html>' , actual = string.escapeXML(''); assert.equal(expected, actual); } , 'test all escape characters for escapeXML': function () { var expected = '<>&"'' , actual = string.escapeXML('<>&"\''); assert.equal(expected, actual); } , 'test no escape characters with string for escapeXML': function () { var expected = 'Geddy' , actual = string.escapeXML('Geddy'); assert.equal(expected, actual); } , 'test no escape characters with numbers for escapeXML': function () { var expected = 05 , actual = string.escapeXML(05); assert.equal(expected, actual); } , 'test basic unescapeXML for string': function () { var expected = '' , actual = string.unescapeXML('<html></html>'); assert.equal(expected, actual); } , 'test all escape characters for unescapeXML': function () { var expected = '<>&"\'' , actual = string.unescapeXML('<>&"''); assert.equal(expected, actual); } , 'test no escape characters with string for unescapeXML': function () { var expected = 'Geddy' , actual = string.unescapeXML('Geddy'); assert.equal(expected, actual); } , 'test no escape characters with numbers for unescapeXML': function () { var expected = 05 , actual = string.unescapeXML(05); assert.equal(expected, actual); } , 'test basic needsEscape for string': function () { var expected = true , actual = string.needsEscape('Geddy>'); assert.equal(expected, actual); } , 'test basic needsEscape thats false for string': function () { var expected = false , actual = string.needsEscape('Geddy'); assert.equal(expected, actual); } , 'test basic needsUnescape for string': function () { var expected = true , actual = string.needsEscape('"Geddy"'); assert.equal(expected, actual); } , 'test basic needsUnescape thats false for string': function () { var expected = false , actual = string.needsEscape('Geddy'); assert.equal(expected, actual); } , 'test escapeRegExpCharacters': function () { var expected = '\\^\\/\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}\\\\' actual = string.escapeRegExpChars('^/.*+?|()[]{}\\'); assert.equal(expected, actual); } , 'test toArray for string': function () { var data = string.toArray('geddy') , expected = ['g', 'e', 'd', 'd', 'y']; // Loop through each item and check // if not, then the arrays aren't _really_ the same var i = expected.length; while (--i >= 0) { assert.equal(expected[i], data[i]); } } , 'test reverse for string': function () { var data = string.reverse('yddeg') , expected = 'geddy'; assert.equal(expected, data); } , 'test basic ltrim for string': function () { var data = string.ltrim(' geddy') , expected = 'geddy'; assert.equal(expected, data); } , 'test custom char ltrim for string': function () { var data = string.ltrim('&&geddy', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test basic rtrim for string': function () { var data = string.rtrim('geddy ') , expected = 'geddy'; assert.equal(expected, data); } , 'test custom char rtrim for string': function () { var data = string.rtrim('geddy&&', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test basic trim for string': function () { var data = string.trim(' geddy ') , expected = 'geddy'; assert.equal(expected, data); } , 'test custom char trim for string': function () { var data = string.trim('&geddy&&', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test chop special-case line-ending': function () { var expected = 'geddy' , actual = string.chop('geddy\r\n'); assert.equal(expected, actual); } , 'test chop not actual special-case line-ending': function () { var expected = 'geddy\n' , actual = string.chop('geddy\n\r'); assert.equal(expected, actual); } , 'test chop normal line-ending': function () { var expected = 'geddy' , actual = string.chop('geddy\n'); assert.equal(expected, actual); } , 'test chop whatever character': function () { var expected = 'gedd' , actual = string.chop('geddy'); assert.equal(expected, actual); } , 'test chop empty string': function () { var expected = '' , actual = string.chop(''); assert.equal(expected, actual); } , 'test basic lpad for string': function () { var data = string.lpad('geddy', '&', 7) , expected = '&&geddy'; assert.equal(expected, data); } , 'test lpad without width for string': function () { var data = string.lpad('geddy', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test lpad without width of char for string': function () { var data = string.lpad('geddy') , expected = 'geddy'; assert.equal(expected, data); } , 'test basic rpad for string': function () { var data = string.rpad('geddy', '&', 7) , expected = 'geddy&&'; assert.equal(expected, data); } , 'test rpad without width for string': function () { var data = string.rpad('geddy', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test rpad without width of char for string': function () { var data = string.rpad('geddy') , expected = 'geddy'; assert.equal(expected, data); } , 'test basic pad for string': function () { var data = string.pad('geddy', '&', 7) , expected = '&geddy&'; assert.equal(expected, data); } , 'test pad without width for string': function () { var data = string.pad('geddy', '&') , expected = 'geddy'; assert.equal(expected, data); } , 'test pad without width of char for string': function () { var data = string.pad('geddy') , expected = 'geddy'; assert.equal(expected, data); } , 'test single tags in truncateHTML': function () { var str = string.truncateHTML('

Once upon a time in a world

', { length: 10 }); assert.equal(str, '

Once up...

'); } , 'test multiple tags in truncateHTML': function () { var str = string.truncateHTML('

Once upon a time in a world

', { length: 10 }); assert.equal(str, '

Once up...in a wo...

'); } , 'test multiple tags but only truncate once in truncateHTML': function () { var str = string.truncateHTML('

Once upon a time in a world

', { length: 10, once: true }); assert.equal(str, '

Once up...in a world

'); } , 'test standard truncate': function () { var str = string.truncate('Once upon a time in a world', { length: 10 }); assert.equal(str, 'Once up...'); } , 'test custom omission in truncate': function () { var str = string.truncate('Once upon a time in a world', { length: 10, omission: '///' }); assert.equal(str, 'Once up///'); } , 'test regex seperator in truncate': function () { var str = string.truncate('Once upon a time in a world', { length: 15, seperator: /\s/ }); assert.equal(str, 'Once upon a...'); } , 'test string seperator in truncate': function () { var str = string.truncate('Once upon a time in a world', { length: 15, seperator: ' ' }); assert.equal(str, 'Once upon a...'); } , 'test unsafe html in truncate': function () { var str = string.truncate('

Once upon a time in a world

', { length: 20 }); assert.equal(str, '

Once upon a ti...'); } , 'test nl2br for string': function () { var data = string.nl2br("geddy\n") , expected = 'geddy
'; assert.equal(expected, data); } , 'test snakeize for string': function () { var data = string.snakeize("geddyJs") , expected = 'geddy_js'; assert.equal(expected, data); } , 'test snakeize with beginning caps for string': function () { var data = string.snakeize("GeddyJs") , expected = 'geddy_js'; assert.equal(expected, data); } , 'test camelize for string': function () { var data = string.camelize("geddy_js") , expected = 'geddyJs'; assert.equal(expected, data); } , 'test camelize with initialCap for string': function () { var data = string.camelize("geddy_js", {initialCap: true}) , expected = 'GeddyJs'; assert.equal(expected, data); } , 'test camelize with leadingUnderscore with no underscore for string': function () { var data = string.camelize("geddy_js", {leadingUnderscore: true}) , expected = 'geddyJs'; assert.equal(expected, data); } , 'test camelize with leadingUnderscore with underscore for string': function () { var data = string.camelize("_geddy_js", {leadingUnderscore: true}) , expected = '_geddyJs'; assert.equal(expected, data); } , 'test decapitalize for string': function () { var data = string.decapitalize("Geddy") , expected = 'geddy'; assert.equal(expected, data); } , 'test capitalize for string': function () { var data = string.capitalize("geddy") , expected = 'Geddy'; assert.equal(expected, data); } , 'test dasherize for string': function () { var data = string.dasherize("geddyJs") , expected = 'geddy-js'; assert.equal(expected, data); } , 'test dasherize with custom replace char for string': function () { var data = string.dasherize("geddyJs", "_") , expected = 'geddy_js'; assert.equal(expected, data); } , 'test underscorize for string': function () { var data = string.underscorize("geddyJs") , expected = 'geddy_js'; assert.equal(expected, data); } , 'test include for string with included string': function () { assert.ok(string.include('foobarbaz', 'foo')); } , 'test include for string with not included string': function () { assert.ok(!string.include('foobarbaz', 'qux')); } , 'test getInflections for string': function () { var actual = string.getInflections("string") , expected = { filename: { singular: "string" , plural: "strings" }, constructor: { singular: "String" , plural: "Strings" }, property: { singular: "string" , plural: "strings" }, }; assert.deepEqual(expected, actual); } , 'test inflection with odd name for string': function () { var actual = string.getInflections("snow_dog") , expected = { filename: { singular: "snow_dog" , plural: "snow_dogs" }, constructor: { singular: "SnowDog" , plural: "SnowDogs" }, property: { singular: "snowDog" , plural: "snowDogs" }, }; assert.deepEqual(expected, actual); } , 'test uuid length for string': function () { var data = string.uuid(5).length , expected = 5; assert.equal(expected, data); } , 'test stripTags': function () { var html = '

foo

bar
wooby

' , expected = 'foobarwooby'; assert.equal(string.stripTags(html), expected); } , 'test stripTags with allowed
': function () { var html = '
foo

bar
wooby

' , expected = 'foobar
wooby'; assert.equal(string.stripTags(html, '
'), expected); } }; module.exports = tests; utilities-1.0.4/test/uri.js000066400000000000000000000103771240177617000156600ustar00rootroot00000000000000/* * Utilities: A classic collection of JavaScript utilities * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ var uri = require('../lib/uri') , array = require('../lib/array') , assert = require('assert') , tests = {}; tests = { 'test getFileExtension for uri': function () { var data = uri.getFileExtension('users.json') , actual = 'json'; assert.equal(actual, data); } , 'test paramify for uri': function () { var data = uri.paramify({username: 'user', token: 'token', secret: 'secret'}) , actual = 'username=user&token=token&secret=secret'; assert.equal(actual, data); } , 'test paramify with conslidate option for uri': function () { var data = uri.paramify({username: 'user', auth: ['token', 'secret']}, {conslidate: true}) , actual = 'username=user&auth=token&auth=secret'; assert.equal(actual, data); } , 'test paramify with includeEmpty option for uri': function () { var data = uri.paramify({username: 'user', token: ''}, {includeEmpty: true}) , actual = 'username=user&token='; assert.equal(actual, data); } , 'test paramify with includeEmpty as 0 option for uri': function () { var data = uri.paramify({username: 'user', token: 0}, {includeEmpty: true}) , actual = 'username=user&token=0'; assert.equal(actual, data); } , 'test paramify with includeEmpty as null option for uri': function () { var data = uri.paramify({username: 'user', token: null}, {includeEmpty: true}) , actual = 'username=user&token='; assert.equal(actual, data); } , 'test paramify with includeEmpty as undefined option for uri': function () { var data = uri.paramify({username: 'user', token: undefined}, {includeEmpty: true}) , actual = 'username=user&token='; assert.equal(actual, data); } , 'test paramify with snakeize option for uri': function () { var data = uri.paramify({username: 'user', authToken: 'token'}, {snakeize: true}) , actual = 'username=user&auth_token=token'; assert.equal(actual, data); } , 'test paramify with escapeVals option for uri': function () { var data = uri.paramify({username: 'user', token: '