diff --git a/README.md b/README.md
index 5a56d29548..0c0a629192 100755
--- a/README.md
+++ b/README.md
@@ -144,6 +144,7 @@ List of Definitions
* [linq.js](http://linqjs.codeplex.com/) (by [Marcin Najder](https://github.com/marcinnajder))
* [Livestamp.js](https://github.com/mattbradley/livestampjs) (by [Vincent Bortone](https://github.com/vbortone))
* [Marked](https://github.com/chjj/marked) (by [William Orr](https://github.com/worr))
+* [Meteor](https://www.meteor.com) (by [Dave Allen](https://github.com/fullflavedave))
* [Modernizr](http://modernizr.com/) (by [Boris Yankov](https://github.com/borisyankov) and [Theodore Brown](https://github.com/theodorejb/))
* [Moment.js](https://github.com/timrwood/moment) (by [Michael Lakerveld](https://github.com/Lakerfield))
* [Mousetrap](http://craig.is/killing/mice) (by [Dániel Tar](https://github.com/qcz))
diff --git a/meteor/meteor-tests.ts b/meteor/meteor-tests.ts
new file mode 100644
index 0000000000..ca1973f638
--- /dev/null
+++ b/meteor/meteor-tests.ts
@@ -0,0 +1,610 @@
+///
+
+/**
+ * All code below was copied from the examples at http://docs.meteor.com/.
+ * When necessary, code was added to make the examples work (e.g. declaring a variable
+ * that was assumed to have been declared earlier)
+ */
+
+
+/*********************************** Begin setup for tests ******************************/
+
+// A developer must declare a var Template like this in a separate file to use this TypeScript type definition file
+interface ITemplate {
+ adminDashboard: IMeteorViewModel;
+ chat: IMeteorViewModel;
+}
+declare var Template: ITemplate;
+
+var Rooms = new Meteor.Collection('rooms');
+var Messages = new Meteor.Collection('messages');
+var Monkeys = new Meteor.Collection('monkeys');
+
+/********************************** End setup for tests *********************************/
+
+
+/**
+ * From Core, Meteor.startup section
+ * Tests Meteor.isServer, Meteor.startup, Collection.insert(), Collection.find()
+ */
+if (Meteor.isServer) {
+ Meteor.startup(function () {
+ if (Rooms.find().count() === 0) {
+ Rooms.insert({name: "Initial room"});
+ }
+ });
+}
+
+/**
+ * From Publish and Subscribe, Meteor.publish section
+ **/
+Meteor.publish("rooms", function () {
+ return Rooms.find({}, {fields: {secretInfo: 0}});
+});
+
+Meteor.publish("adminSecretInfo", function () {
+ return Rooms.find({admin: this.userId}, {fields: {secretInfo: 1}});
+});
+
+Meteor.publish("roomAndMessages", function (roomId) {
+ check(roomId, String);
+ return [
+ Rooms.find({_id: roomId}, {fields: {secretInfo: 0}}),
+ Messages.find({roomId: roomId})
+ ];
+});
+
+/**
+ * Also from Publish and Subscribe, Meteor.publish section
+ */
+Meteor.publish("counts-by-room", function (roomId) {
+ var self = this;
+ check(roomId, String);
+ var count = 0;
+ var initializing = true;
+ var handle = Messages.find({roomId: roomId}).observeChanges({
+ added: function (id) {
+ count++;
+// if (!initializing)
+
+// Todo: Not sure how to define in typescript
+// self.changed("counts", roomId, {count: count});
+ },
+ removed: function (id) {
+ count--;
+// Todo: Not sure how to define in typescript
+// self.changed("counts", roomId, {count: count});
+ }
+ });
+
+ initializing = false;
+
+// Todo: Not sure how to define in typescript
+// self.added("counts", roomId, {count: count});
+ self.ready();
+
+ self.onStop(function () {
+ handle.stop();
+ });
+});
+
+var Counts = new Meteor.Collection("counts");
+
+Deps.autorun(function () {
+ Meteor.subscribe("counts-by-room", Session.get("roomId"));
+});
+
+console.log("Current room has " +
+ Counts.findOne(Session.get("roomId")).count +
+ " messages.");
+
+/**
+ * From Publish and Subscribe, Meteor.subscribe section
+ */
+Meteor.subscribe("allplayers");
+
+/**
+ * Also from Meteor.subscribe section
+ */
+Deps.autorun(function () {
+ Meteor.subscribe("chat", {room: Session.get("current-room")});
+ Meteor.subscribe("privateMessages");
+});
+
+/**
+ * From Methods, Meteor.methods section
+ */
+Meteor.methods({
+ foo: function (arg1, arg2) {
+ check(arg1, String);
+ check(arg2, [Number]);
+
+ var you_want_to_throw_an_error = true;
+ if (you_want_to_throw_an_error)
+ throw new Meteor.Error(404, "Can't find my pants");
+ return "some return value";
+ },
+
+ bar: function () {
+ // .. do other stuff ..
+ return "baz";
+ }
+});
+
+/**
+ * From Methods, Meteor.call section
+ */
+Meteor.call('foo', 1, 2, function (error, result) {} );
+var result = Meteor.call('foo', 1, 2);
+
+/**
+ * From Collections, Meteor.Collection section
+ */
+// DA: I added the "var" keyword in there
+var Chatrooms = new Meteor.Collection("chatrooms");
+Messages = new Meteor.Collection("messages");
+
+var myMessages = Messages.find({userId: Session.get('myUserId')}).fetch();
+
+Messages.insert({text: "Hello, world!"});
+
+Messages.update(myMessages[0]._id, {$set: {important: true}});
+
+var Posts = new Meteor.Collection("posts");
+Posts.insert({title: "Hello world", body: "First post"});
+
+// Couldn't find assert() in the meteor docs
+//assert(Posts.find().count() === 1);
+
+/**
+ * Todo: couldn't figure out how to make this next line work with Typescript
+ * since there is already a Collection constructor with a different signature
+ *
+ var Scratchpad = new Meteor.Collection;
+for (var i = 0; i < 10; i++)
+ Scratchpad.insert({number: i * 2});
+assert(Scratchpad.find({number: {$lt: 9}}).count() === 5);
+**/
+
+var Animal = function (doc) {
+// _.extend(this, doc);
+};
+
+// DA: I altered this to remove dependencies on Underscore
+Animal.prototype = {
+ makeNoise: function () {
+ console.log(this.sound);
+ }
+}
+
+
+// Define a Collection that uses Animal as its document
+var Animals = new Meteor.Collection("Animals", {
+ transform: function (doc) { return new Animal(doc); }
+});
+
+// Create an Animal and call its makeNoise method
+Animals.insert({name: "raptor", sound: "roar"});
+Animals.findOne({name: "raptor"}).makeNoise(); // prints "roar"
+
+/**
+ * From Collections, Collection.insert section
+ */
+// DA: I added the variable declaration statements to make this work
+var Lists = new Meteor.Collection('Lists');
+var Items = new Meteor.Collection('Lists');
+
+var groceriesId = Lists.insert({name: "Groceries"});
+Items.insert({list: groceriesId, name: "Watercress"});
+Items.insert({list: groceriesId, name: "Persimmons"});
+
+/**
+ * From Collections, collection.update section
+ */
+var Players = new Meteor.Collection('Players');
+
+Template.adminDashboard.events({
+ 'click .givePoints': function () {
+ Players.update(Session.get("currentPlayer"), {$inc: {score: 5}});
+ }
+});
+
+/**
+ * Also from Collections, collection.update section
+ */
+Meteor.methods({
+ declareWinners: function () {
+ Players.update({score: {$gt: 10}},
+ {$addToSet: {badges: "Winner"}},
+ {multi: true});
+ }
+});
+
+/**
+ * From Collections, collection.remove section
+ */
+Template.chat.events({
+ 'click .remove': function () {
+ Messages.remove(this._id);
+ }
+});
+
+// DA: I added this next line
+var Logs = new Meteor.Collection('logs');
+
+Meteor.startup(function () {
+ if (Meteor.isServer) {
+ Logs.remove({});
+ Players.remove({karma: {$lt: -2}});
+ }
+});
+
+/***
+ * From Collections, collection.allow section
+ */
+Posts = new Meteor.Collection("posts");
+
+Posts.allow({
+ insert: function (userId, doc) {
+ // the user must be logged in, and the document must be owned by the user
+ return (userId && doc.owner === userId);
+ },
+ update: function (userId, doc, fields, modifier) {
+ // can only change your own documents
+ return doc.owner === userId;
+ },
+ remove: function (userId, doc) {
+ // can only remove your own documents
+ return doc.owner === userId;
+ },
+ fetch: ['owner']
+});
+
+Posts.deny({
+ update: function (userId, docs, fields, modifier) {
+ // can't change owners
+ return docs.userId = userId;
+ },
+ remove: function (userId, doc) {
+ // can't remove locked documents
+ return doc.locked;
+ },
+ fetch: ['locked'] // no need to fetch 'owner'
+});
+
+/**
+ * From Collections, cursor.forEach section
+ */
+var topPosts = Posts.find({}, {sort: {score: -1}, limit: 5});
+var count = 0;
+topPosts.forEach(function (post) {
+ console.log("Title of post " + count + ": " + post.title);
+ count += 1;
+});
+
+/**
+ * From Collections, cursor.count section
+ */
+var frag = Meteor.render(function () {
+ var highScoring = Posts.find({score: {$gt: 10}});
+ return "
There are " + highScoring.count() + " posts with " +
+ "scores greater than 10
";
+});
+document.body.appendChild(frag);
+
+/**
+ * From Collections, cursor.observeChanges section
+ */
+// DA: I added this line to make it work
+var Users = new Meteor.Collection('users');
+
+var count1 = 0;
+var query = Users.find({admin: true, onlineNow: true});
+var handle = query.observeChanges({
+ added: function (id, user) {
+ count1++;
+ console.log(user.name + " brings the total to " + count1 + " admins.");
+ },
+ removed: function () {
+ count1--;
+ console.log("Lost one. We're now down to " + count1 + " admins.");
+ }
+});
+
+// After five seconds, stop keeping the count.
+setTimeout(function () {handle.stop();}, 5000);
+
+/**
+ * From Sessions, Session.set section
+ */
+Deps.autorun(function () {
+ Meteor.subscribe("chat-history", {room: Session.get("currentRoomId")});
+});
+
+// Causes the function passed to Deps.autorun to be re-run, so
+// that the chat-history subscription is moved to the room "home".
+Session.set("currentRoomId", "home");
+
+/**
+ * From Sessions, Session.get section
+ */
+Session.set("enemy", "Eastasia");
+var frag1 = Meteor.render(function () {
+ return "We've always been at war with " +
+ Session.get("enemy") + "
";
+});
+
+// Page will say "We've always been at war with Eastasia"
+
+// DA: commented out since transpiler didn't like append()
+//document.body.append(frag1);
+
+// Page will change to say "We've always been at war with Eurasia"
+Session.set("enemy", "Eurasia");
+
+/**
+ * From Sessions, Session.equals section
+ */
+var value;
+Session.get("key") === value;
+Session.equals("key", value);
+
+/**
+ * From Accounts, Meteor.users section
+ */
+Meteor.publish("userData", function () {
+ return Meteor.users.find({_id: this.userId},
+ {fields: {'other': 1, 'things': 1}});
+});
+
+Meteor.users.deny({update: function () { return true; }});
+
+/**
+ * From Accounts, Meteor.loginWithExternalService section
+ */
+Accounts.loginServiceConfiguration.remove({
+ service: "weibo"
+});
+Accounts.loginServiceConfiguration.insert({
+ service: "weibo",
+ clientId: "1292962797",
+ secret: "75a730b58f5691de5522789070c319bc"
+});
+
+Meteor.loginWithGithub({
+ requestPermissions: ['user', 'public_repo']
+}, function (err) {
+ if (err)
+ Session.set('errorMessage', err.reason || 'Unknown error');
+});
+
+/**
+ * From Accounts, Accounts.ui.config section
+ */
+Accounts.ui.config({
+ requestPermissions: {
+ facebook: ['user_likes'],
+ github: ['user', 'repo']
+ },
+ requestOfflineToken: {
+ google: true
+ },
+ passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
+});
+
+/**
+ * From Accounts, Accounts.validateNewUser section
+ */
+Accounts.validateNewUser(function (user) {
+ if (user.username && user.username.length >= 3)
+ return true;
+ throw new Meteor.Error(403, "Username must have at least 3 characters");
+});
+// Validate username, without a specific error message.
+Accounts.validateNewUser(function (user) {
+ return user.username !== "root";
+});
+
+/**
+ * From Accounts, Accounts.onCreateUser section
+ */
+Accounts.onCreateUser(function(options, user) {
+ var d6 = function () { return Math.floor(Math.random() * 6) + 1; };
+ user.dexterity = d6() + d6() + d6();
+ // We still want the default hook's 'profile' behavior.
+ if (options.profile)
+ user.profile = options.profile;
+ return user;
+});
+
+/**
+ * From Passwords, Accounts.emailTemplates section
+ */
+Accounts.emailTemplates.siteName = "AwesomeSite";
+Accounts.emailTemplates.from = "AwesomeSite Admin ";
+Accounts.emailTemplates.enrollAccount.subject = function (user) {
+ return "Welcome to Awesome Town, " + user.profile.name;
+};
+Accounts.emailTemplates.enrollAccount.text = function (user, url) {
+ return "You have been selected to participate in building a better future!"
+ + " To activate your account, simply click the link below:\n\n"
+ + url;
+};
+
+/**
+ * From Templates, Template.myTemplate.helpers section
+ */
+Template.adminDashboard.helpers({
+ foo: function () {
+ return Session.get("foo");
+ }
+});
+
+/**
+ * From Templates, Template.myTemplate.preserve
+ */
+Template.adminDashboard.preserve({
+ 'input[id]': function (node) { return node.id; }
+});
+
+/**
+ * From Templates, Meteor.render section
+ */
+var frag2 = Meteor.render(function () {
+ return "There are " + Players.find({online: true}).count() +
+ " players online.
";
+});
+document.body.appendChild(frag2);
+
+Players.update({idleTime: {$gt: 30}}, {$set: {online: false}});
+
+/**
+ * From Templates, Meteor.renderList section
+ */
+var frag3 = Meteor.renderList(
+ Posts.find({tags: "frontpage"}),
+ function(post) {
+ var style = Session.equals("selectedId", post._id) ? "selected" : "";
+ // A real app would need to quote/sanitize post.name
+ return '' + post.name + '
';
+ });
+document.body.appendChild(frag3);
+
+var somePost = Posts.findOne({tags: "frontpage"});
+Session.set("selectedId", somePost._id);
+
+var eventTester = {
+ 'click p': function (event: IMeteorEvent) {
+ var paragraph = event.currentTarget; // always a P
+ var clickedElement = event.target; // could be the P or a child element
+ }
+}
+
+/**
+ * From Match section
+ */
+var Chats = new Meteor.Collection('chats');
+
+Meteor.publish("chats-in-room", function (roomId) {
+ // Make sure roomId is a string, not an arbitrary mongo selector object.
+ check(roomId, String);
+ return Chats.find({room: roomId});
+});
+
+Meteor.methods({addChat: function (roomId, message) {
+ check(roomId, String);
+ check(message, {
+ text: String,
+ timestamp: Date,
+ // Optional, but if present must be an array of strings.
+ tags: Match.Optional('Test String')
+ });
+
+ // ... do something with the message ...
+}});
+
+/**
+ * From Match patterns section
+ */
+var pat = { name: Match.Optional('test') };
+check({ name: "something" }, pat) // OK
+check({}, pat) // OK
+check({ name: undefined }, pat) // Throws an exception
+
+// Outside an object
+check(undefined, Match.Optional('test')); // OK
+
+/**
+ * From Deps, Deps.autorun section
+ */
+Deps.autorun(function () {
+ var oldest = Monkeys.findOne('age = 20');
+
+ if (oldest)
+ Session.set("oldest", oldest.name);
+});
+
+Deps.autorun(function (c) {
+ if (! Session.equals("shouldAlert", true))
+ return;
+
+ c.stop();
+ alert("Oh no!");
+});
+
+/**
+ * From Deps, Deps.Computation
+ */
+if (Deps.active) {
+ Deps.onInvalidate(function () {
+ Monkeys.destroy();
+ Rooms.finalize();
+ });
+}
+
+/**
+ * From Deps, Deps.Dependency
+ */
+var weather = "sunny";
+var weatherDep = new Deps.Dependency;
+
+var getWeather = function () {
+ weatherDep.depend()
+ return weather;
+};
+
+var setWeather = function (w) {
+ weather = w;
+ // (could add logic here to only call changed()
+ // if the new value is different from the old)
+ weatherDep.changed();
+};
+
+/**
+ * From HTTP, HTTP.call section
+ */
+Meteor.methods({checkTwitter: function (userId) {
+ check(userId, String);
+ this.unblock();
+ var result = HTTP.call("GET", "http://api.twitter.com/xyz",
+ {params: {user: userId}});
+ if (result.statusCode === 200)
+ return true
+ return false;
+}});
+
+
+HTTP.call("POST", "http://api.twitter.com/xyz",
+ {data: {some: "json", stuff: 1}},
+ function (error, result) {
+ if (result.statusCode === 200) {
+ Session.set("twizzled", true);
+ }
+ });
+
+/**
+ * From Email, Email.send section
+ */
+Meteor.methods({
+ sendEmail: function (to, from, subject, text) {
+ check([to, from, subject, text], [String]);
+
+ // Let other method calls from the same client start running,
+ // without waiting for the email sending to complete.
+ this.unblock();
+
+ Email.send({
+ to: to,
+ from: from,
+ subject: subject,
+ text: text
+ });
+ }
+});
+
+// In your client code: asynchronously send an email
+Meteor.call('sendEmail',
+ 'alice@example.com',
+ 'Hello from Meteor!',
+ 'This is a test of Email.send.');
+
diff --git a/meteor/meteor.d.ts b/meteor/meteor.d.ts
new file mode 100644
index 0000000000..83c3c94285
--- /dev/null
+++ b/meteor/meteor.d.ts
@@ -0,0 +1,591 @@
+// Type definitions for Meteor 0.6.5
+// Project: http://www.meteor.com/
+// Definitions by: Dave Allen
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+
+interface IMeteor {
+
+ /********
+ * Core *
+ ********/
+ isClient: boolean;
+ isServer: boolean;
+ startup(func: Function): void;
+ absoluteUrl(path: string,
+ options: {
+ secure?: boolean;
+ replaceLocalhost?: boolean;
+ rootUrl?: string;
+ }): void;
+ settings: Object;
+ release: string;
+
+
+ /*************************
+ * Publish and Subscribe *
+ *************************/
+
+ /**
+ * Publish a record set.
+ *
+ * @param name Name of the attribute set. If null, the set has no name, and the record set is
+ * automatically sent to all connected clients.
+ * @param func Function called on the server each time a client subscribes. Inside the function,
+ * this is the publish handler object, described below. If the client passed arguments
+ * to subscribe, the function is called with the same arguments.
+ */
+ publish(name: string, func: Function): any;
+ //Todo: Figure out a way to define this.userId, this.added, this.changed, etc that can be called from within publish
+
+ /**
+ * Subscribe to a record set. Returns a handle that provides stop() and ready() methods.
+ *
+ * @param name Name of the subscription. Matches name of server's publish() call.
+ * @param arg1,arg2,arg3 Optional arguments passed to publisher function on server.
+ * @param callbacks Optional. May include onError and onReady callbacks. Can be Object or Function. If a function
+ * is passed instead of an object, it is interpreted as an onReady callback.
+ */
+ subscribe(name: string, arg1?: any, arg2?: any, ars3?: any, arg4?: any, callbacks?: Object): IMeteorHandle;
+
+
+ /***********
+ * Methods *
+ ***********/
+ methods(methods: Object): void;
+ Error(error: number, reason?: string, details?: string): void;
+ // DA: Really should be defined like this: call(name: string, ...args?: any[], asyncCallback?: Function): void;
+ // But typescript does not allow Rest parameter (..args) to not be the last parameter defined
+ call(name: string, param1?: Object, param2?: Object, param3?: Object, param4?: Object, asyncCallback?: Function): void;
+ apply(name: string, options: any[], asyncCallback?: Function): void;
+ defer(callback: Function): void;
+
+
+ /*********************
+ * ServerConnections *
+ *********************/
+ status(): {
+ connected: boolean
+ status: string;
+ retryCount: number;
+ retryTime: number;
+ reason: string;
+ }
+ reconnect(): void;
+ disconnect(): void;
+
+
+ /***************
+ * Collections *
+ ***************/
+ Collection(name: string,
+ options?: {
+ connection?: Object;
+ idGeneration?: string;
+ transform?: Function;
+ }): void;
+
+
+ /************
+ * Accounts *
+ ************/
+ user(): {
+ _id: string;
+ username: string;
+ emails?: {
+ address: string;
+ verified: boolean;
+ }
+ profile?: Object;
+ services?: Object;
+ createdAt?: number;
+ };
+ userId(): string;
+ users: IMeteorCollection;
+ loggingIn(): boolean;
+ logout(callback?: Function): void;
+ loginWithPassword(user: Object, password: string, callback?: Function): void;
+ loginWithExternalService(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithFacebook(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithGithub(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithGoogle(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithMeetup(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithTwitter(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+ loginWithWeibo(options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ },
+ callback?: Function): void;
+
+ /*************
+ * Templates *
+ *************/
+ render(htmlFunc: Function): DocumentFragment;
+ renderList(observable: IMeteorCursor, docFunc: Function, elseFunc?: Function): DocumentFragment;
+
+ /**********
+ * Timers *
+ **********/
+ setTimeout(func: Function, delay: number): void;
+ setInterval(func: Function, delay: number): void;
+ clearTimeout(id: number): void;
+ clearInterval(id: number): void;
+
+
+ /******************** Begin definitions for contributed packages from Atmosphere (or elsewhere) *******************/
+
+ /**************************************************
+ * For Paginated-Subscription contributed package *
+ **************************************************/
+ subscribeWithPagination(collection: string, limit: number): IMeteorHandle;
+ Template(): void;
+
+ /*************************************************
+ * For Router or Iron-Router contributed package *
+ *************************************************/
+ Router: IMeteorRouter;
+
+ /*********************************
+ * For Error contributed package *
+ *********************************/
+ Errors: IMeteorErrors;
+
+ /******************** End definitions for contributed packages from Atmosphere (or elsewhere) *********************/
+
+} // End Meteor.someFunction definitions
+
+
+/***************
+ * Collections *
+ ***************/
+interface IMeteorCollection {
+ find(selector?, options?: Object): IMeteorCursor;
+ findOne(selector, options?: Object): Object;
+ insert(doc, callback?: Function): number;
+ update(selector, modifier, options?: Object, callback?: Function): void;
+ remove(selector, callback?: Function): void;
+ allow(options: Object): boolean;
+ deny(options: Object): boolean;
+ ObjectID(hexString?: string): Object;
+}
+
+interface IMeteorCursor {
+ forEach(callback: Function): void;
+ map(callback: Function): void;
+ fetch(): any[];
+ count(): number;
+ rewind(): void;
+ observe(callbacks: Object): void;
+ observeChanges(callbacks: Object): void;
+}
+
+
+/*************
+ * Templates *
+ *************/
+
+/**
+ * To use Meteor's Template.templateName.function, you must define an interface in a separate file with
+ * extension ".d.ts". Within the interface, every template name must have a property by that name that
+ * is of type IMeteorViewModel or IMeteorManager (choose either depending on your philosophical
+ * preference -- both work the same)
+ * e.g. file ".../client/views/view-model-types.d.ts":
+ *
+ * interface ITemplate {
+ * postsList: IMeteorViewModel;
+ * comment: IMeteorViewModel;
+ * notifications: IMeteorViewModel;
+ * [your template name]: IMeteorViewModel;
+ * }
+ * declare var Template: ITemplate;
+ */
+interface IMeteorViewModel {
+ rendered(callback: Function): void;
+ created(callback: Function): void;
+ destroyed(callback: Function): void;
+ events(eventMap: {[eventName: string]: Function;}): void;
+ helpers(helpers: Object): void;
+ preserve(selector: Object): void;
+}
+
+interface IMeteorManager {
+ rendered(callback: Function): void;
+ created(callback: Function): void;
+ destroyed(callback: Function): void;
+ events(eventMap: {[eventType: string]: Function}): void;
+ helpers(helpers: Object): Object;
+ preserve(selector: Object): void;
+}
+
+// DA: Currently not used, but I'd like to figure out a way to define the function signature
+// for teh callbacks in IMeteorViewModel, IMeteorManager, and many other interfaces
+interface IMeteorEvent {
+ type?: MeteorEventType.Value;
+ target?: Element;
+ currentTarget?: Element;
+ which?: number;
+ stopPropogation(): void;
+ stopImmediatePropogation(): void;
+ preventDefault(): void;
+ isPropogationStopped(): boolean;
+ isImmediatePropogationStopped(): boolean;
+ isDefaultPrevented(): boolean;
+}
+
+declare module MeteorEventType {
+ export enum Value {'click', 'dblclick', 'focus', 'blur', 'change',
+ 'mouseenter', 'mouseleave', 'mousedown', 'mouseup', 'keydown', 'keypress', 'keyup', 'tap'}
+}
+
+/***********
+ * Session *
+ ***********/
+interface IMeteorSession {
+ set(key: string, value: Object): void;
+ setDefault(key: string, value: Object): void;
+ get(key: string): Object;
+ equals(key: string, value: any): void;
+}
+
+interface IMeteorHandle {
+ loaded(): number;
+ limit(): number;
+ ready(): boolean;
+ loadNextPage(): void;
+}
+
+/**************************
+ * Accounts and Passwords *
+ **************************/
+interface IMeteorAccounts {
+ config(options: {
+ sendVerificationEmail?: boolean;
+ forbidClientAccountCreation?: boolean;
+ }): void;
+ ui: {
+ config(options: {
+ requestPermissions?: Object;
+ requestOfflineToken?: Object;
+ passwordSignupFields?: string;
+ });
+ }
+ validateNewUser(func: Function): void;
+ onCreateUser(func: Function): void;
+ createUser(options: {
+ username?: string;
+ email?: string;
+ password?: string;
+ profile?: string;
+ },
+ callback?: Function): void;
+ changePassword(oldPassword: string, newPassword: string, callback?: Function): void;
+ forgotPassword(options: {
+ email: string
+ },
+ callback?: Function): void;
+ resetPassword(token: string, newPassword: string, callback?: Function): void;
+ setPassword(userId: string, newPassword: string): void;
+ verifyEmail(token: string, callback?: Function): void;
+ sendResetPasswordEmail(userId: string, email?: string): void;
+ sendEnrollmentEmail(userId: string, email?: string): void;
+ sendVerificationEmail(userId: string, email?: string): void;
+ emailTemplates: {
+ from: string;
+ siteName: string;
+ resetPassword: IMeteorEmailValues;
+ enrollAccount: IMeteorEmailValues;
+ verifyEmail: IMeteorEmailValues;
+ }
+ // DA: I didn't see the signature for this, but it appears in the examples
+ loginServiceConfiguration: {
+ remove(options: Object): void;
+ insert(options: Object): void;
+ }
+}
+
+interface IMeteorEmailValues {
+ subject?: Function;
+ text?: Function;
+}
+
+interface IMeteorMatch {
+ test(value: any, pattern: any): boolean;
+ Any;
+ String;
+ Number;
+ Boolean;
+ undefined;
+ null;
+ Integer;
+ ObjectIncluding;
+ Object;
+ Optional(pattern: string);
+ OneOf(...args: string[]);
+ Where(condition: boolean);
+}
+
+interface IExternalServiceParams {
+ options?: {
+ requestPermissions?: string[];
+ requestOfflineToken?: boolean;
+ forceApprovalPrompt?: boolean;
+ }
+ callback?: Function;
+}
+
+/********
+ * Deps *
+ ********/
+interface IMeteorDeps {
+ autorun(runFunc: Function): IMeteorComputationObject;
+ flush(): void;
+ nonreactive(func: Function): void;
+ active: boolean;
+ currentComputation: IMeteorComputationObject;
+ onInvalidate(callback: Function): void;
+ afterFlush(callback: Function): void;
+
+ /**
+ * @constructor
+ */
+ Computation(): void;
+
+ /**
+ * @constructor
+ */
+ Dependency(): void;
+}
+
+interface IMeteorComputationObject {
+ stop(): void;
+ invalidate(): void;
+ onInvalidate(callback: Function): void;
+ stopped: boolean;
+ invalidated: boolean;
+ firstRun: boolean;
+}
+
+interface IMeteorDependencyObject {
+ changed(): void;
+ depend(fromComputation?: IMeteorComputationObject): boolean;
+ hasDependents(): boolean;
+}
+
+/*********
+ * EJSON *
+ *********/
+interface IMeteorEJSON {
+ parse(str: string): void;
+ stringify(val: any): string;
+ fromJSONValue(val): any;
+ toJSONValue(val): JSON;
+ equals(any: any): boolean;
+ clone(val: any): any;
+ newBinary(size: number): void;
+ isBinary(x: any): boolean;
+ addType(name: string, factory: Function): void;
+}
+
+/****************
+ * HTTP package *
+ ****************/
+interface IMeteorHTTP {
+ call(method: string, url: string, options: {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+ }, asyncCallback?: Function): IMeteorHTTPResult;
+ get(url: string, options?: {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+ }, asyncCallback?: Function): IMeteorHTTPResult;
+ post(url: string, options?: {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+ }, asyncCallback?: Function): IMeteorHTTPResult;
+ put(url: string, options?: {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+ }, asyncCallback?: Function): IMeteorHTTPResult;
+ del(url: string, options?: {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+ }, asyncCallback?: Function): IMeteorHTTPResult;
+}
+
+// DA: Currently not used
+// I would like to figure out a way to specify this as type for options for IMeteorHTTP methods.
+// Tests don't work if I simply specify this interface as the type.
+interface IMeteorHTTPCallOptions {
+ content?: string;
+ data?: Object;
+ query?: string;
+ params?: Object;
+ auth?: string;
+ headers?: Object;
+ timeout?: number;
+ followRedirects?: boolean;
+}
+
+interface IMeteorHTTPResult {
+ statusCode: number;
+ content: string;
+ data?: JSON;
+ headers: Object;
+}
+
+/*********
+ * Email *
+ *********/
+interface IMeteorEmail {
+ send(options: {
+ from?: string;
+ to: any;
+ cc?: any;
+ bcc?: any;
+ replyTo?: any;
+ subject?: string;
+ text?: string;
+ html?: string;
+ headers?: Object;
+ }): void;
+}
+
+
+/**********
+ * Assets *
+ **********/
+interface IMeteorAssets {
+ getText(assetPath: string, asyncCallback?: Function): string;
+ getBinary(assetPath: string, asyncCallback?: Function): any;
+}
+
+/*******
+ * DPP *
+ *******/
+interface IMeteorDPP {
+ connect(url: string): void;
+}
+
+declare var Meteor: IMeteor;
+declare var Collection: IMeteorCollection;
+declare var Session: IMeteorSession;
+declare var Deps: IMeteorDeps;
+declare var Accounts: IMeteorAccounts;
+declare var Match: IMeteorMatch;
+declare function check(value: any, pattern: any): void;
+declare var Computation: IMeteorComputationObject;
+declare var Dependency: IMeteorDependencyObject;
+declare var EJSON: IMeteorEJSON;
+declare var HTTP: IMeteorHTTP;
+declare var Email: IMeteorEmail;
+declare var Assets: IMeteorAssets;
+declare var DPP: IMeteorDPP;
+
+declare function changed(collection: string, id: string, fields, Object): void;
+
+/******************** Begin definitions for contributed packages from Atmosphere (or elsewhere) *********************/
+
+/***************************************************
+ * For Router and Iron-Router contributed packages *
+ ***************************************************/
+interface IMeteorRouter {
+
+ // These are for Router
+ page(): void;
+ add(route: Object): void;
+ to(path: string, ...args: any[]): void;
+ filters(filtersMap: Object);
+ filter(filterName: string, options?: Object);
+
+ // These are for Iron-Router
+ map(routeMap: Function): void;
+ path(route: string, params?: Object): void;
+ url(route: string): void;
+ routes: Object;
+ configure(options: IMeteorRouterConfig): void;
+}
+
+// For Iron-Router
+interface IMeteorRouterConfig {
+ layout: string;
+ notFoundTemplate: string;
+ loadingTemplate: string;
+ renderTemplates: Object;
+}
+
+interface IMeteorErrors {
+ throw(message: string): void;
+ clear(): void;
+}
+
+// For Router and Iron-Router contributed packages
+declare var Router: IMeteorRouter;
+
+/******************** End definitions for contributed packages from Atmosphere (or elsewhere) ***********************/
+
+/**
+ * Todo:
+ * Define "this.function" functions.
+ * Define the signatures of callback functions and other functions.
+ ***/
\ No newline at end of file