Click here to Skip to main content
15,893,594 members
Articles / Web Development / Node.js

Node.Js And Stuff

Rate me:
Please Sign up or sign in to vote.
4.97/5 (55 votes)
11 Feb 2013CPOL23 min read 360.6K   2.3K   172  
Small demo app using Node.Js/Socket.IO/MongoDB/D3.Js and jQuery.
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var fs = require('fs')
  , socket = require('../lib/io')
  , uglify = require('uglify-js')
  , activeXObfuscator = require('active-x-obfuscator');

/**
 * License headers.
 *
 * @api private
 */

var template = '/*! Socket.IO.%ext% build:' + socket.version + ', %type%. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */\n'
  , development = template.replace('%type%', 'development').replace('%ext%', 'js')
  , production = template.replace('%type%', 'production').replace('%ext%', 'min.js');

/**
 * If statements, these allows you to create serveride & client side compatible
 * code using specially designed `if` statements that remove serverside
 * designed code from the source files
 *
 * @api private
 */

var starttagIF = '// if node'
  , endtagIF = '// end node';

/**
 * The modules that are required to create a base build of Socket.IO.
 *
 * @const
 * @type {Array}
 * @api private
 */

var base = [
    'io.js'
  , 'util.js'
  , 'events.js'
  , 'json.js'
  , 'parser.js'
  , 'transport.js'
  , 'socket.js'
  , 'namespace.js'
  ];

/**
 * The available transports for Socket.IO. These are mapped as:
 * 
 *   - `key` the name of the transport
 *   - `value` the dependencies for the transport
 *
 * @const
 * @type {Object}
 * @api public
 */

var baseTransports = {
    'websocket': ['transports/websocket.js']
  , 'flashsocket': [
        'transports/websocket.js'
      , 'transports/flashsocket.js'
      , 'vendor/web-socket-js/swfobject.js'
      , 'vendor/web-socket-js/web_socket.js'
    ] 
  , 'htmlfile': ['transports/xhr.js', 'transports/htmlfile.js']
  /* FIXME: re-enable me once we have multi-part support
  , 'xhr-multipart': ['transports/xhr.js', 'transports/xhr-multipart.js'] */
  , 'xhr-polling': ['transports/xhr.js', 'transports/xhr-polling.js']
  , 'jsonp-polling': [
        'transports/xhr.js'
      , 'transports/xhr-polling.js'
      , 'transports/jsonp-polling.js'
    ]
};

/**
 * Wrappers for client-side usage.
 * This enables usage in top-level browser window, client-side CommonJS systems and AMD loaders.
 * If doing a node build for server-side client, this wrapper is NOT included.
 * @api private
 */
var wrapperPre = "\nvar io = ('undefined' === typeof module ? {} : module.exports);\n(function() {\n";

var wrapperPost = "\nif (typeof define === \"function\" && define.amd) {" +
                  "\n  define([], function () { return io; });" +
                  "\n}\n})();";


/**
 * Builds a custom Socket.IO distribution based on the transports that you
 * need. You can configure the build to create development build or production
 * build (minified).
 *
 * @param {Array} transports The transports that needs to be bundled.
 * @param {Object} [options] Options to configure the building process.
 * @param {Function} callback Last argument should always be the callback
 * @callback {String|Boolean} err An optional argument, if it exists than an error
 *    occurred during the build process.
 * @callback {String} result The result of the build process.
 * @api public
 */

var builder = module.exports = function () {
  var transports, options, callback, error = null
    , args = Array.prototype.slice.call(arguments, 0)
    , settings = {
        minify: true
      , node: false
      , custom: []
      };

  // Fancy pancy argument support this makes any pattern possible mainly
  // because we require only one of each type
  args.forEach(function (arg) {
    var type = Object.prototype.toString.call(arg)
        .replace(/\[object\s(\w+)\]/gi , '$1' ).toLowerCase();

    switch (type) {
      case 'array':
        return transports = arg;
      case 'object':
        return options = arg;
      case 'function':
        return callback = arg;
    }
  });

  // Add defaults
  options = options || {};
  transports = transports || Object.keys(baseTransports);

  // Merge the data
  for(var option in options) {
    settings[option] = options[option];
  }

  // Start creating a dependencies chain with all the required files for the
  // custom Socket.IO bundle.
  var files = [];
  base.forEach(function (file) {
    files.push(__dirname + '/../lib/' + file);
  });

  transports.forEach(function (transport) {
    var dependencies = baseTransports[transport];
    if (!dependencies) {
      error = 'Unsupported transport `' + transport + '` supplied as argument.';
      return;
    }

    // Add the files to the files list, but only if they are not added before
    dependencies.forEach(function (file) {
      var path = __dirname + '/../lib/' + file;
      if (!~files.indexOf(path)) files.push(path);
    })
  });

  // check to see if the files tree compilation generated any errors.
  if (error) return callback(error);

  var results = {};
  files.forEach(function (file) {
    fs.readFile(file, function (err, content) {
      if (err) error = err;
      results[file] = content;

      // check if we are done yet, or not.. Just by checking the size of the result
      // object.
      if (Object.keys(results).length !== files.length) return;

      // we are done, did we error?
      if (error) return callback(error);

      // start with the license header
      var code = development
        , ignore = 0;

      // pre-wrapper for non-server-side builds
      if (!settings.node) code += wrapperPre;

      // concatenate the file contents in order
      files.forEach(function (file) {
        code += results[file];
      });

      // check if we need to add custom code
      if (settings.custom.length) {
        settings.custom.forEach(function (content) {
          code += content;
        });
      }

      // post-wrapper for non-server-side builds
      if (!settings.node) {
          code += wrapperPost;
      }

      code = activeXObfuscator(code);

      // Search for conditional code blocks that need to be removed as they
      // where designed for a server side env. but only if we don't want to
      // make this build node compatible.
      if (!settings.node) {
        code = code.split('\n').filter(function (line) {
          // check if there are tags in here
          var start = line.indexOf(starttagIF) >= 0
            , end = line.indexOf(endtagIF) >= 0
            , ret = ignore;

          // ignore the current line
          if (start) {
            ignore++;
            ret = ignore;
          }

          // stop ignoring the next line
          if (end) {
            ignore--;
          }

          return ret == 0;
        }).join('\n');
      }

      // check if we need to process it any further
      if (settings.minify) {
        var ast = uglify.parser.parse(code);
        ast = uglify.uglify.ast_mangle(ast);
        ast = uglify.uglify.ast_squeeze(ast);

        code = production + uglify.uglify.gen_code(ast, { ascii_only: true });
      }

      callback(error, code);
    })
  })
};

/**
 * Builder version is also the current client version
 * this way we don't have to do another include for the
 * clients version number and we can just include the builder.
 *
 * @type {String}
 * @api public
 */
 
builder.version = socket.version;

/**
 * A list of all build in transport types.
 *
 * @type {Object}
 * @api public
 */
 
builder.transports = baseTransports;

/**
 * Command line support, this allows us to generate builds without having
 * to load it as module.
 */
 
if (!module.parent){
  // the first 2 are `node` and the path to this file, we don't need them
  var args = process.argv.slice(2);

  // build a development build
  builder(args.length ? args : false, { minify:false }, function (err, content) {
    if (err) return console.error(err);

    fs.write(
        fs.openSync(__dirname + '/../dist/socket.io.js', 'w')
      , content
      , 0
      , 'utf8'
    );
    console.log('Successfully generated the development build: socket.io.js');
  });

  // and build a production build
  builder(args.length ? args : false, function (err, content) {
    if (err) return console.error(err);
 
    fs.write(
        fs.openSync(__dirname + '/../dist/socket.io.min.js', 'w')
      , content
      , 0
      , 'utf8'
    );
    console.log('Successfully generated the production build: socket.io.min.js');
  });
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions