Activities = new Mongo.Collection('activities');

Activities.allow({
  insert: function(userId, doc) {
    return doc.userId === userId;
  }
});

Activities.latest = function() {
  return Activities.find({}, {sort: {date: -1}, limit: 1});
}

Meteor.methods({
  createActivity: function(activity, tweet, loc) {
    check(Meteor.userId(), String);
    check(activity, {
      recipeName: String,
      text: String,
      image: String
    });
    check(tweet, Boolean);
    check(loc, Match.OneOf(Object, null));

    activity.userId = Meteor.userId();
    activity.userAvatar = Meteor.user().services.twitter.profile_image_url_https;
    activity.userName = Meteor.user().profile.name;
    activity.date = new Date;

    if (! this.isSimulation && loc)
      activity.place = getLocationPlace(loc);

    var id = Activities.insert(activity);

    if (! this.isSimulation && tweet)
      tweetActivity(activity);

    return id;
  }
});

if (Meteor.isServer) {
  var twitterOauth = function(options) {
    var config = Meteor.settings.twitter
    var userConfig = Meteor.user().services.twitter;

    return {
      consumer_key: config.consumerKey,
      consumer_secret: config.secret,
      token: userConfig.accessToken,
      token_secret: userConfig.accessTokenSecret
    };
  }

  var tweetActivity = function(activity) {
    // creates the tweet text, optionally truncating to fit the appended text
    function appendTweet(text, append) {
      var MAX = 117; // Max size of tweet with image attached

      if ((text + append).length > MAX)
        return text.substring(0, (MAX - append.length - 3)) + '...' + append;
      else
        return text + append;
    }

    // we need to strip the "data:image/jpeg;base64," bit off the data url
    var image = activity.image.replace(/^data.*base64,/, '');

    var response = HTTP.post(
      'https://upload.twitter.com/1.1/media/upload.json', {
        params: { media: image },
        npmRequestOptions: { oauth: twitterOauth() }
      }
    );

    if (response.statusCode !== 200)
      throw new Meteor.Error(500, 'Unable to post image to twitter');

    if (! response.data)
      throw new Meteor.Error(500, 'Did not receive attachment from twitter');

    var attachment = response.data;

    response = HTTP.post(
      'https://api.twitter.com/1.1/statuses/update.json', {
        params: {
          status: appendTweet(activity.text, ' #localmarket'),
          media_ids: attachment.media_id_string
        },
        npmRequestOptions: { oauth: twitterOauth() }
      }
    );

    if (response.statusCode !== 200)
      throw new Meteor.Error(500, 'Unable to create tweet');
  }

  var getLocationPlace = function(loc) {
    var url = 'https://api.twitter.com/1.1/geo/reverse_geocode.json'
      + '?granularity=neighborhood'
      + '&max_results=1'
      + '&accuracy=' + loc.coords.accuracy
      + '&lat=' + loc.coords.latitude
      + '&long=' + loc.coords.longitude;

    var response = HTTP.get(url,
                            {npmRequestOptions: { oauth: twitterOauth() } });

    if (response.statusCode === 200 && response.data) {
      var place = _.find(response.data.result.places, function(place) {
        return place.place_type === 'neighborhood';
      });

      return place && place.full_name;
    }
  }
}

// Initialize a seed activity
Meteor.startup(function() {
  if (Meteor.isServer && Activities.find().count() === 0) {
    Activities.insert({
      recipeName: 'summer-apricots-honey-panna-cotta',
      text: 'I substituted strawberries for apricots - incredible!',
      image: '/img/activity/activity-placeholder-strawberry-640x640.jpg',
      userAvatar: 'https://avatars3.githubusercontent.com/u/204768?v=2&s=400',
      userName: 'Matt Debergalis',
      place: 'SoMA, San Francisco',
      date: new Date
    });
  }
});


var versionRecords = this._contentQuery(

"SELECT content FROM versions WHERE packageName=?", [name]);

var pvParse = _.memoize(VersionParser.parse);

# github

An implementation of the GitHub OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link


var _ = require('underscore');

var bundler = require('../isobuild/bundler.js');
import Builder from '../isobuild/builder.js';
var compiler = require('../isobuild/compiler.js');
var isopackCacheModule = require('../isobuild/isopack-cache.js');

var buildmessage = require('../utils/buildmessage.js');
var files = require('../fs/files.js');
var config = require('../meteor-services/config.js');
var watch = require('../fs/watch.js');
var Console = require('../console/console.js').Console;
var fiberHelpers = require('../utils/fiber-helpers.js');
var packageMapModule = require('../packaging/package-map.js');

// TL;DR: Isopacket is a set of isopacks. Isopackets are used only inside
// meteor-tool.

// An isopacket is a predefined set of isopackages which the meteor command-line
// tool can load into its process. This is how we use the DDP client and many
// other packages inside the tool. The isopackets are listed below in the
// ISOPACKETS constant.
//
// All packages that are in isopackets and all of their transitive dependencies
// must be part of the core Meteor git checkout (not loaded from troposphere).
//
// The requested packages will be loaded together with all of their
// dependencies. If you request to load the same isopacket more than once, you
// will efficiently get the same pre-loaded isopacket. On the other hand, two
// different loaded isopackets contain distinct copies of all of their packages
// copy of all of the packages. The return value is an object that maps package
// name to package exports (that is, it is the Package object from inside the
// sandbox created for the newly loaded packages).
//
// For built releases, all of the isopackets are pre-compiled (as JsImages,
// similar to a plugin or a server program) into the tool.
//
// When run from a checkout, all isopackets are re-compiled early in the startup
// process if any of their sources have changed.
//
// Example usage:
//   var DDP = require('./isopackets.js').load('ddp')['ddp-client'].DDP;
//   var reverse = DDP.connect('reverse.meteor.com');
//   Console.info(reverse.call('reverse', 'hello world'));

// All of the defined isopackets. Whenever they are being built, they will be
// built in the order listed here.
var ISOPACKETS = {
  'ddp': ['ddp-client'],
  'mongo': ['npm-mongo'],
  'ejson': ['ejson'],
  'constraint-solver': ['constraint-solver'],
  'cordova-support': ['boilerplate-generator', 'logging', 'webapp-hashing',
                      'xmlbuilder'],
  'logging': ['logging']
};

// Caches isopackets in memory (each isopacket only needs to be loaded
// once).  This is a map from isopacket name to either:
//
//  - The 'Package' dictionary, if the isopacket has already been loaded
//    into memory
//  - null, if the isopacket hasn't been loaded into memory but its on-disk
//    instance is known to be ready
//
// The subtlety here is that when running from a checkout, we don't want to
// accidentally load an isopacket before ensuring that it doesn't need to be
// rebuilt. We used to need to load a "js-analyze" isopacket as part
// of building other isopackets in ensureIsopacketsLoadable which made this
// more important, though we've simplified it now by moving that code into
// the tool itself.
var loadedIsopackets = {};

// The main entry point: loads and returns an isopacket from cache or from
// disk. Does not do a build step: ensureIsopacketsLoadable must be called
// first!
var load = function (isopacketName) {
  return fiberHelpers.noYieldsAllowed(function () {
    if (_.has(loadedIsopackets, isopacketName)) {
      if (loadedIsopackets[isopacketName]) {
        return loadedIsopackets[isopacketName];
      }

      // This is the case where the isopacket is up to date on disk but not
      // loaded.
      var isopacket = loadIsopacketFromDisk(isopacketName);
      loadedIsopackets[isopacketName] = isopacket;
      return isopacket;
    }

    if (_.has(ISOPACKETS, isopacketName)) {
      throw Error("Can't load isopacket before it has been verified: "
                  + isopacketName);
    }

    throw Error("Unknown isopacket: " + isopacketName);
  });
};

var isopacketPath = function (isopacketName) {
  return files.pathJoin(config.getIsopacketRoot(), isopacketName);
};

// ensureIsopacketsLoadable is called at startup and ensures that all isopackets
// exist on disk as up-to-date loadable programs.
var calledEnsure = false;
var ensureIsopacketsLoadable = function () {
  if (calledEnsure) {
    throw Error("can't ensureIsopacketsLoadable twice!");
  }
  calledEnsure = true;

  // If we're not running from checkout, then there's nothing to build and we
  // can declare that all isopackets are loadable.
  if (! files.inCheckout()) {
    _.each(ISOPACKETS, function (packages, name) {
      loadedIsopackets[name] = null;
    });
    return;
  }

  // We make this object lazily later.
  var isopacketBuildContext = null;

  var failedPackageBuild = false;
  // Look at each isopacket. Check to see if it's on disk and up to date. If
  // not, build it. We rebuild them in the order listed in ISOPACKETS.
  var messages = Console.withProgressDisplayVisible(function () {
    return buildmessage.capture(function () {
      _.each(ISOPACKETS, function (packages, isopacketName) {
        if (failedPackageBuild) {
          return;
        }

        var isopacketRoot = isopacketPath(isopacketName);
        var existingBuildinfo = files.readJSONOrNull(
          files.pathJoin(isopacketRoot, 'isopacket-buildinfo.json'));
        var needRebuild = ! existingBuildinfo;
        if (! needRebuild && existingBuildinfo.builtBy !== compiler.BUILT_BY) {
          needRebuild = true;
        }
        if (! needRebuild) {
          var watchSet = watch.WatchSet.fromJSON(existingBuildinfo.watchSet);
          if (! watch.isUpToDate(watchSet)) {
            needRebuild = true;
          }
        }
        if (! needRebuild) {
          // Great, it's loadable without a rebuild.
          loadedIsopackets[isopacketName] = null;
          return;
        }

        // We're going to need to build! Make a catalog and loader if we haven't
        // yet.
        if (! isopacketBuildContext) {
          isopacketBuildContext = makeIsopacketBuildContext();
        }

        buildmessage.enterJob({
          title: "bundling " + isopacketName + " packages for the tool"
        }, function () {
          // Build the packages into the in-memory IsopackCache.
          isopacketBuildContext.isopackCache.buildLocalPackages(packages);
          if (buildmessage.jobHasMessages()) {
            return;
          }

          // Now bundle them into a program.
          var built = bundler.buildJsImage({
            name: "isopacket-" + isopacketName,
            packageMap: isopacketBuildContext.packageMap,
            isopackCache: isopacketBuildContext.isopackCache,
            use: packages
          });
          if (buildmessage.jobHasMessages()) {
            return;
          }

          var builder = new Builder({outputPath: isopacketRoot});
          builder.writeJson('isopacket-buildinfo.json', {
            builtBy: compiler.BUILT_BY,
            watchSet: built.watchSet.toJSON()
          });
          built.image.write(builder);
          builder.complete();
          // It's loadable now.
          loadedIsopackets[isopacketName] = null;
        });
      });
    });
  });

  // This is a build step ... but it's one that only happens in development, so
  // it can just crash the app instead of being handled nicely.
  if (messages.hasMessages()) {
    Console.error("Errors prevented isopacket build:");
    Console.printMessages(messages);
    throw new Error("isopacket build failed?");
  }
};

// Returns a new all-local-packages catalog to be used for building isopackets.
var newIsopacketBuildingCatalog = function () {
  if (! files.inCheckout()) {
    throw Error("No need to build isopackets unless in checkout!");
  }

  var catalogLocal = require('../packaging/catalog/catalog-local.js');
  var isopacketCatalog = new catalogLocal.LocalCatalog;
  var messages = buildmessage.capture(
    { title: "scanning local core packages" },
    function () {
      // When running from a checkout, isopacket building does use local
      // packages, but *ONLY THOSE FROM THE CHECKOUT*: not app packages or
      // $PACKAGE_DIRS packages.  One side effect of this: we really really
      // expect them to all build, and we're fine with dying if they don't
      // (there's no worries about needing to springboard).
      isopacketCatalog.initialize({
        localPackageSearchDirs: [files.pathJoin(
          files.getCurrentToolsDir(), 'packages')],
        buildingIsopackets: true
      });
    });
  if (messages.hasMessages()) {
    Console.arrowError("Errors while scanning core packages:");
    Console.printMessages(messages);
    throw new Error("isopacket scan failed?");
  }
  return isopacketCatalog;
};

var makeIsopacketBuildContext = function () {
  var context = {};
  var catalog = newIsopacketBuildingCatalog();
  var versions = {};
  _.each(catalog.getAllPackageNames(), function (packageName) {
    versions[packageName] = catalog.getLatestVersion(packageName).version;
  });
  context.packageMap = new packageMapModule.PackageMap(versions, {
    localCatalog: catalog
  });
  // Make an isopack cache that doesn't save isopacks to disk and has no
  // access to versioned packages.
  context.isopackCache = new isopackCacheModule.IsopackCache({
    packageMap: context.packageMap,
    includeCordovaUnibuild: false,
    // When linking JS files, don't include the padding spaces and line number
    // comments. Since isopackets are loaded as part of possibly very short
    // 'meteor' tool command invocations, we care more about startup time than
    // legibility, and the difference is actually observable (eg 25% speedup
    // loading constraint-solver).
    noLineNumbers: true
  });
  return context;
};

// Loads a built isopacket from disk. Always loads (the cache is in 'load', not
// this function). Does not run a build process; it must already be built.
var loadIsopacketFromDisk = function (isopacketName) {
  var image = bundler.readJsImage(
    files.pathJoin(isopacketPath(isopacketName), 'program.json'));

  // An incredibly minimalist version of the environment from
  // tools/server/boot.js.  Kind of a hack.
  var env = {
    __meteor_bootstrap__: { startupHooks: [] },
    __meteor_runtime_config__: { meteorRelease: "ISOPACKET" }
  };

  var ret;
  var messages = buildmessage.capture({
    title: "loading isopacket `" + isopacketName + "`"
  }, function () {
    ret = image.load(env);
  });

  // This is a build step ... but it's one that only happens in development, so
  // it can just crash the app instead of being handled nicely.
  if (messages.hasMessages()) {
    Console.error("Errors prevented isopacket load:");
    Console.printMessages(messages);
    throw new Error("isopacket load failed?");
  }

  // Run any user startup hooks.
  while (env.__meteor_bootstrap__.startupHooks.length) {
    var hook = env.__meteor_bootstrap__.startupHooks.shift();
    hook();
  }
  // Setting this to null tells Meteor.startup to call hooks immediately.
  env.__meteor_bootstrap__.startupHooks = null;

  return ret;
};

var isopackets = exports;
_.extend(exports, {
  load: load,
  ensureIsopacketsLoadable: ensureIsopacketsLoadable,
  ISOPACKETS: ISOPACKETS,
  makeIsopacketBuildContext: makeIsopacketBuildContext
});


This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.

You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.


PackageGlobalVar = {};
PermittedGlobal = {};
Package.minimongo.accessPackageVarInMeteor = "is OK";


var Console = require('../console/console.js').Console;

var fiberHelpers = require('../utils/fiber-helpers.js');

var packageMapModule = require('../packaging/package-map.js');

// TL;DR: Isopacket is a set of isopacks. Isopackets are used only inside

var Profile = require('./profile.js').Profile;

var _ = require('underscore');
var sqlite3 = require('sqlite3');

var files = require('../../fs/files.js');
var utils = require('../../utils/utils.js');
var buildmessage = require('../../utils/buildmessage.js');
var config = require('../../meteor-services/config.js');
var archinfo = require('../../utils/archinfo.js');
var Console = require('../../console/console.js').Console;

var tropohouse = require('../tropohouse.js');
var packageClient = require('../package-client.js');
var VersionParser = require('../package-version-parser.js');

// XXX: Rationalize these flags.  Maybe use the logger?
var DEBUG_SQL = !!process.env.METEOR_DEBUG_SQL;

var SYNCTOKEN_ID = "1";

var METADATA_LAST_SYNC = "lastsync";

var BUSY_RETRY_ATTEMPTS = 10;
var BUSY_RETRY_INTERVAL = 1000;

var Mutex = function () {
  var self = this;

  self._locked = false;

  self._resolvers = [];
};

_.extend(Mutex.prototype, {
  lock: function () {
    var self = this;

    while (true) {
      if (!self._locked) {
        self._locked = true;
        return;
      }

      new Promise(function (resolve) {
        self._resolvers.push(resolve);
      }).await();
    }
  },

  unlock: function () {
    var self = this;

    if (!self._locked) {
      throw new Error("unlock called on unlocked mutex");
    }

    self._locked = false;
    var resolve = self._resolvers.shift();
    if (resolve) {
      resolve();
    }
  }
});

var Txn = function (db) {
  var self = this;
  self.db = db;
  self.closed = false;
  self.committed = false;
  self.started = false;
};

_.extend(Txn.prototype, {
  // Runs a SQL query and returns the rows
  query: function (sql, params) {
    var self = this;
    return self.db._query(sql, params);
  },

  // Runs a SQL statement, returning no rows
  execute: function (sql, params) {
    var self = this;
    return self.db._execute(sql, params);
  },

  // Start a transaction
  begin: function (mode) {
    var self = this;

    // XXX: Use DEFERRED mode?
    mode = mode || "IMMEDIATE";

    if (self.started) {
      throw new Error("Transaction already started");
    }

    self.db._execute("BEGIN " + mode + " TRANSACTION");
    self.started = true;
  },

  // Releases resources from the transaction; Rollback if commit not already called.
  close: function () {
    var self = this;

    if (self.closed) {
      return;
    }

    if (!self.started) {
      return;
    }

    self.db._execute("ROLLBACK TRANSACTION");
    self.committed = false;
    self.closed = true;
  },

  // Commits the transaction.  close() will then be a no-op
  commit: function () {
    var self = this;

    self.db._execute("END TRANSACTION");
    self.committed = true;
    self.closed = true;
  }
});

var Db = function (dbFile, options) {
  var self = this;

  self._dbFile = dbFile;

  self._autoPrepare = true;
  self._prepared = {};

  self._transactionMutex = new Mutex();

  self._db = self._retry(function () {
    return self.open(dbFile);
  });

  // WAL mode copes much better with (multi-process) concurrency
  self._retry(function () {
    self._execute('PRAGMA journal_mode=WAL');
  });
};

_.extend(Db.prototype, {

  // TODO: Move to utils?
  _retry: function (f, options) {
    options = _.extend({ maxAttempts: 3, delay: 500}, options || {});

    for (var attempt = 1; attempt <= options.maxAttempts; attempt++) {
      try {
        return f();
      } catch (err) {
        if (attempt < options.maxAttempts) {
          Console.warn("Retrying after error", err);
        } else {
          throw err;
        }
      }

      if (options.delay) {
        utils.sleepMs(options.delay);
      }
    }
  },

  // Runs functions serially, in a mutex
  _serialize: function (f) {
    var self = this;

    try {
      self._transactionMutex.lock();
      return f();
    } finally {
      self._transactionMutex.unlock();
    }
  },

  // Do not call any other methods on this object after calling this one.
  // This yields.
  closePermanently: function () {
    var self = this;
    self._closePreparedStatements();
    var db = self._db;
    self._db = null;
    Promise.denodeify(db.close).call(db).await();
  },

  // Runs the function inside a transaction block
  runInTransaction: function (action) {
    var self = this;

    var runOnce = function () {
      var txn = new Txn(self);

      var t1 = Date.now();

      var rollback = true;
      var result = null;
      var resultError = null;

      txn.begin();
      try {
        result = action(txn);
        txn.commit();
      } catch (err) {
        resultError = err;
      } finally {
        try {
          txn.close();
        } catch (e) {
          // We don't have a lot of options here...
          Console.warn("Error closing transaction", e);
        }
      }

      //self._closePreparedStatements();

      if (DEBUG_SQL) {
        var t2 = Date.now();
        // XXX: Hack around not having loggers
        Console.info("Transaction took: ", (t2 - t1));
      }

      if (resultError) {
        throw resultError;
      }

      return result;
    };

    for (var attempt = 0; ; attempt++) {
      try {
        return self._serialize(runOnce);
      } catch (err) {
        var retry = false;
        // Grr... doesn't expose error code; must string-match
        if (err.message
            && (   err.message == "SQLITE_BUSY: database is locked"
                || err.message == "SQLITE_BUSY: cannot commit transaction - SQL statements in progress")) {
          if (attempt < BUSY_RETRY_ATTEMPTS) {
            retry = true;
          }
        }
        if (!retry) {
          throw err;
        }
      }

      // Wait on average BUSY_RETRY_INTERVAL, but randomize to avoid thundering herd
      var t = (Math.random() + 0.5) * BUSY_RETRY_INTERVAL;
      utils.sleepMs(t);
    }
  },

  open: function (dbFile) {
    var self = this;

    if ( !files.exists(files.pathDirname(dbFile)) ) {
      Console.debug("Creating database directory", dbFile);

      var folder = files.pathDirname(dbFile);
      if ( !files.mkdir_p(folder) )
        throw new Error("Could not create folder at " + folder);
    }

    Console.debug("Opening db file", dbFile);
    return new sqlite3.Database(files.convertToOSPath(dbFile));
  },

  // Runs a query synchronously, returning all rows
  // Hidden to enforce transaction usage
  _query: function (sql, params) {
    var self = this;

    var prepared = null;
    var prepare = self._autoPrepare && !_.isEmpty(params);
    if (prepare) {
      prepared = self._prepareWithCache(sql);
    }

    if (DEBUG_SQL) {
      var t1 = Date.now();
    }

    //Console.debug("Executing SQL ", sql, params);

    var promise = prepared
      ? Promise.denodeify(prepared.all).call(prepared, params)
      : Promise.denodeify(self._db.all).call(self._db, sql, params);

    var rows = promise.await();

    if (DEBUG_SQL) {
      var t2 = Date.now();
      if ((t2 - t1) > 10) {
        // XXX: Hack around not having log levels
        Console.info("SQL statement ", sql, " took ", (t2 - t1));
      }
    }

    return rows;
  },

  // Runs a query synchronously, returning no rows
  // Hidden to enforce transaction usage
  _execute: function (sql, params) {
    var self = this;

    var prepared = null;
    // We don't prepare non-parametrized statements, because (a) there's not
    // that much of a win from doing so, since we don't tend to run them in bulk
    // and (b) doing so can trigger
    // https://github.com/mapbox/node-sqlite3/pull/355 .  (We can avoid that bug
    // by being careful to pass in an empty array or no argument for params to
    // prepared.run instead of undefined, but we can also just avoid the issue
    // entirely.)
    var prepare = self._autoPrepare && !_.isEmpty(params);
    if (prepare) {
      prepared = self._prepareWithCache(sql);
    }

    if (DEBUG_SQL) {
      var t1 = Date.now();
    }

    //Console.debug("Executing SQL ", sql, params);

    var ret = new Promise(function (resolve, reject) {
      function callback(err) {
        err ? reject(err) : resolve({
          // Yes, lastID & changes are on this(!)
          lastID: this.lastID,
          changes: this.changes
        });
      }

      if (prepared) {
        prepared.run(params, callback);
      } else {
        self._db.run(sql, params, callback);
      }
    }).await();

    if (DEBUG_SQL) {
      var t2 = Date.now();
      if ((t2 - t1) > 10) {
        Console.info("SQL statement ", sql, " took ", (t2 - t1));
      }
    }

    return ret;
  },

  // Prepares the statement, caching the result
  _prepareWithCache: function (sql) {
    var self = this;

    var prepared = self._prepared[sql];
    if (!prepared) {
      //Console.debug("Preparing statement: ", sql);
      new Promise(function (resolve, reject) {
        prepared = self._db.prepare(sql, function (err) {
          err ? reject(err) : resolve();
        });
      }).await();

      self._prepared[sql] = prepared;
    }

    return prepared;
  },

  // Close any cached prepared statements
  _closePreparedStatements: function () {
    var self = this;

    var prepared = self._prepared;
    self._prepared = {};

    _.each(prepared, function (statement) {
      var err = new Promise(function (resolve) {
        // We resolve the promise with an error instead of rejecting it,
        // because we don't want to throw.
        statement.finalize(resolve);
      }).await();

      if (err) {
        Console.warn("Error finalizing statement ", err);
      }
    });
  }

});

var Table = function (name, jsonFields, options) {
  var self = this;
  options = options || {};

  self.name = name;
  self.jsonFields = jsonFields;
  self.noContentColumn = options.noContentColumn;

  self._buildStatements();
};

_.extend(Table.prototype, {
  _buildStatements: function () {
    var self = this;

    var queryParams = self._generateQuestionMarks(
      self.jsonFields.length + (self.noContentColumn ? 0 : 1));
    self._selectQuery = "SELECT * FROM " + self.name + " WHERE _id=?";
    self._insertQuery = "INSERT INTO " + self.name + " VALUES " + queryParams;
    self._deleteQuery = "DELETE FROM " + self.name + " WHERE _id=?";
  },

  // Generate a string of the form (?, ?) where the n is the number of question
  // mark.
  _generateQuestionMarks: function (n) {
    return "(" + _.times(n, function () { return "?" }).join(",") + ")";
  },

  find: function (txn, id) {
    var self = this;
    var rows = txn.query(self._selectQuery, [ id ]);
    if (rows.length !== 0) {
      if (rows.length !== 1) {
        throw new Error("Corrupt database (PK violation)");
      }
      return rows[0];
    }
    return undefined;
  },

  upsert: function (txn, objects) {
    var self = this;

    // XXX: Use sqlite upsert
    // XXX: Speculative insert
    // XXX: Fix transaction logic so we always roll back
    _.each(objects, function (o) {
      var id = o._id;
      var rows = txn.query(self._selectQuery, [ id ]);
      if (rows.length !== 0) {
        var deleteResults = txn.execute(self._deleteQuery, [ id ]);
        if (deleteResults.changes !== 1) {
          throw new Error("Unable to delete row: " + id);
        }
      }
      var row = [];
      _.each(self.jsonFields, function (jsonField) {
        row.push(o[jsonField]);
      });
      if (! self.noContentColumn) {
        row.push(JSON.stringify(o));
      }
      txn.execute(self._insertQuery, row);
    });
  },

  createTable: function (txn) {
    var self = this;

    var sql = 'CREATE TABLE IF NOT EXISTS ' + self.name + '(';
    for (var i = 0; i < self.jsonFields.length; i++) {
      var jsonField = self.jsonFields[i];
      var sqlColumn = jsonField;
      if (i != 0) sql += ", ";
      sql += sqlColumn + " STRING";
      if (sqlColumn === '_id') {
        sql += " PRIMARY KEY";
      }
    }
    if (! self.noContentColumn) {
      sql += ", content STRING";
    }
    sql += ")";
    txn.execute(sql);

    //sql = "CREATE INDEX IF NOT EXISTS idx_" + self.name + "_id ON " + self.name + "(_id)";
    //txn.execute(sql);
  }
});

// A RemoteCatalog is a local cache of the content of troposphere.
// A default instance of this catalog is registered by the layered catalog and is available
// under the variable "official" from the catalog.js
//
// The remote catalog is backed by a db to make things easier on the memory and for faster queries
var RemoteCatalog = function () {
  var self = this;

  // Set this to true if we are not going to connect to the remote package
  // server, and will only use the cached data for our package information
  // This means that the catalog might be out of date on the latest developments.
  self.offline = null;

  self.db = null;
};

_.extend(RemoteCatalog.prototype, {
  toString: function () {
    var self = this;
    return "RemoteCatalog";
  },

  // Used for special cases that want to ensure that all connections to the DB
  // are closed (eg to ensure that all writes have been flushed from the '-wal'
  // file to the main DB file). Most methods on this class will stop working
  // after you call this method. Note that this yields.
  closePermanently: function () {
    var self = this;
    self.db.closePermanently();
    self.db = null;
  },

  getVersion: function (name, version) {
    var result = this._contentQuery(
      "SELECT content FROM versions WHERE packageName=? AND version=?",
      [name, version]);
    if(!result || result.length === 0) {
      return null;
    }
    return result[0];
  },

  // As getVersion, but returns info on the latest version of the
  // package, or null if the package doesn't exist or has no versions.
  getLatestVersion: function (name) {
    var self = this;

    var versions = self.getSortedVersions(name);
    return self.getVersion(name, _.last(versions));
  },

  getSortedVersions: function (name) {
    var self = this;
    var match = this._columnsQuery(
      "SELECT version FROM versions WHERE packageName=?", name);
    if (match === null)
      return [];
    return _.pluck(match, 'version').sort(VersionParser.compare);
  },

  // Just getVersion mapped over getSortedVersions, but only makes one round
  // trip to sqlite.
  getSortedVersionRecords: function (name) {
    var self = this;
    var versionRecords = this._contentQuery(
      "SELECT content FROM versions WHERE packageName=?", [name]);
    if (! versionRecords)
      return [];
    versionRecords.sort(function (a, b) {
      return VersionParser.compare(a.version, b.version);
    });
    return versionRecords;
  },

  getLatestMainlineVersion: function (name) {
    var self = this;
    var versions = self.getSortedVersions(name);
    versions.reverse();
    var latest = _.find(versions, function (version) {
      return !/-/.test(version);
    });
    if (!latest)
      return null;
    return self.getVersion(name, latest);
  },

  getPackage: function (name, options) {
    var result = this._contentQuery(
      "SELECT content FROM packages WHERE name=?", name);
    if (!result || result.length === 0)
      return null;
    if (result.length !== 1) {
      throw new Error("Found multiple packages matching name: " + name);
    }
    return result[0];
  },

  getAllBuilds: function (name, version) {
    var result = this._contentQuery(
      "SELECT content FROM builds WHERE builds.versionId = " +
        "(SELECT _id FROM versions WHERE versions.packageName=? AND " +
        "versions.version=?)",
      [name, version]);
    if (!result || result.length === 0)
      return null;
    return result;
  },

  // If this package has any builds at this version, return an array of builds
  // which cover all of the required arches, or null if it is impossible to
  // cover them all (or if the version does not exist).
  // Note that this method is specific to RemoteCatalog.
  getBuildsForArches: function (name, version, arches) {
    var self = this;

    var solution = null;
    var allBuilds = self.getAllBuilds(name, version) || [];

    utils.generateSubsetsOfIncreasingSize(allBuilds, function (buildSubset) {
      // This build subset works if for all the arches we need, at least one
      // build in the subset satisfies it. It is guaranteed to be minimal,
      // because we look at subsets in increasing order of size.
      var satisfied = _.all(arches, function (neededArch) {
        return _.any(buildSubset, function (build) {
          var buildArches = build.buildArchitectures.split('+');
          return !!archinfo.mostSpecificMatch(neededArch, buildArches);
        });
      });
      if (satisfied) {
        solution = buildSubset;
        return true;  // stop the iteration
      }
    });
    return solution;  // might be null!
  },

  // Returns general (non-version-specific) information about a
  // release track, or null if there is no such release track.
  getReleaseTrack: function (name) {
    var self = this;
    var result = self._contentQuery(
      "SELECT content FROM releaseTracks WHERE name=?", name);
    if (!result || result.length === 0)
      return null;
    return result[0];
  },

  getReleaseVersion: function (track, version) {
    var self = this;
    var result = self._contentQuery(
      "SELECT content FROM releaseVersions WHERE track=? AND version=?",
      [track, version]);
    if (!result || result.length === 0)
      return null;
    return result[0];
  },

  // Used by make-bootstrap-tarballs. Only should be used on catalogs that are
  // specially constructed for bootstrap tarballs.
  forceRecommendRelease: function (track, version) {
    var self = this;
    var releaseVersionData = self.getReleaseVersion(track, version);
    if (!releaseVersionData) {
      throw Error("Can't force-recommend unknown release " + track + "@"
                  + version);
    }
    releaseVersionData.recommended = true;
    self._insertReleaseVersions([releaseVersionData]);
  },

  getAllReleaseTracks: function () {
    return _.pluck(this._columnsQuery("SELECT name FROM releaseTracks"),
                   'name');
  },

  getAllPackageNames: function () {
    return _.pluck(this._columnsQuery("SELECT name FROM packages"), 'name');
  },

  initialize: function (options) {
    var self = this;

    options = options || {};
    // We should to figure out if we are intending to connect to the package server.
    self.offline = options.offline ? options.offline : false;

    var dbFile = options.packageStorage || config.getPackageStorage();
    self.db = new Db(dbFile);

    self.tableVersions = new Table('versions', ['packageName', 'version', '_id']);
    self.tableBuilds = new Table('builds', ['versionId', '_id']);
    self.tableReleaseTracks = new Table('releaseTracks', ['name', '_id']);
    self.tableReleaseVersions = new Table('releaseVersions', ['track', 'version', '_id']);
    self.tablePackages = new Table('packages', ['name', '_id']);
    self.tableSyncToken = new Table('syncToken', ['_id']);
    self.tableMetadata = new Table('metadata', ['_id']);
    self.tableBannersShown = new Table(
      'bannersShown', ['_id', 'lastShown'], { noContentColumn: true });

    self.allTables = [
      self.tableVersions,
      self.tableBuilds,
      self.tableReleaseTracks,
      self.tableReleaseVersions,
      self.tablePackages,
      self.tableSyncToken,
      self.tableMetadata,
      self.tableBannersShown
    ];
    return self.db.runInTransaction(function(txn) {
      _.each(self.allTables, function (table) {
        table.createTable(txn);
      });

      // Extra indexes for the most expensive queries
      // These are non-unique indexes
      // XXX We used to have a versionsNamesIdx here on versions(packageName);
      //     we no longer create it but we don't waste time dropping it either.
      txn.execute("CREATE INDEX IF NOT EXISTS versionsIdx ON " +
                  "versions(packageName, version)");
      txn.execute("CREATE INDEX IF NOT EXISTS buildsVersionsIdx ON " +
                  "builds(versionId)");
      txn.execute("CREATE INDEX IF NOT EXISTS packagesIdx ON " +
                  "packages(name)");
      txn.execute("CREATE INDEX IF NOT EXISTS releaseTracksIdx ON " +
                  "releaseTracks(name)");
      txn.execute("CREATE INDEX IF NOT EXISTS releaseVersionsIdx ON " +
                  "releaseVersions(track, version)");
    });
  },

  // This function empties the DB. This is called from the package-client.
  reset: function () {
    var self = this;
    return self.db.runInTransaction(function (txn) {
      _.each(self.allTables, function (table) {
        txn.execute("DELETE FROM " + table.name);
      });
    });
  },

  refresh: function (options) {
    var self = this;
    options = options || {};

    Console.debug("In remote catalog refresh");

    if (process.env.METEOR_TEST_FAIL_RELEASE_DOWNLOAD === 'offline') {
      var e = new Error;
      e.errorType = 'DDP.ConnectionError';
      throw e;
    }

    if (self.offline)
      return false;

    if (options.maxAge) {
      var lastSync = self.getMetadata(METADATA_LAST_SYNC);
      Console.debug("lastSync = ", lastSync);
      if (lastSync && lastSync.timestamp) {
        if ((Date.now() - lastSync.timestamp) < options.maxAge) {
          Console.debug("Package catalog is sufficiently up-to-date; not updating\n");
          return false;
        }
      }
    }

    var updateResult = {};
    // XXX This buildmessage.enterJob only exists for showing progress.
    buildmessage.enterJob({ title: 'updating package catalog' }, function () {
      updateResult = packageClient.updateServerPackageData(self);
    });

    if (updateResult.resetData) {
      tropohouse.default.wipeAllPackages();
    }

    return true;
  },

  // Given a release track, returns all recommended versions for this track,
  // sorted by their orderKey. Returns the empty array if the release track does
  // not exist or does not have any recommended versions.
  getSortedRecommendedReleaseVersions: function (track, laterThanOrderKey) {
    var self = this;
    var versions =
          self.getSortedRecommendedReleaseRecords(track, laterThanOrderKey);
    return _.pluck(versions, "version");
  },

  // Given a release track, returns all recommended version *records* for this
  // track, sorted by their orderKey. Returns the empty array if the release
  // track does not exist or does not have any recommended versions.
  getSortedRecommendedReleaseRecords: function (track, laterThanOrderKey) {
    var self = this;
    // XXX releaseVersions content objects are kinda big; if we put
    // 'recommended' and 'orderKey' in their own columns this could be faster
    var result = self._contentQuery(
      "SELECT content FROM releaseVersions WHERE track=?", track);

    var recommended = _.filter(result, function (v) {
      if (!v.recommended)
        return false;
      return !laterThanOrderKey || v.orderKey > laterThanOrderKey;
    });

    var recSort = _.sortBy(recommended, function (rec) {
      return rec.orderKey;
    });
    recSort.reverse();
    return recSort;
  },

  // Given a release track, returns all version records for this track.
  getReleaseVersionRecords: function (track) {
    var self = this;
    var result = self._contentQuery(
      "SELECT content FROM releaseVersions WHERE track=?", track);
    return result;
  },

  // For a given track, returns the total number of release versions on that
  // track.
  getNumReleaseVersions: function (track) {
    var self = this;
    var result = self._columnsQuery(
      "SELECT count(*) FROM releaseVersions WHERE track=?", track);
    return result[0]["count(*)"];
  },

  // Returns the default release version on the DEFAULT_TRACK, or for a
  // given release track.
  getDefaultReleaseVersion: function (track) {
    var self = this;
    var versionRecord = self.getDefaultReleaseVersionRecord(track);
    if (! versionRecord)
      throw new Error("Can't get default release version for track " + track);
    return _.pick(versionRecord, ["track", "version" ]);
  },

  // Returns the default release version record for the DEFAULT_TRACK, or for a
  // given release track.
  getDefaultReleaseVersionRecord: function (track) {
    var self = this;

    if (!track)
      track = exports.DEFAULT_TRACK;

    var versions = self.getSortedRecommendedReleaseRecords(track);
    if (!versions.length)
      return null;
    return  versions[0];
  },

  getBuildWithPreciseBuildArchitectures: function (versionRecord, buildArchitectures) {
    var self = this;
    var matchingBuilds = this._contentQuery(
      "SELECT content FROM builds WHERE versionId=?", versionRecord._id);
    return _.findWhere(matchingBuilds, { buildArchitectures: buildArchitectures });
  },

  // Executes a query, returning an array of each content column parsed as JSON
  _contentQuery: function (query, params) {
    var self = this;
    var rows = self._columnsQuery(query, params);
    return _.map(rows, function(entity) {
      return JSON.parse(entity.content);
    });
  },

  // Executes a query, returning an array of maps from column name to data.
  // No JSON parsing is performed.
  _columnsQuery: function (query, params) {
    var self = this;
    var rows = self.db.runInTransaction(function (txn) {
      return txn.query(query, params);
    });
    return rows;
  },

  _insertReleaseVersions: function(releaseVersions) {
    var self = this;
    return self.db.runInTransaction(function (txn) {
      self.tableReleaseVersions.upsert(txn, releaseVersions);
    });
  },

  //Given data from troposphere, add it into the local store
  insertData: function(serverData, syncComplete) {
    var self = this;
    return self.db.runInTransaction(function (txn) {
      self.tablePackages.upsert(txn, serverData.collections.packages);
      self.tableBuilds.upsert(txn, serverData.collections.builds);
      self.tableVersions.upsert(txn, serverData.collections.versions);
      self.tableReleaseTracks.upsert(txn, serverData.collections.releaseTracks);
      self.tableReleaseVersions.upsert(txn, serverData.collections.releaseVersions);

      var syncToken = serverData.syncToken;
      Console.debug("Adding syncToken: ", JSON.stringify(syncToken));
      syncToken._id = SYNCTOKEN_ID; //Add fake _id so it fits the pattern
      self.tableSyncToken.upsert(txn, [syncToken]);

      if (syncComplete) {
        var lastSync = {timestamp: Date.now()};
        self._setMetadata(txn, METADATA_LAST_SYNC, lastSync);
      }
    });
  },

  getSyncToken: function() {
    var self = this;
    var result = self._contentQuery("SELECT content FROM syncToken WHERE _id=?",
                                    [ SYNCTOKEN_ID ]);
    if (!result || result.length === 0) {
      Console.debug("No sync token found");
      return null;
    }
    if (result.length !== 1) {
      throw new Error("Unexpected number of sync tokens found");
    }
    delete result[0]._id;
    Console.debug("Returning sync token: " + JSON.stringify(result[0]));
    return result[0];
  },

  getMetadata: function(key) {
    var self = this;
    var row = self.db.runInTransaction(function (txn) {
      return self.tableMetadata.find(txn, key);
    });
    if (row) {
      return JSON.parse(row['content']);
    }
    return undefined;
  },

  setMetadata: function(key, value) {
    var self = this;
    self.db.runInTransaction(function (txn) {
      self._setMetadata(txn, key, value);
    });
  },

  _setMetadata: function(txn, key, value) {
    var self = this;
    value._id = key;
    self.tableMetadata.upsert(txn, [value]);
  },

  shouldShowBanner: function (releaseName, bannerDate) {
    var self = this;
    var row = self.db.runInTransaction(function (txn) {
      return self.tableBannersShown.find(txn, releaseName);
    });
    // We've never printed a banner for this release.
    if (! row)
      return true;
    try {
      var lastShown = new Date(JSON.parse(row.lastShown));
      return lastShown < bannerDate;
    } catch (e) {
      // Probably an error in JSON.parse or something. Just show the banner.
      return true;
    }
  },

  setBannerShownDate: function (releaseName, bannerShownDate) {
    var self = this;
    self.db.runInTransaction(function (txn) {
      self.tableBannersShown.upsert(txn, [{
        _id: releaseName,
        // XXX For now, there's no way to tell this file to make a non-string
        // column in a sqlite table, but this should probably change to a
        // 'timestamp with time zone' or whatever.
        lastShown: JSON.stringify(bannerShownDate)
      }]);
    });
  }
});

exports.RemoteCatalog = RemoteCatalog;

// We put this constant here because we don't have any better place that would otherwise cause a cycle
exports.DEFAULT_TRACK = 'METEOR';

// The catalog as provided by troposhere (aka atomospherejs.com)
exports.official = new RemoteCatalog();


{{#template name="apiTimers"}}

Timers

Meteor uses global environment variables
to keep track of things like the current request's user.  To make sure
these variables have the right values, you need to use
`Meteor.setTimeout` instead of `setTimeout` and `Meteor.setInterval`
instead of `setInterval`.

These functions work just like their native JavaScript equivalents.
If you call the native function, you'll get an error stating that Meteor
code must always run within a Fiber, and advising to use
`Meteor.bindEnvironment`.

{{> autoApiBox "Meteor.setTimeout"}}

Returns a handle that can be used by `Meteor.clearTimeout`.

{{> autoApiBox "Meteor.setInterval"}}

Returns a handle that can be used by `Meteor.clearInterval`.

{{> autoApiBox "Meteor.clearTimeout"}}
{{> autoApiBox "Meteor.clearInterval"}}
{{/template}}


// Since this file is added with bare: true, this variable will be accessible
// in package scope.
var exportedFromBareFile = "Yes";


/* Pretty printing styles. Used with prettify.js. */
/* Vim sunburst theme by David Leibovic */

pre .str, code .str { color: #65B042; } /* string  - green */
pre .kwd, code .kwd { color: #E28964; } /* keyword - dark pink */
pre .com, code .com { color: #AEAEAE; font-style: italic; } /* comment - gray */
pre .typ, code .typ { color: #89bdff; } /* type - light blue */
pre .lit, code .lit { color: #3387CC; } /* literal - blue */
pre .pun, code .pun { color: #fff; } /* punctuation - white */
pre .pln, code .pln { color: #fff; } /* plaintext - white */
pre .tag, code .tag { color: #89bdff; } /* html/xml tag    - light blue */
pre .atn, code .atn { color: #bdb76b; } /* html/xml attribute name  - khaki */
pre .atv, code .atv { color: #65B042; } /* html/xml attribute value - green */
pre .dec, code .dec { color: #3387CC; } /* decimal - blue */

pre.prettyprint, code.prettyprint {
	background-color: #000;
	-moz-border-radius: 8px;
	-webkit-border-radius: 8px;
	-o-border-radius: 8px;
	-ms-border-radius: 8px;
	-khtml-border-radius: 8px;
	border-radius: 8px;
}

pre.prettyprint {
	width: 95%;
	margin: 1em auto;
	padding: 1em;
	white-space: pre-wrap;
}

/* Specify class=linenums on a pre to get line numbering */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE; } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }

@media print {
  pre .str, code .str { color: #060; }
  pre .kwd, code .kwd { color: #006; font-weight: bold; }
  pre .com, code .com { color: #600; font-style: italic; }
  pre .typ, code .typ { color: #404; font-weight: bold; }
  pre .lit, code .lit { color: #044; }
  pre .pun, code .pun { color: #440; }
  pre .pln, code .pln { color: #000; }
  pre .tag, code .tag { color: #006; font-weight: bold; }
  pre .atn, code .atn { color: #404; }
  pre .atv, code .atv { color: #060; }
}


__meteor_bootstrap__: { startupHooks: [] },

__meteor_runtime_config__: { meteorRelease: "ISOPACKET" }

title: "loading isopacket `" + isopacketName + "`"

var CS = ConstraintSolver;
var PV = PackageVersion;

var pvkey = function (pkg, version) {
  return pkg + " " + version;
};

// Stores the Dependencies for each known PackageAndVersion.
CS.CatalogCache = function () {
  // String(PackageAndVersion) -> String -> Dependency.
  // For example, "foo 1.0.0" -> "bar" -> Dependency.fromString("?bar@1.0.2").
  this._dependencies = {};
  // A map derived from the keys of _dependencies, for ease of iteration.
  // "foo" -> ["1.0.0", ...]
  // Versions in the array are unique but not sorted, unless the `.sorted`
  // property is set on the array.  The array is never empty.
  this._versions = {};
};

CS.CatalogCache.prototype.hasPackageVersion = function (pkg, version) {
  return _.has(this._dependencies, pvkey(pkg, version));
};

CS.CatalogCache.prototype.addPackageVersion = function (p, v, deps) {
  check(p, String);
  check(v, String);
  // `deps` must not have any duplicate values of `.packageConstraint.package`
  check(deps, [CS.Dependency]);

  var key = pvkey(p, v);
  if (_.has(this._dependencies, key)) {
    throw new Error("Already have an entry for " + key);
  }

  if (! _.has(this._versions, p)) {
    this._versions[p] = [];
  }
  this._versions[p].push(v);
  this._versions[p].sorted = false;

  var depsByPackage = {};
  this._dependencies[key] = depsByPackage;
  _.each(deps, function (d) {
    var p2 = d.packageConstraint.package;
    if (_.has(depsByPackage, p2)) {
      throw new Error("Can't have two dependencies on " + p2 +
                      " in " + key);
    }
    depsByPackage[p2] = d;
  });
};

// Returns the dependencies of a (package, version), stored in a map.
// The values are Dependency objects; the key for `d` is
// `d.packageConstraint.package`.  (Don't mutate the map.)
CS.CatalogCache.prototype.getDependencyMap = function (p, v) {
  var key = pvkey(p, v);
  if (! _.has(this._dependencies, key)) {
    throw new Error("No entry for " + key);
  }
  return this._dependencies[key];
};

// Returns an array of version strings, sorted, possibly empty.
// (Don't mutate the result.)
CS.CatalogCache.prototype.getPackageVersions = function (pkg) {
  var result = (_.has(this._versions, pkg) ?
                this._versions[pkg] : []);
  if ((!result.length) || result.sorted) {
    return result;
  } else {
    // sort in place, and record so that we don't sort redundantly
    // (we'll sort again if more versions are pushed onto the array)
    result.sort(PV.compare);
    result.sorted = true;
    return result;
  }
};

CS.CatalogCache.prototype.hasPackage = function (pkg) {
  return _.has(this._versions, pkg);
};

CS.CatalogCache.prototype.toJSONable = function () {
  var self = this;
  var data = {};
  _.each(self._dependencies, function (depsByPackage, key) {
    // depsByPackage is a map of String -> Dependency.
    // Map over the values to get an array of String.
    data[key] = _.map(depsByPackage, function (dep) {
      return dep.toString();
    });
  });
  return { data: data };
};

CS.CatalogCache.fromJSONable = function (obj) {
  check(obj, { data: Object });

  var cache = new CS.CatalogCache();
  _.each(obj.data, function (depsArray, pv) {
    check(depsArray, [String]);
    pv = CS.PackageAndVersion.fromString(pv);
    cache.addPackageVersion(
      pv.package, pv.version,
      _.map(depsArray, function (str) {
        return CS.Dependency.fromString(str);
      }));
  });
  return cache;
};

// Calls `iter` on each PackageAndVersion, with the second argument being
// a map from package name to Dependency.  If `iter` returns true,
// iteration is stopped.  There's no particular order to the iteration.
CS.CatalogCache.prototype.eachPackageVersion = function (iter) {
  var self = this;
  _.find(self._dependencies, function (value, key) {
    var stop = iter(CS.PackageAndVersion.fromString(key), value);
    return stop;
  });
};

// Calls `iter` on each package name, with the second argument being
// a list of versions present for that package (unique and sorted).
// If `iter` returns true, iteration is stopped.
ConstraintSolver.CatalogCache.prototype.eachPackage = function (iter) {
  var self = this;
  _.find(_.keys(self._versions), function (key) {
    var stop = iter(key, self.getPackageVersions(key));
    return stop;
  });
};


{{#template name="apiHttp"}}

HTTP

`HTTP` provides an HTTP request API on the client and server.  To use
these functions, add the HTTP package to your project by running in your
terminal:

```bash
meteor add http
```

{{> autoApiBox "HTTP.call"}}

This function initiates an HTTP request to a remote server.

On the server, this function can be run either synchronously or
asynchronously.  If the callback is omitted, it runs synchronously
and the results are returned once the request completes successfully.
If the request was not successful, an error is thrown.
This is
useful when making server-to-server HTTP API calls from within Meteor
methods, as the method can succeed or fail based on the results of the
synchronous HTTP call.  In this case, consider using
[`this.unblock()`](#method_unblock) to allow other methods on the same
connection to run in
the mean time.  On the client, this function must be used
asynchronously by passing a callback.

Both HTTP and HTTPS protocols are supported.  The `url` argument must be
an absolute URL including protocol and host name on the server, but may be
relative to the current host on the client.  The `query` option
replaces the query string of `url`.  Parameters specified in `params`
that are put in the URL are appended to any query string.
For example, with a `url` of `"/path?query"` and
`params` of `{foo:"bar"}`, the final URL will be `"/path?query&foo=bar"`.

The `params` are put in the URL or the request body, depending on the
type of request.  In the case of request with no bodies, like GET and
HEAD, the parameters will always go in the URL.  For a POST or other
type of request, the parameters will be encoded into the body with a
standard `x-www-form-urlencoded` content type, unless the `content`
or `data` option is used to specify a body, in which case the
parameters will be appended to the URL instead.

When run in asynchronous mode, the callback receives two arguments,
`error` and `result`.  The
`error` argument will contain an Error if the request fails in any
way, including a network error, time-out, or an HTTP status code in
the 400 or 500 range.  In case of a 4xx/5xx HTTP status code, the
`response` property on `error` matches the contents of the result
object.  When run in synchronous mode, either `result` is returned
from the function, or `error` is thrown.

Contents of the result object:

statusCode
  Number

Numeric HTTP result status code, or null on error.

content
  String

The body of the HTTP response as a string.

data
  Object or null

If the response headers indicate JSON content, this contains the body of the document parsed as a JSON object.

headers
  Object

A dictionary of HTTP headers from the response.

Example server method:

    Meteor.methods({checkTwitter: function (userId) {
      check(userId, String);
      this.unblock();
      try {
        var result = HTTP.call("GET", "http://api.twitter.com/xyz",
                               {params: {user: userId}});
        return true;
      } catch (e) {
        // Got a network error, time-out or HTTP error in the 400 or 500 range.
        return false;
      }
    }});

Example asynchronous HTTP call:

    HTTP.call("POST", "http://api.twitter.com/xyz",
              {data: {some: "json", stuff: 1}},
              function (error, result) {
                if (!error) {
                  Session.set("twizzled", true);
                }
              });

{{> autoApiBox "HTTP.get"}}
{{> autoApiBox "HTTP.post"}}
{{> autoApiBox "HTTP.put"}}
{{> autoApiBox "HTTP.del"}}
{{/template}}


Package.describe({
  name: 'dep-package',
  version: '0.0.1',
  documentation: null
});

Package.onUse(function(api) {
  api.addFiles('dep-package.js');
  api.use('jshint');
});


var _ = require('underscore');
var selftest = require('../tool-testing/selftest.js');
var httpHelpers = require('../utils/http-helpers.js');
var Sandbox = selftest.Sandbox;
var testUtils = require('../tool-testing/test-utils.js');
var config = require('../meteor-services/config.js');

// This is not an end-to-end test for Cordova hot code push, but this test
// is for the issue that we observed where the value of the
// --mobile-server argument would get lost across hot code pushes. That
// is: the initial Cordova app (before receiving any hot code pushes)
// would connect to the server specified by --mobile-server, but after
// it receives a hot code push, it would be connected to whatever
// ROOT_URL is on the server.
selftest.define(
  "cordova --mobile-server argument persists across hot code pushes", ["cordova"], function () {
    var s = new Sandbox();
    var run;

    s.createApp("myapp", "standard-app");
    s.cd("myapp");

    // Add 'android' to the .meteor/platforms file, just so that the
    // Cordova boilerplate will be generated and served, without having
    // to download the whole Android sdk.
    var platforms = s.read(".meteor/platforms");
    s.write(".meteor/platforms", platforms + "\nandroid\n");

    run = s.run("--mobile-server", "example.com");
    run.waitSecs(30);
    run.match("Started your app");

    var result = httpHelpers.getUrl(
      "http://localhost:3000/__cordova/index.html");

    var mrc = testUtils.getMeteorRuntimeConfigFromHTML(result);
    selftest.expectEqual(mrc.DDP_DEFAULT_CONNECTION_URL, "http://example.com");
    selftest.expectEqual(mrc.ROOT_URL, "http://example.com");

    run.stop();
});

selftest.define("cordova app gets https:// URLs when force-ssl is used", ["cordova", "net", "slow"], function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  // Add 'android' to the .meteor/platforms file, just so that the
  // Cordova boilerplate will be generated and served, without having
  // to download the whole Android sdk.
  var platforms = s.read(".meteor/platforms");
  s.write(".meteor/platforms", platforms + "\nandroid\n");

  run = s.run("add", "force-ssl");
  run.waitSecs(15);
  run.expectExit(0);

  testUtils.login(s, "test", "testtest");

  var appName = testUtils.randomAppName() + "." + config.getDeployHostname();
  run = s.run("deploy", appName);
  run.waitSecs(90);
  run.expectExit(0);

  var url = "https://" + appName;

  var result = httpHelpers.getUrl(url + "/__cordova/index.html");

  run = s.run("deploy", "-D", appName);
  run.waitSecs(30);
  run.expectExit(0);

  testUtils.logout(s);

  var mrc = testUtils.getMeteorRuntimeConfigFromHTML(result);
  selftest.expectEqual(mrc.DDP_DEFAULT_CONNECTION_URL, url + "/");
  selftest.expectEqual(mrc.ROOT_URL, url + "/");
});


const char kmChromiumArgs[] = "chromium-args";

const char kmSingleInstance[] = "single-instance";

// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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.

#include "content/nw/src/nw_package.h"

#include <vector>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_file_value_serializer.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/common/zip.h"
#include "content/nw/src/common/shell_switches.h"
#include "googleurl/src/gurl.h"
#include "grit/nw_resources.h"
#include "net/base/escape.h"
#include "third_party/node/deps/uv/include/uv.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "webkit/glue/image_decoder.h"

namespace nw {

namespace {

#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif

bool MakePathAbsolute(FilePath* file_path) {
  DCHECK(file_path);

  FilePath current_directory;
  if (!file_util::GetCurrentDirectory(&current_directory))
    return false;

  if (file_path->IsAbsolute())
    return true;

  if (current_directory.empty())
    return file_util::AbsolutePath(file_path);

  if (!current_directory.IsAbsolute())
    return false;

  *file_path = current_directory.Append(*file_path);
  return true;
}

FilePath GetSelfPath() {
  CommandLine* command_line = CommandLine::ForCurrentProcess();

  FilePath path;

  size_t size = 2*PATH_MAX;
  char* execPath = new char[size];
  if (uv_exepath(execPath, &size) == 0) {
    path = FilePath::FromUTF8Unsafe(std::string(execPath, size));
  } else {
    path = FilePath(command_line->GetProgram());
  }

#if defined(OS_MACOSX)
  // Find if we have node-webkit.app/Resources/app.nw.
  path = path.DirName().DirName().Append("Resources").Append("app.nw");
#endif

  return path;
}

void RelativePathToURI(FilePath root, base::DictionaryValue* manifest) {
  std::string old;
  if (!manifest->GetString(switches::kmMain, &old))
    return;

  // Don't append path if there is already a prefix
  if (MatchPattern(old, "*://*"))
    return;

  FilePath main_path = root.Append(FilePath::FromUTF8Unsafe(old));
  manifest->SetString(switches::kmMain,
                      std::string("file://") + main_path.AsUTF8Unsafe());
}

std::wstring ASCIIToWide(const std::string& ascii) {
  DCHECK(IsStringASCII(ascii)) << ascii;
  return std::wstring(ascii.begin(), ascii.end());
}

}  // namespace

Package::Package()
    : path_(GetSelfPath()),
      self_extract_(true) {
  // First try to extract self.
  if (InitFromPath())
    return;

  // Then see if we have arguments and extract it.
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  const CommandLine::StringVector& args = command_line->GetArgs();
  if (args.size() > 0) {
    self_extract_ = false;
    path_ = FilePath(args[0]);
  } else {
    // Try to load from the folder where the exe resides.
    // Note: self_extract_ is true here, otherwise a 'Invalid Package' error
    // would be triggered.
    path_ = GetSelfPath().DirName();
#if defined(OS_MACOSX)
    path_ = path_.DirName().DirName().DirName();
#endif
  }
  if (InitFromPath())
    return;

  // Finally we init with default settings.
  self_extract_ = false;
  InitWithDefault();
}

Package::Package(FilePath path)
    : path_(path),
      self_extract_(false) {
  if (!InitFromPath())
    InitWithDefault();
}

Package::~Package() {
}

FilePath Package::ConvertToAbsoutePath(const FilePath& path) {
  if (path.IsAbsolute())
    return path;

  return this->path().Append(path);
}

bool Package::GetImage(const FilePath& icon_path, gfx::Image* image) {
  FilePath path = ConvertToAbsoutePath(icon_path);

  // Read the file from disk.
  std::string file_contents;
  if (path.empty() || !file_util::ReadFileToString(path, &file_contents))
    return false;

  // Decode the bitmap using WebKit's image decoder.
  const unsigned char* data =
      reinterpret_cast<const unsigned char*>(file_contents.data());
  webkit_glue::ImageDecoder decoder;
  scoped_ptr<SkBitmap> decoded(new SkBitmap());
  // Note: This class only decodes bitmaps from extension resources. Chrome
  // doesn't (for security reasons) directly load extension resources provided
  // by the extension author, but instead decodes them in a separate
  // locked-down utility process. Only if the decoding succeeds is the image
  // saved from memory to disk and subsequently used in the Chrome UI.
  // Chrome is therefore decoding bitmaps here that were generated by Chrome.
  *decoded = decoder.Decode(data, file_contents.length());
  if (decoded->empty())
    return false;  // Unable to decode.

  *image = gfx::Image(*decoded.release());
  return true;
}

GURL Package::GetStartupURL() {
  std::string url;
  // Specify URL in --url
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kUrl)) {
    url = command_line->GetSwitchValueASCII(switches::kUrl);
    GURL gurl(url);
    if (!gurl.has_scheme())
      return GURL(std::string("http://") + url);

    return gurl;
  }

  // Report if encountered errors.
  if (!error_page_url_.empty())
    return GURL(error_page_url_);

  // Read from manifest.
  if (root()->GetString(switches::kmMain, &url))
    return GURL(url);
  else
    return GURL("nw:blank");
}

std::string Package::GetName() {
  std::string name("node-webkit");
  root()->GetString(switches::kmName, &name);
  return name;
}

bool Package::GetUseNode() {
  bool use_node = true;
  root()->GetBoolean(switches::kmNodejs, &use_node);
  return use_node;
}

base::DictionaryValue* Package::window() {
  base::DictionaryValue* window;
  root()->GetDictionaryWithoutPathExpansion(switches::kmWindow, &window);
  return window;
}

bool Package::InitFromPath() {
  base::ThreadRestrictions::SetIOAllowed(true);

  if (!ExtractPath())
    return false;

  // path_/package.json
  FilePath manifest_path = path_.AppendASCII("package.json");
  if (!file_util::PathExists(manifest_path)) {
    if (!self_extract())
      ReportError("Invalid package",
                  "There is no 'package.json' in the package, please make "
                  "sure the 'package.json' is in the root of the package.");
    return false;
  }

  // Parse file.
  std::string error;
  JSONFileValueSerializer serializer(manifest_path);
  scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
  if (!root.get()) {
    ReportError("Unable to parse package.json",
                error.empty() ?
                    "Failed to read the manifest file: " +
                        manifest_path.AsUTF8Unsafe() :
                    error);
    return false;
  } else if (!root->IsType(Value::TYPE_DICTIONARY)) {
    ReportError("Invalid package.json",
                "package.json's content should be a object type.");
    return false;
  }

  // Save result in global
  root_.reset(static_cast<DictionaryValue*>(root.release()));

  // Check fields
  const char* required_fields[] = {
    switches::kmMain,
    switches::kmName
  };
  for (unsigned i = 0; i < arraysize(required_fields); i++)
    if (!root_->HasKey(required_fields[i])) {
      ReportError("Invalid package.json",
                  std::string("Field '") + required_fields[i] + "'"
                      " is required.");
      return false;
    }

  // Force window field no empty.
  if (!root_->HasKey(switches::kmWindow)) {
    base::DictionaryValue* window = new base::DictionaryValue();
    window->SetString(switches::kmPosition, "center");
    root_->Set(switches::kmWindow, window);
  }

  // Read chromium command line args.
  ReadChromiumArgs();

  RelativePathToURI(path_, this->root());
  return true;
}

void Package::InitWithDefault() {
  root_.reset(new base::DictionaryValue());
  root()->SetString(switches::kmName, "node-webkit");
  root()->SetString(switches::kmMain, "nw:blank");
  base::DictionaryValue* window = new base::DictionaryValue();
  root()->Set(switches::kmWindow, window);

  // Hide toolbar if specifed in the command line.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoToolbar))
    window->SetBoolean(switches::kmToolbar, false);

  // Window should show in center by default.
  window->SetString(switches::kmPosition, "center");
}

bool Package::ExtractPath() {
  // Convert to absoulute path.
  if (!MakePathAbsolute(&path_)) {
    ReportError("Cannot extract package",
                "Path is invalid: " + path_.AsUTF8Unsafe());
    return false;
  }

  // Read symbolic link.
#if defined(OS_POSIX)
  FilePath target;
  if (file_util::ReadSymbolicLink(path_, &target))
    path_ = target;
#endif

  // If it's a file then try to extract from it.
  if (!file_util::DirectoryExists(path_)) {
    FilePath extracted_path;
    if (ExtractPackage(path_, &extracted_path)) {
      path_ = extracted_path;
    } else if (!self_extract()) {
      ReportError("Cannot extract package",
                  "Failed to unzip the package file: " + path_.AsUTF8Unsafe());
      return false;
    }
  }

  return true;
}

bool Package::ExtractPackage(const FilePath& zip_file, FilePath* where) {
  // Auto clean our temporary directory
  static scoped_ptr<base::ScopedTempDir> scoped_temp_dir;

#if defined(OS_WIN)
  if (!file_util::CreateNewTempDirectory(L"nw", where)) {
#else
  if (!file_util::CreateNewTempDirectory("nw", where)) {
#endif
    ReportError("Cannot extract package",
                "Unable to create temporary directory.");
    return false;
  }

  scoped_temp_dir.reset(new base::ScopedTempDir());
  if (!scoped_temp_dir->Set(*where)) {
    ReportError("Cannot extract package",
                "Unable to set temporary directory.");
    return false;
  }

  return zip::Unzip(zip_file, *where);
}

void Package::ReadChromiumArgs() {
  if (!root()->HasKey(switches::kmChromiumArgs))
    return;

  std::string args;
  if (!root()->GetStringASCII(switches::kmChromiumArgs, &args))
    return;

  std::vector<std::string> chromium_args;
  base::SplitString(args, ' ', &chromium_args);

  CommandLine* command_line = CommandLine::ForCurrentProcess();

  for (unsigned i = 0; i < chromium_args.size(); ++i) {
    CommandLine::StringType key, value;
#if defined(OS_WIN)
    // Note:: On Windows, the |CommandLine::StringType| will be |std::wstring|,
    // so the chromium_args[i] is not compatible. We convert the wstring to
    // string here is safe beacuse we use ASCII only.
    if (!IsSwitch(ASCIIToWide(chromium_args[i]), &key, &value))
      continue;
    command_line->AppendSwitchASCII(WideToASCII(key),
                                    WideToASCII(value));
#else
    if (!IsSwitch(chromium_args[i], &key, &value))
      continue;
    command_line->AppendSwitchASCII(key, value);
#endif
  }
}

void Package::ReportError(const std::string& title,
                          const std::string& content) {
  if (!error_page_url_.empty())
    return;

  const base::StringPiece template_html(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
          IDR_NW_PACKAGE_ERROR));

  if (template_html.empty()) {
    // Print hand written error info if nw.pak doesn't exist.
    NOTREACHED() << "Unable to load error template.";
    error_page_url_ = "data:text/html;base64,VW5hYmxlIHRvIGZpbmQgbncucGFrLgo=";
    return;
  }

  std::vector<std::string> subst;
  subst.push_back(title);
  subst.push_back(content);
  error_page_url_ = "data:text/html;charset=utf-8," +
      net::EscapeQueryParamValue(
          ReplaceStringPlaceholders(template_html, subst, NULL), false);
}

}  // namespace nw


// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_
#define CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "content/shell/shell_content_client.h"
#include "content/public/app/content_main_delegate.h"

namespace content {
class ShellContentBrowserClient;
class ShellContentRendererClient;

class ShellMainDelegate : public ContentMainDelegate {
 public:
  ShellMainDelegate();
  virtual ~ShellMainDelegate();

  // ContentMainDelegate implementation:
  virtual bool BasicStartupComplete(int* exit_code) OVERRIDE;
  virtual void PreSandboxStartup() OVERRIDE;
  virtual int RunProcess(
      const std::string& process_type,
      const MainFunctionParams& main_function_params) OVERRIDE;
  virtual ContentBrowserClient* CreateContentBrowserClient() OVERRIDE;
  virtual ContentRendererClient* CreateContentRendererClient() OVERRIDE;

  static void InitializeResourceBundle();

 private:
  scoped_ptr<ShellContentBrowserClient> browser_client_;
  scoped_ptr<ShellContentRendererClient> renderer_client_;
  ShellContentClient content_client_;

  DISALLOW_COPY_AND_ASSIGN(ShellMainDelegate);
};

}  // namespace content

#endif  // CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_


// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/nw/src/net/clear_on_exit_policy.h"

#include "content/public/common/url_constants.h"
#include "googleurl/src/gurl.h"
#include "webkit/quota/special_storage_policy.h"

ClearOnExitPolicy::ClearOnExitPolicy(
    quota::SpecialStoragePolicy* special_storage_policy)
    : special_storage_policy_(special_storage_policy) {
}

ClearOnExitPolicy::~ClearOnExitPolicy() {
}

bool ClearOnExitPolicy::HasClearOnExitOrigins() {
  return special_storage_policy_.get() &&
         special_storage_policy_->HasSessionOnlyOrigins();
}

bool ClearOnExitPolicy::ShouldClearOriginOnExit(const std::string& domain,
                                                bool scheme_is_secure) {
  if (domain.length() == 0)
    return false;

  std::string scheme =
      scheme_is_secure ? chrome::kHttpsScheme : chrome::kHttpScheme;
  std::string host = domain[0] == '.' ? domain.substr(1) : domain;
  GURL url(scheme + content::kStandardSchemeSeparator + host);

  return special_storage_policy_->IsStorageSessionOnly(url);
}


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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.

#ifndef CONTENT_NW_SRC_BROWSER_SHELL_TOOLBAR_DELEGATE_MAC_H_
#define CONTENT_NW_SRC_BROWSER_SHELL_TOOLBAR_DELEGATE_MAC_H_

#import <Cocoa/Cocoa.h>

#include "content/nw/src/browser/native_window.h"

namespace content {
class Shell;
}

@interface ShellToolbarDelegate : NSObject<NSToolbarDelegate> {
 @private
  content::Shell* shell_;
  NSTextField* entry_;
  NSToolbarItem* back_button_;
  NSToolbarItem* forward_button_;
  NSToolbarItem* refresh_stop_button_;
  NSToolbarItem* devtools_button_;
  NSToolbarItem* refresh_dev_button_;
}

- (id)initWithShell:(content::Shell*)shell;
- (void)buttonPressed:(id)sender;
- (void)urlEntered:(id)sender;
- (void)setEnabled:(BOOL)enabled
         forButton:(nw::NativeWindow::TOOLBAR_BUTTON)button;
- (void)setUrl:(NSString*)url;
- (void)setIsLoading:(BOOL)loading;

- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
     itemForItemIdentifier:(NSString *)itemIdentifier
 willBeInsertedIntoToolbar:(BOOL)flag;
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar;
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar;
@end

#endif  // CONTENT_NW_SRC_BROWSER_SHELL_TOOLBAR_DELEGATE_MAC_H_


// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Defines all the "content_shell" command-line switches.

#ifndef CONTENT_NW_SRC_SHELL_SWITCHES_H_
#define CONTENT_NW_SRC_SHELL_SWITCHES_H_

namespace switches {

extern const char kContentShellDataPath[];
extern const char kDeveloper[];
extern const char kNoToolbar[];
extern const char kUrl[];
extern const char kWorkingDirectory[];
extern const char kNodeMain[];

// Manifest settings
extern const char kmMain[];
extern const char kmName[];
extern const char kmWebkit[];
extern const char kmNodejs[];
extern const char kmWindow[];
extern const char kmChromiumArgs[];

extern const char kmSingleInstance[];

extern const char kmTitle[];
extern const char kmToolbar[];
extern const char kmIcon[];
extern const char kmFrame[];
extern const char kmShow[];
extern const char kmPosition[];
extern const char kmX[];
extern const char kmY[];
extern const char kmWidth[];
extern const char kmHeight[];
extern const char kmMinWidth[];
extern const char kmMinHeight[];
extern const char kmMaxWidth[];
extern const char kmMaxHeight[];
extern const char kmResizable[];
extern const char kmAsDesktop[];
extern const char kmFullscreen[];
extern const char kmKiosk[];
extern const char kmAlwaysOnTop[];

extern const char kmWebgl[];
extern const char kmJava[];
extern const char kmPlugin[];
extern const char kmPageCache[];
extern const char kmUserAgent[];
extern const char kmRemotePages[];
extern const char kmNewInstance[];
}  // namespace switches

#endif  // CONTENT_NW_SRC_SHELL_SWITCHES_H_


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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.

#include "content/nw/src/api/tray/tray.h"

#include "base/values.h"
#import <Cocoa/Cocoa.h>
#include "content/nw/src/api/menu/menu.h"

namespace api {

void Tray::Create(const base::DictionaryValue& option) {
  NSStatusBar *status_bar = [NSStatusBar systemStatusBar];
  status_item_ = [status_bar statusItemWithLength:NSVariableStatusItemLength];
  [status_item_ setHighlightMode:YES];
  [status_item_ retain];
}

void Tray::ShowAfterCreate() {
}

void Tray::Destroy() {
  [status_item_ release];
}

void Tray::SetTitle(const std::string& title) {
  [status_item_ setTitle:[NSString stringWithUTF8String:title.c_str()]];
}

void Tray::SetIcon(const std::string& icon) {
  if (!icon.empty()) {
    NSImage* image = [[NSImage alloc]
         initWithContentsOfFile:[NSString stringWithUTF8String:icon.c_str()]];
    [status_item_ setImage:image];
    [image release];
  } else {
    [status_item_ setImage:nil];
  }
}

void Tray::SetTooltip(const std::string& tooltip) {
  [status_item_ setToolTip:[NSString stringWithUTF8String:tooltip.c_str()]];
}

void Tray::SetMenu(Menu* menu) {
  [status_item_ setMenu:menu->menu_];
}

void Tray::Remove() {
  [[NSStatusBar systemStatusBar] removeStatusItem:status_item_];
}

}  // namespace api


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <title>SWFObject - step 1</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

      <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780" height="420">
        <param name="movie" value="relog.swf" />

        <object type="application/x-shockwave-flash" data="relog.swf" width="780" height="420">

Alternative content

        </object>

      </object>


keep keep keep keep keep replace keep keep keep keep


<button onclick="fourTimes(newWindow)">New gui windows</button>
<button onclick="fourTimes(newBrowserWindow)">New browser windows</button>

<button onclick="testWindows()">Test windows</button>
<button onclick="gui.App.quit()">quit</button>


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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.

#ifndef CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_MAC_H_
#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_MAC_H_

#import <Cocoa/Cocoa.h>

#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_nsobject.h"
#include "content/nw/src/browser/native_window.h"

@class ShellNSWindow;
@class ShellToolbarDelegate;
class SkRegion;

namespace nw {

class NativeWindowCocoa : public NativeWindow {
 public:
  explicit NativeWindowCocoa(content::Shell* shell,
                             base::DictionaryValue* manifest);
  virtual ~NativeWindowCocoa();

  // NativeWindow implementation.
  virtual void Close() OVERRIDE;
  virtual void Move(const gfx::Rect& pos) OVERRIDE;
  virtual void Focus(bool focus) OVERRIDE;
  virtual void Show() OVERRIDE;
  virtual void Hide() OVERRIDE;
  virtual void Maximize() OVERRIDE;
  virtual void Unmaximize() OVERRIDE;
  virtual void Minimize() OVERRIDE;
  virtual void Restore() OVERRIDE;
  virtual void SetFullscreen(bool fullscreen) OVERRIDE;
  virtual bool IsFullscreen() OVERRIDE;
  virtual void SetSize(const gfx::Size& size) OVERRIDE;
  virtual gfx::Size GetSize() OVERRIDE;
  virtual void SetMinimumSize(int width, int height) OVERRIDE;
  virtual void SetMaximumSize(int width, int height) OVERRIDE;
  virtual void SetResizable(bool resizable) OVERRIDE;
  virtual void SetAlwaysOnTop(bool top) OVERRIDE;
  virtual void SetPosition(const std::string& position) OVERRIDE;
  virtual void SetPosition(const gfx::Point& position) OVERRIDE;
  virtual gfx::Point GetPosition() OVERRIDE;
  virtual void SetTitle(const std::string& title) OVERRIDE;
  virtual void FlashFrame(bool flash) OVERRIDE;
  virtual void SetKiosk(bool kiosk) OVERRIDE;
  virtual bool IsKiosk() OVERRIDE;
  virtual void SetMenu(api::Menu* menu) OVERRIDE;
  virtual void SetToolbarButtonEnabled(TOOLBAR_BUTTON button,
                                       bool enabled) OVERRIDE;
  virtual void SetToolbarUrlEntry(const std::string& url) OVERRIDE;
  virtual void SetToolbarIsLoading(bool loading) OVERRIDE;

  // Called to handle a mouse event.
  void HandleMouseEvent(NSEvent* event);

  bool use_system_drag() const { return use_system_drag_; }
  SkRegion* draggable_region() const { return draggable_region_.get(); }

  void set_is_fullscreen(bool fullscreen) { is_fullscreen_ = fullscreen; }

  NSWindow* window() const { return window_; }

 protected:
  // NativeWindow implementation.
  virtual void AddToolbar() OVERRIDE;
  virtual void UpdateDraggableRegions(
      const std::vector<extensions::DraggableRegion>& regions) OVERRIDE;
  virtual void HandleKeyboardEvent(
      const content::NativeWebKeyboardEvent& event) OVERRIDE;

  void SetNonLionFullscreen(bool fullscreen);

 private:
  void InstallView();
  void UninstallView();
  void InstallDraggableRegionViews();
  void UpdateDraggableRegionsForSystemDrag(
      const std::vector<extensions::DraggableRegion>& regions,
      const extensions::DraggableRegion* draggable_area);
  void UpdateDraggableRegionsForCustomDrag(
      const std::vector<extensions::DraggableRegion>& regions);

  NSWindow* window_;

  // Delegate to the toolbar.
  scoped_nsobject<ShellToolbarDelegate> toolbar_delegate_;

  bool is_fullscreen_;
  bool is_kiosk_;
  NSRect restored_bounds_;

  NSInteger attention_request_id_;  // identifier from requestUserAttention

  // Indicates whether system drag or custom drag should be used, depending on
  // the complexity of draggable regions.
  bool use_system_drag_;

  // For system drag, the whole window is draggable and the non-draggable areas
  // have to been explicitly excluded.
  std::vector<gfx::Rect> system_drag_exclude_areas_;

  // For custom drag, the whole window is non-draggable and the draggable region
  // has to been explicitly provided.
  scoped_ptr<SkRegion> draggable_region_;  // used in custom drag.

  // Mouse location since the last mouse event, in screen coordinates. This is
  // used in custom drag to compute the window movement.
  NSPoint last_mouse_location_;

  DISALLOW_COPY_AND_ASSIGN(NativeWindowCocoa);
};

}  // namespace nw

#endif  // CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_MAC_H_


keep keep keep keep add keep keep keep keep

// Separator for string of |chromium_args| from |manifest|.

const char kChromiumArgsSeparator[] = " ";


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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.

#ifndef CONTENT_NW_SRC_API_APP_APP_H_
#define CONTENT_NW_SRC_API_APP_APP_H_

#include "base/basictypes.h"

#include <string>

namespace base {
class ListValue;
}

namespace content {
class RenderProcessHost;
class Shell;
}

namespace api {

class App {
 public:
  static void Call(const std::string& method,
                   const base::ListValue& arguments);

  static void Call(content::Shell* shell,
                   const std::string& method,
                   const base::ListValue& arguments,
                   base::ListValue* result);

  // Try to close all windows (then will cause whole app to quit).
  static void CloseAllWindows();

  // Quit the whole app.
  static void Quit(content::RenderProcessHost* render_view_host);

  // Post "open" event.
  static void EmitOpenEvent(const std::string& path);

 private:
  App();

  DISALLOW_COPY_AND_ASSIGN(App);
};

}  // namespace api

#endif  // CONTENT_NW_SRC_API_APP_APP_H_


var assert = require('assert');

describe('require', function() {
  describe('type', function() {
    it('is function', function() {
      assert.equal(typeof global.require, 'function');
    });
  });

  describe('members', function() {
    it('have resolve', function() {
      assert.equal(typeof global.require.resolve, 'function');
    });

    it('have main', function() {
      assert.equal(typeof global.require.main, 'object');
    });

    it('have extensions', function() {
      assert.equal(typeof global.require.extensions, 'object');
    });

    it('have registerExtension', function() {
      assert.equal(typeof global.require.registerExtension, 'function');
    });

    it('have cache', function() {
      assert.equal(typeof global.require.cache, 'object');
    });
  });
});

describe('node', function() {

  before(function() {
    var mocha_callback = process.listeners('uncaughtException')[1];
    process.removeListener('uncaughtException', mocha_callback);
  });

  describe('process', function() {
    it('uncaughtException should have a default listener', function() {
      assert.equal(process.listeners('uncaughtException').length, 1);
    });

    it('throwing uncaughtException should not show error page when having listener', function(done) {
      var empty = function(e) {
        process.removeListener('uncaughtException', empty);
        done();
      };
      process.on('uncaughtException', empty);
      process.nextTick(function() {
        throw new String('If you see this then uncaughtException test failed');
      });
    });
  });
});

describe('module', function() {
  describe('javascript modules', function() {
    var path = require('path');

    it('require by path', function() {
      assert.equal(require('./tests/node/module1.js').test(), 'module1');
    });

    it('simple file module', function() {
      assert.equal(require('./tests/node/node_modules/module3').test(), 'module3');
    });

    it('space in path', function() {
      assert.equal(require('./tests/node/node_modules/module4 space').test(), 'module4');
    });
  });

  describe('built-in modules', function() {
    var fs = require('fs');
    var http = require('http');
    var path = require('path');
    var exec = require('child_process').exec;

    it('read self', function() {
      var path1 = 'tests/node/node.js';
      var path2 = path.join(process.cwd(), 'tests', 'node', 'node.js');
      var content = fs.readFileSync(path1, 'utf8');
      fs.readFile(path2, 'utf8', function(err, data) {
        assert.equal(err, null);
        assert.equal(content, data);
      });
    });

    it('execute child process', function(done) {
      var child = exec('echo echo back', function (error, stdout, stderr) {
        assert.equal(error, null);
        assert.equal(stdout, process.platform == 'win32' ? 'echo back\r\n' : 'echo back\n');
        assert.equal(stderr, '');
        done();
      });
    });

    it('Buffer', function() {
      assert.equal(String(Buffer('test')), 'test');
    });

    it('http server and client', function(done) {
      var server = http.createServer(function (req, res) {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('hello');
      }).listen(10086);
      process.nextTick(function() {
        var count = 0;
        for (var i = 0; i < 5; ++i) {
          http.get("http://127.0.0.1:10086", function(res) {
            var data = '';
            res.on('end', function(e) {
              if (data == 'hello')
                ++count;
            }).on('data', function(chunk) {
              data += chunk;
            });
          }).on('error', function() {
            assert.equal(true, false);
          });
        }
        setTimeout(function() {
          assert.equal(count, 5);
          server.close();
          done();
        }, 50);
      });
    });
  });

  describe('native modules', function() {
    it('bignum should work on posix environment', function() {
      if (process.platform != 'win32') {
        var bignum = require('./node_modules/bignum');
        assert.equal(bignum('782910138827292261791972728324982').sub('182373273283402171237474774728373').div(8), '75067108192986261319312244199576');
      }
    });

    it('native modules without handle scope', function() {
      require('./node_modules/nw_test_loop_without_handle');
    });
  });
});

describe('performance', function() {
  describe('timers', function() {
    it('setTimeout in webkit and node.js should be accurate', function(done) {
      var flag1, flag2;
      global.setTimeout(function() { flag1 = true; }, 10);
      window.setTimeout(function() { flag2 = true; }, 10);

      setTimeout(function() {
        assert.equal(flag1, true);
        assert.equal(flag2, true);
        done();
      }, 50);
    });

    it('setInterval int webkit and node.js should be accurate', function(done) {
      var count1 = 0;
      var count2 = 0;;
      global.setInterval(function() { ++count1; }, 10);
      window.setInterval(function() { ++count2; }, 10);

      setTimeout(function() {
        assert.equal(count1 > 3, true);
        assert.equal(count2 > 3, true);
        done();
      }, 50);
    });
  });

  describe('IO', function() {
    var fs = require('fs');
    var path = require('path');
    var exec = require('child_process').exec;
    var tar = require('tar');

    it('file reading should be quick even when we have a child process running', function(done) {
      this.timeout(100);
      exec('sleep 1000', function () { });
      fs.readFile('tests/node/node.js', 'utf8', function(err, data) {
        done();
      });
    });

    it('untar a file should be quick', function(done) {
      this.timeout(100);
      fs.createReadStream('tests/node/test.tar')
      .pipe(tar.Extract({ path: 'tmp' }))
      .on('error', function (er) {
        assert.equal(false, true);
      })
      .on('end', function () {
        var content = fs.readFileSync('tmp/text', 'utf8');
        assert.equal(content, 'I love luyao\n');
        fs.unlinkSync('tmp/text');
        fs.rmdirSync('tmp');
        done();
      });
    });
  });
});

describe('browser', function() {
  describe('security', function() {
    it('cross domain call in file:// should succeed', function(done) {
      window.$.get('http://baidu.com', function(data, status) {
        assert.equal(status, 'success');
        assert.notEqual(data, null);
        done();
      });
    });
  });

  describe('render crashes', function() {
    it('MessageChannel should not crash', function(done) {
      var test = require('./tests/node/message_channel_test.js');
      test.run(done); // force to run in node context.
    });
  });
});