UNPKG

nmsp

Version:

Create, manage and extend your namespaces in the browser and NodeJS.

280 lines (224 loc) 7.94 kB
/*! github.com/ryanfitzer/nmsp/blob/master/LICENSE */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(factory); } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { // CommonJS module.exports = factory(); } else { // Browser globals root.nmsp = factory(); } }(this, function ( exports ) { var toString = Object.prototype.toString; /** * Test if a value is an Object. * * @example * if ( !isObject( someValue ) ) return; * * @param value {*} The value to test. * @returns {Boolean} */ function isObject( value ) { return toString.call( value ) === toString.call( {} ); } /** * Assign (recursively) the properties of a source object to * a destination object. If a `context` object is provided, * the returned function will use it as the destination to extend. * * @example * var extendWithContext = extend( myAwesomeContext ); * extendWithContext( myAwesomeSource ); * extendWithContext( anotherAwesomeSource ); * // or * var extender = extend(); * var totesExtended = extender( myTotesDest, myTotesSrc ); * * @param [context] {Object} The object to use as the returned function's destination * @returns {Function} The function accepts a `dest` and `src` object as arguments. */ function extend( context ) { return function ( dest, src ) { if ( context ) { src = dest; dest = context; } Object.keys( src ).forEach( function ( key ) { if ( isObject( src[ key ] ) ) { dest[ key ] = isObject( dest[ key ] ) ? dest[ key ] : {}; dest[ key ] = dest[ key ] || {}; extend( dest[ key ] )( src[ key ] ); } else { dest[ key ] = src[ key ]; } }); return dest; }; } /** * Create an array of elements from a path. * * @example * var myPath = toPath( 'a.b.c.d' ); * // [ 'a', 'b', 'c', 'd' ] * var myOtherPath = toPath( [ 'a', 'b', 'c', 'd' ] ); * // [ 'a', 'b', 'c', 'd' ] * * @private * * @param path {String|Array} The path. * @returns {Array} The path's elements. */ function toPath( path ) { return Array.isArray( path ) ? path : path.split( '.' ); } /** * Get the value at a path in a source object. If a `context` object is provided, * the returned function will use it as the source. * * @example * var atPathWithContext = atPath( myAwesomeContext ); * var myAwesomeValue = atPathWithContext( 'my.awesome.prop' ); * var myEvenMoreAwesomeValue = atPathWithContext( 'my.even.more.awesome.prop' ); * // or * var getAtDatPath = atPath(); * var gimmeSomeDatValue = getAtThatPath( 'dat.prop.tho', datSrcTho ); * * @param [context] {Object} The object to use as the returned function's `src` argument. * @param path {String} The path to search in the object. * @param src {Object} The object to search. * @returns {Function} The function accepts a `path` and `src` object as arguments and returns the value found at `path`. */ function atPath( context ) { return function ( path, src ) { src = context || src; return toPath( path ).reduce( function ( accum, key ) { return accum && accum[ key ]; }, src ); }; } /** * Create a nested object based on the provided path. * * @example * var objFromPath = fromPath( 'one.two.three` ); * // { one: { two: { three: {} } } } * * @param path {String} The path to model the object. * @returns {Object} */ function fromPath( path ) { var accumulator = {}; toPath( path ).reduce( function ( accum, key ) { accum[ key ] = {}; return accum[ key ]; }, accumulator ); return accumulator; } /** * Create a plain object that consists of only the enumerable own properties of a source object. * * @example * var myNameSpace = nmsp( { some: { data: {} } } ); * myNameSpace.extend( 'some.other.data', { the: 'data' } ); * var plainObj = myNameSpace.plain(); * * @param store {Object} The object to return as a plain object. * @returns {Object} */ function plain( store ) { return function( src ) { src = store || src; // @support: IE and Nashorn do not not support Object.assign(); return Object.keys( src ).reduce( function ( accum, key ) { accum[ key ] = src[ key ]; return accum; }, {} ); }; } /** * Create an object with an API that enables easy extension. * * @example * var myNameSpace = nmsp(); * myNameSpace.extend( { some: { data: {} } } ); * myNameSpace.extend( 'some.other.data', { the: 'data' } ); * // or * var myNameSpace = nmsp( { some: { data: {} } } ); * myNameSpace.extend( 'some.other.data', { the: 'data' } ); * * // { * // some: { * // data: {}, * // other: { * // data: { * // the: 'data' * // } * // } * // } * // } * * @param [initialValue] {Object|String} Create a namespace with an existing object. A path string (ex: `'a.b.c'` ) can be passed as the model for the namespace. * @returns `{Object}` An object extended with the `nmsp` API. */ function nmsp( initialValue ) { var extendStore , store = Object.create( {} ) ; if ( isObject( initialValue ) ) { store = initialValue; } else if ( initialValue !== undefined ) { store = fromPath( initialValue ); } extendStore = extend( store ); return Object.defineProperties( store, { nmsp: { value: true }, extend: { value: function ( path, src ) { // Extend the store at the top level using // the path value as src. if ( !src ) { extendStore( path ); } // Extend the store at a specific path. else { // Get the value at the path. var pathValue = atPath( store )( path ); if ( Array.isArray( pathValue ) ) { // Concatenate the path array with the src value. extend( pathValue )( pathValue.concat( src ) ); } else { // Create the required path, if not already present. extendStore( fromPath( path ) ); // Set the path value as context and // extend it with the src value. extend( atPath( store )( path ) )( src ); } } } }, atPath: { value: atPath( store ) }, plain: { value: plain( store ) } }); } exports = nmsp; exports.extend = extend(); exports.atPath = atPath(); exports.plain = plain(); exports.fromPath = fromPath; return exports; }));