Source: core/objectid.js

/*! objectid.js */

/**
 * This generates MongoDB-style ObjectID.
 *
 * @class ObjectID
 * @see http://docs.mongodb.org/manual/reference/object-id/
 * @example
 * var ObjectID = KagoDB.bundle.objectid;
 *
 * var id = new ObjectID(); // => such as '51b96d2a845cca7213000002'
 */

module.exports = ObjectID;

function ObjectID(source) {
  if (!(this instanceof ObjectID)) return new ObjectID(source);

  // http://docs.mongodb.org/manual/reference/mongodb-extended-json/#data_oid
  var str;
  if (source instanceof ObjectID) {
    str = source.$oid;
  } else if (typeof(source) == 'object' && source && source.$oid) {
    str = source.$oid;
  } else if (typeof(source) == 'string') {
    str = source;
  }
  if (!str) {
    this.$oid = getDefaultStyle(source);
  } else if (str.length == 24) {
    this.$oid = str;
  } else if (str.length == 12) {
    this.$oid = parseOctets(str);
  } else if (str.length > 0) {
    throw new Error('Invalid source ObjectID: ' + source);
  }
}

ObjectID.prototype = {
  // objectid_test.js uses this undocumented property.
  // get generationTime() { // getter
  getGenerationTime: function() {
    return parseInt(this.$oid.substr(0, 8), 16);
  },

  // set generationTime(timestamp) { // setter
  setGenerationTime: function(timestamp) {
    var ts = ('00000000' + (timestamp.toString(16))).substr(-8, 8);
    var rest = this.$oid.substr(8);
    var list = [ts, rest];
    this.$oid = list.join('');
  },

  toHexString: function() {
    return this.$oid;
  },

  // http://mongodb.github.com/node-mongodb-native/api-bson-generated/objectid.html#gettimestamp
  getTimestamp: function() {
    var time = parseInt(this.$oid.substr(0, 8), 16);
    return new Date(time * 1000);
  },

  equals: function(test) {
    return this + '' == test;
  },

  toOctets: function() {
    var str = this.toHexString();
    var tmp = [];
    var len = str.length;
    for (var i = 0; i < len; i += 2) {
      var hex = str.substr(i, 2);
      var code = parseInt(hex, 16);
      tmp.push(code);
    }
    return String.fromCharCode.apply(null, tmp);
  }
};

ObjectID.prototype.valueOf = ObjectID.prototype.toHexString;
ObjectID.prototype.toString = ObjectID.prototype.toHexString;
ObjectID.prototype.toJSON = ObjectID.prototype.toHexString;

// http://mongodb.github.com/node-mongodb-native/api-bson-generated/objectid.html#objectid-createfromtime
ObjectID.createFromTime = function(time) {
  time = Math.floor(time) || 0;
  var ts = ('00000000' + (time.toString(16))).substr(-8, 8);
  var mac = '000000';
  var pid = '0000';
  var seq = '000000';
  var list = [ts, mac, pid, seq];
  var hex = list.join('');
  return new ObjectID(hex);
};

// http://mongodb.github.com/node-mongodb-native/api-bson-generated/objectid.html#objectid-createfromhexstring
ObjectID.createFromHexString = function(source) {
  return new ObjectID(source);
};

// 12 octets == Latin-1 12 bytes != UTF-8 12 bytes

function parseOctets(source) {
  var tmp = [];
  var len = source.length;
  for (var i = 0; i < len; i++) {
    var chr = source.charCodeAt(i);
    var hex = chr.toString(16);
    if (hex.length == 1) {
      hex = '0' + hex;
    }
    tmp.push(hex);
  }
  return tmp.join('');
}

function getDefaultStyle(source) {
  var date = (source instanceof Date) ? source : new Date();
  var timestamp = Math.floor(date.getTime() / 1000);
  var machineId = ObjectID._machineId || (ObjectID._machineId = Math.floor(Math.random() * 0x1000000));
  var processId = ObjectID._processId || (ObjectID._processId = Math.floor(Math.random() * 0x10000));
  if (!ObjectID._globalSeq) ObjectID._globalSeq = 0;
  var globalSeq = ++ObjectID._globalSeq;
  var ts = ('00000000' + (timestamp.toString(16))).substr(-8, 8);
  var mac = ('000000' + (machineId.toString(16))).substr(-6, 6);
  var pid = ('0000' + (processId.toString(16))).substr(-4, 4);
  var seq = ('000000' + (globalSeq.toString(16))).substr(-6, 6);
  var list = [ts, mac, pid, seq];
  return list.join('');
}