nk
Version:
nodakwaeri (nk): A simple, yet powerful, and fully-featured cross-platform application framework for Node.js.
508 lines (438 loc) • 13.8 kB
JavaScript
/**
* package: nodakwaeri
* version: 0.3.2
* author: Richard B. Winters <a href='mailto:rik@mmogp.com'>Rik At MMOGP</a>
* copyright: 2011-2015 Massively Modified, Inc.
* license: Apache, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>
*/
// Deps
var http = require( 'http' ),
env = process.env.NODE_ENV,
argv;
if( env === ( "" || null ) )
{
env = "development";
}
// See if admin argument is passed, if so set the proper variables for usage within the framework
if( process.argv.length > 2 )
{
argv = [];
var index = 0;
for( var i = 2; i < process.argv.length; i++ )
{
argv[index] = process.argv[i];
}
}
// Entry point to the NodaKwaeri module
function nk( config )
{
na = this;
if( config )
{
// Set up dev or production environment
this.config = config[env];
this.config.debug = ( env === "development" ) ? true : false;
// See if we are running the public or administrative application
if( argv && argv.length > 0 )
{
this.admin = false;
if( argv[0] === 'admin' )
{
this.admin = true;
this.config.appType = 'admin';
}
else
{
this.config.appType = 'app';
}
}
else
{
this.admin = false;
this.config.appType = 'app';
// Remove the admin paths from memory
delete this.config.admin;
}
}
else
{
//console.log( 'No config - loading core tools.' );
for( var lib in this )
{
if( !na._coremap.hasOwnProperty( lib ) )
{
delete this.lib;
}
}
}
};
// Component version string
nk.prototype._versionmap =
{
"nodakwaeri": "0.2.4"
};
// Object class to class type mapping
nk.prototype._classmap =
{
// List of types
"[object Boolean]": "boolean",
"[object Number]": "number",
"[object String]": "string",
"[object Function]": "function",
"[object Array]": "array",
"[object Date]": "date",
"[object RegExp]": "regexp",
"[object Object]": "object"
};
// Core facility mapping
nk.prototype._coremap =
{
"nk": true,
"_versionmap": true,
"_classmap": true,
"_coremap": true,
"version": true,
"type": true,
"extend": true,
"each": true,
"sjcl": true,
"hash": true,
"html": true
}
/**
* Gets the current NodaKwaeri Version
*
* @returns string The NodaKwaeri version string
*
* @since 0.0.1
*/
nk.prototype.version = function()
{
return this._versionmap['nodakwaeri'];
};
/**
* Returns the type of the entity passed
*
* @param entity object The object to check the instance type of
*
* @returns string The instance type of the object class
*
* @since 0.0.1
*/
nk.prototype.type = function( entity )
{
// The working code below yields faster performance than the following snippet by almost 1.5 - 2 times the amount
// ( http://stackoverflow.com/a/12022491 ):
//
// return ( o === null ) ? String( o ) : _type[ toString.call( o ) ] || "object";
if( entity === null )
{
return String( entity );
}
else
{
return this._classmap[ toString.call( entity ) ] || "unknown";
}
};
/**
* Extends an object with another, using provided values for missing keys in object a
*
* @param a object Object being extended
* @param b object Object which keys and values are taken to extend another object
* @param def bool Determines if (true)object will be added to or (false)extended as an object in the case that (a) and (b) do not match in type
*
* @returns object a, extended by b
*/
nk.prototype.extend = function( a, b, def )
{ // Default to true
if( !def && def !== false || def === true )
{
def = true;
}
else
{
def = false;
}
var tA = this.type( a ), tB = this.type( b );
if( tA === tB )
{ // Both types are alike
if( tA === 'array' )
{ // And both arrays
var alength = a.length + 1;
for( var i = 0; i < b.length; i++ )
{
a[alength] = b[i];
alength++;
}
}
else
{ // And both objects
for( var property in b )
{
if( !a.hasOwnProperty( property ) )
{
a[property] = b[property];
}
}
}
}
else
{ // Otherwise
if( tA === 'array' )
{ // We're extending an array with an object
if( !def )
{
for( var property in b )
{
if( IsNumeric( property ) )
{
if( !a[property] )
{
a[property] = b[property];
}
}
}
}else
{
var alength = a.length + 1;
for( var property in b )
{
a[alength] = b[property];
alength++;
}
}
}
else
{ // We're extending an object with an array
for( var i = 0; i < b.length; i++ )
{
if( !a.hasOwnProperty( i ) )
{
a[i] = b[i]
}
}
}
}
// Regardless of how we got here, return a
return a;
};
/**
* Executes a supplied method for each member of a supplied object or array
*
* @param o object An array or object to be iterated over
* @param f function A function to execute in each iteration
*
* @returns {Boolean} True if processed, false if invalid arguments supplied
*/
nk.prototype.each = function( o, f )
{
// Ensure the second argument is a function
if( this.type( f ) === "function" )
{ // Get the number of arguments expected
var nargs = f.length;
// Determine the type of the supplied object
switch( this.type( o ) )
{
case "array":
{
// Iterate over the array
for( var i = 0; i < o.length; i++ )
{
// Provide the index as the key to the provided function
if( nargs > 1 )
{ // Send the key and value
f( i, o[i] );
}
else if( nargs === 1 )
{ // Send only the value
f( o[i] );
}
else
{
f();
}
}
}break;
case "object":
{
// Iterate over the objects properties
for( var p in o )
{
// Supply the property name as the key to the provided function
if( nargs > 1 )
{ // Send the key and value
f( p, o[p] );
}
else if( nargs === 1 )
{ // Send only the value
f( o[p] );
}
else
{
f();
}
}
}break;
default:
{
// First argument invalid, must be Associative Object or an Array
return false;
}break;
}
// Everything went well
return true;
}
// Second argument invalid, must be a function that takes 2 arguments
return false;
};
/**
* Hashes a given password using the supplied salt
*
* @param args Object Defines the arguments for the method
* @var data String Defines the string to be hashed
* @var salt String Defines a 128, 192, or 256 bit(s) string to derive a salt from
* @var iterations Int ( Optional ) Defines the number of iterations; Key stretching
* @var keySize Int Controls how the method will interpret the salt argument
*
* @returns String() Securely hashed data.
*
* @since 0.2.0
*/
nk.prototype.hash = function( args )
{
var na = this,
def = { data: 'password', salt: false, iterations: 100, keySize: 256 },
o = this.extend( args || {}, def );
// Courtesy of: https://jswebcrypto.azurewebsites.net/demo.html#/pbkdf2
var hmacSHA256 = function( key )
{
var hasher = new na.sjcl.misc.hmac( key, na.sjcl.hash.sha256 );
this.encrypt = function()
{
return hasher.encrypt.apply( hasher, arguments );
};
};
if( !o.salt )
{
o.salt = "fff9a94e51b5aa416ff4c42a2e3c754447f3c78cb4f7da9226a63672e15bf00e";
}
var salt = this.sjcl.codec.hex.toBits( o.salt ),
derived = this.sjcl.misc.pbkdf2( o.data, salt, o.iterations, o.keySize, hmacSHA256 );
return this.sjcl.codec.hex.fromBits( derived );
};
/**
* Starts the application
*
* @param o
*/
nk.prototype.init = function( o )
{
var na = this,
nnk = new nk(),
appType = this.config.appType;
// Set the defaults for those who wish to use NodaKwaeri to its
// fullest, and invoke this method supplying only the necessary paths
var defaults =
{
sessn: 'nk',
routr: 'nk',
ctrlr: 'nk',
dbdvr: 'nk',
modl: 'nk',
vew: 'nk'
};
o = this.extend( o || {}, defaults );
// Make sure the paths were provided, they have to be because of the fact that __dirname in this
// file will not point to the proper directories for the application being created using this
// framework
if( this.admin )
{
if( !this.config.admin.controller_path || !this.config.admin.model_path || !this.config.admin.view_path || !this.config.admin.asset_path )
{
console.error( 'Administrative MVC and Asset paths must be set for administrative application');
throw( 'MVC and Asset paths must be set.' );
}
}
else
{
if( !this.config.app.controller_path || !this.config.app.model_path || !this.config.app.view_path || !this.config.app.asset_path )
{
console.error( 'MVC and Asset paths must be set.' );
throw ( 'MVC and Asset paths must be set.' );
}
}
// If a custom session provider wasn't specified, deploy the built in session provider
if( o.sessn === 'nk' )
{
o.sessn = this.session;
console.log( 'Loading nk.session' );
}
// If a custom router wasn't provided, deploy the built in router
if( o.routr === 'nk' )
{
o.routr = this.router;
console.log( 'Loading nk.router' );
}
// If a custom database provider wasn't provided, deploy the built in driver
if( o.dbdvr === 'nk' )
{
o.dbdvr = require( 'nk-mysql' );
console.log( 'Loading nk-mysql' );
}
// If a custom model provider wasn't provided, deploy the built in facility
if( o.modl === 'nk' )
{
o.modl = new this.model( { dbdvr: o.dbdvr, database: this.config.database } );
console.log( 'Loading nk.model' );
}
// If a custom renderer wasn't provided, add renderer_tools to the configuration
if( o.vew === 'nk' )
{
// If custom rendering_tools was not provided in the configuration, add it
if( !o.hasOwnProperty( 'rtools' ) )
{
o.rtools = nnk;
console.log( 'Loading nk.html' );
}
else
{
// Otherwise make sure it's an object, and then extend it to make sure all ends are covered in case of missing components
if( this.type( o.rtools ) !== ( 'object' ) )
{
o.rtools = nnk;
console.log( 'Replacing custom rtools with nk.html' );
}
else
{
o.rtools = this.extend( o.rtools, new nnk.html( nnk ) );
console.log( 'Loading nk.html' );
}
}
// Now instantiate the renderer passing in the configuration
o.vew = new this.renderer( o.rtools, this.config[appType].view_path );
}
// If a custom controller wasn't provided, deploy the built in controller initialized with the supplied controller path
if( o.ctrlr === 'nk' )
{
o.ctrlr = new this.controller( { dpath: this.config[appType].controller_path, modl: o.modl, vew: o.vew } );
console.log( 'Loading nk.controller' );
// we can also delete the model_provider variable now as we won't need it
delete o.modl;
delete o.vew
}
// Let's update the configuration to handle dependencies further along the chain
config = this.extend( this.config, o );
// Initialize our server with the updated configuration
var server = new this.server( config );
server.start();
};
nk.prototype.sjcl = require( './library/sjcl/sjcl' );
nk.prototype.server = require( "./library/server" );
nk.prototype.session = require( "./library/session" );
nk.prototype.router = require( "./library/router" );
nk.prototype.controller = require( "./library/controller" );
nk.prototype.model = require( "./library/model" );
nk.prototype.renderer = require( "./library/renderer" );
nk.prototype.html = require( "./library/html" );
// Export
module.exports = exports = nk;