├── .gitignore ├── LICENSE.txt ├── README.md ├── example ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── identifier │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── example.css ├── example.html ├── example.js └── packages │ └── percolate:momentum ├── momentum.html ├── momentum.js ├── package.js ├── plugins ├── css.js ├── fade.js ├── growl.js ├── none.js ├── side-to-side.js ├── slide-height.js └── velocity.js └── versions.json /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Percolate Studio Pty. Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | meteor-momentum 2 | =============== 3 | 4 | Reactive animation package for Meteor. 5 | 6 | Momentum allows you to easily and simply re-use animation behaviour in your app via plugins that can be shared easily. 7 | 8 | # Caveat 9 | 10 | We published this package as an exploration of an idea rather than something we rigorously maintain for public consumption. As such, the plugins that we ship probably won't work for many use cases in the wild. You're encouraged to create your own custom plugins specific to the needs of your application. Momentum is designed as a thin interface to `uihooks` that can be declaratively used in your templates. 11 | 12 | # Install 13 | 14 | ``` 15 | meteor add percolate:momentum 16 | ``` 17 | 18 | # Usage 19 | 20 | Wrap an element which is being added or removed (or moved) from the DOM in 21 | 22 | ``` 23 | {{#momentum plugin='X'}} 24 | {{! your content }} 25 | {{/momentum}} 26 | ``` 27 | 28 | For example 29 | 30 | ``` 31 | {{#momentum plugin='right-to-left'}} 32 | {{#if show}} 33 |

My text!

34 | {{/if}} 35 | {{/momentum}} 36 | ``` 37 | 38 | When the `show` helper changes the element will appear as normal, but mediated by the `right-to-left` plugin (see the examples folder for some example plugins). 39 | 40 | See also: [momentum-iron-router](https://atmospherejs.com/percolate/momentum-iron-router) 41 | 42 | # Examples 43 | 44 | See the example directory in the project, also hosted at [momentum-example.meteor.com](http://momentum-example.meteor.com) 45 | 46 | # Built-in Plugins 47 | 48 | ## `css` 49 | 50 | Makes it easy to drive css transitions. By default adds the `.in`, `.out` and `.off-screen` classes to conrol the transition. Use the `extra` option to set another class during the transition. Add a `timeout` as a fallback if the `transtionEnd` event doesn't fire. 51 | 52 | ## `growl` 53 | 54 | A simple, but useful plugin to do a "growl" style system notification animation. 55 | 56 | ## `right-to-left`, `left-to-right` 57 | 58 | Good for mobile-style page-page transition animations. 59 | 60 | ## `fade` 61 | 62 | Fades in/out. 63 | 64 | ## `slide-height` 65 | 66 | Animate height in from zero to full, out from full to zero. Works with auto-height elements. 67 | 68 | # Your own plugin 69 | 70 | Writing a plugin is simple. See the existing plugins for examples. You just need to provide an `insertElement` and `removeElement` (and optionally `moveElement`) hook. These have the same API as [Meteor's `_uihooks`](https://github.com/meteor/meteor/blob/master/History.md#blaze-2). 71 | 72 | # Contributions 73 | 74 | It would make sense to build packages that provide extra plugins. If there's something you need in the core package to make your package work, please, open an issue or make a pull request. 75 | 76 | ## License 77 | 78 | MIT. (c) Percolate Studio, maintained by Zoltan Olah (@zol). 79 | -------------------------------------------------------------------------------- /example/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | -------------------------------------------------------------------------------- /example/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /example/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | ms27ed1jl2nsp1hchaa1 8 | -------------------------------------------------------------------------------- /example/.meteor/identifier: -------------------------------------------------------------------------------- 1 | 195s59qqwyhb117fklju -------------------------------------------------------------------------------- /example/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | standard-app-packages 7 | autopublish 8 | insecure 9 | percolate:momentum 10 | -------------------------------------------------------------------------------- /example/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /example/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.0 2 | -------------------------------------------------------------------------------- /example/.meteor/versions: -------------------------------------------------------------------------------- 1 | application-configuration@1.0.3 2 | autopublish@1.0.1 3 | autoupdate@1.1.3 4 | base64@1.0.1 5 | binary-heap@1.0.1 6 | blaze-tools@1.0.1 7 | blaze@2.0.3 8 | boilerplate-generator@1.0.1 9 | callback-hook@1.0.1 10 | check@1.0.2 11 | ctl-helper@1.0.4 12 | ctl@1.0.2 13 | ddp@1.0.11 14 | deps@1.0.5 15 | ejson@1.0.4 16 | fastclick@1.0.1 17 | follower-livedata@1.0.2 18 | geojson-utils@1.0.1 19 | html-tools@1.0.2 20 | htmljs@1.0.2 21 | http@1.0.8 22 | id-map@1.0.1 23 | insecure@1.0.1 24 | jquery@1.0.1 25 | json@1.0.1 26 | launch-screen@1.0.0 27 | livedata@1.0.11 28 | logging@1.0.5 29 | meteor-platform@1.2.0 30 | meteor@1.1.3 31 | minifiers@1.1.2 32 | minimongo@1.0.5 33 | mobile-status-bar@1.0.1 34 | mongo@1.0.8 35 | observe-sequence@1.0.3 36 | ordered-dict@1.0.1 37 | percolate:momentum@0.7.2 38 | random@1.0.1 39 | reactive-dict@1.0.4 40 | reactive-var@1.0.3 41 | reload@1.1.1 42 | retry@1.0.1 43 | routepolicy@1.0.2 44 | session@1.0.4 45 | spacebars-compiler@1.0.3 46 | spacebars@1.0.3 47 | standard-app-packages@1.0.3 48 | templating@1.0.9 49 | tracker@1.0.3 50 | ui@1.0.4 51 | underscore@1.0.1 52 | url@1.0.2 53 | velocityjs:velocityjs@1.2.1 54 | webapp-hashing@1.0.1 55 | webapp@1.1.4 56 | -------------------------------------------------------------------------------- /example/example.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background: #eee; 3 | width: 200px; 4 | height: 200px; 5 | margin-bottom: 10px; 6 | } 7 | 8 | .content { 9 | background: #ddd; 10 | width: 100px; 11 | } 12 | 13 | .if-container .content { 14 | height: 100px; 15 | } 16 | 17 | .each-container .content { 18 | height: 20px; 19 | } 20 | 21 | .notifications .content { 22 | height: 20px; 23 | width: 200px; 24 | margin-bottom: 4px; 25 | } 26 | 27 | .plugin { 28 | float: left; 29 | margin-left: 10px; 30 | } 31 | 32 | .plugin.css .content { 33 | transition: opacity 500ms ease-in; 34 | opacity: 1; 35 | } 36 | 37 | .plugin.css .content.off-screen { 38 | opacity: 0; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | 5 | 6 |

Momentum Example

7 | {{> hello}} 8 | {{> growl}} 9 | 10 | 11 | 52 | 53 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | if (Meteor.isClient) { 2 | var Items = new Meteor.Collection(null); 3 | var Notifications = new Meteor.Collection(null); 4 | 5 | Session.setDefault('foo', true) 6 | Template.hello.helpers({ 7 | foo: function() { 8 | return Session.get('foo'); 9 | }, 10 | 11 | items: function() { 12 | return Items.find(); 13 | } 14 | }); 15 | 16 | Template.hello.events({ 17 | 'click .if-container': function() { 18 | Session.set('foo', ! Session.get('foo')); 19 | }, 20 | 21 | 'click .each-container': function() { 22 | Items.insert({}); 23 | } 24 | }); 25 | 26 | Template.growl.helpers({ 27 | notifications: function() { 28 | return Notifications.find({}, {sort: {createdAt: -1}}); 29 | } 30 | }) 31 | 32 | Template.growl.events({ 33 | 'click [data-add-notification]': function() { 34 | var id = Notifications.insert({ 35 | title: 'Notification no. ' + Notifications.find().count(), 36 | createdAt: new Date 37 | }); 38 | 39 | Meteor.setTimeout(function() { 40 | Notifications.remove({_id: id}); 41 | }, 3000); 42 | } 43 | }); 44 | } -------------------------------------------------------------------------------- /example/packages/percolate:momentum: -------------------------------------------------------------------------------- 1 | ../.. -------------------------------------------------------------------------------- /momentum.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /momentum.js: -------------------------------------------------------------------------------- 1 | Template.momentum.rendered = function() { 2 | if (! this.data || ! this.data.plugin) 3 | return console.error("Missing 'plugin' argument to momentum"); 4 | 5 | var plugin = Momentum.plugins[this.data.plugin]; 6 | 7 | if (! plugin) 8 | return console.error("Can't find momentum plugin '" + this.data.plugin + "'"); 9 | 10 | var hooks = plugin(_.omit(this.data, 'plugin')); 11 | 12 | // default is to remove, *then* add 13 | if (! hooks.moveElement) 14 | hooks.moveElement = function(node, next, done) { 15 | hooks.removeElement(node, function() { 16 | hooks.insertElement(node, next, done); 17 | }); 18 | } 19 | 20 | check(hooks, Match.Where(function (x) { 21 | return _.isFunction(x.insertElement) && _.isFunction(x.moveElement) && _.isFunction(x.removeElement); 22 | })); 23 | 24 | // Pass in the _identity function for the done callback as by default 25 | // momentum doesn't care about when transitions are done. 26 | this.lastNode._uihooks = { 27 | insertElement: function(node, next) { 28 | hooks.insertElement(node, next, _.identity); 29 | }, 30 | moveElement: function(node, next) { 31 | hooks.moveElement(node, next, _.identity); 32 | }, 33 | removeElement: function(node, done) { 34 | hooks.removeElement(node, _.identity); 35 | } 36 | }; 37 | } 38 | 39 | Momentum = { 40 | plugins: {}, 41 | registerPlugin: function(name, plugin) { 42 | check(name, String); 43 | check(plugin, Function) 44 | this.plugins[name] = plugin; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: "Reactive animations", 3 | version: "0.7.3", 4 | name: "percolate:momentum", 5 | git: "https://github.com/percolatestudio/meteor-momentum.git" 6 | }); 7 | 8 | Package.onUse(function (api) { 9 | api.versionsFrom('METEOR@0.9.2'); 10 | api.use(['templating', 'check', 'jquery', 'underscore', 'velocityjs:velocityjs@1.2.1' 11 | ], 'client'); 12 | 13 | api.addFiles([ 14 | 'momentum.html', 15 | 'momentum.js', 16 | 'plugins/none.js', 17 | 'plugins/css.js', 18 | 'plugins/velocity.js', 19 | 'plugins/growl.js', 20 | 'plugins/side-to-side.js', 21 | 'plugins/slide-height.js', 22 | 'plugins/fade.js' 23 | ], 'client'); 24 | 25 | api.export(['Momentum'], 'client'); 26 | }); 27 | -------------------------------------------------------------------------------- /plugins/css.js: -------------------------------------------------------------------------------- 1 | // defaults, should be overrideable 2 | var OFFSCREEN_CLASS = 'off-screen'; 3 | var IN_CLASS = 'in'; 4 | var OUT_CLASS = 'out'; 5 | 6 | var EVENTS = 'webkitTransitionEnd oTransitionEnd transitionEnd ' 7 | + 'msTransitionEnd transitionend'; 8 | 9 | Momentum.registerPlugin('css', function(options) { 10 | options = _.extend({ 11 | // extra: a function that returns an extra class to be added 12 | // timeout: a "maximum" time that the transition can take 13 | }, options); 14 | 15 | if (_.isString(options.extra)) { 16 | var extra = options.extra; 17 | options.extra = function() { return extra; } 18 | } 19 | check(options.extra, Match.Optional(Function)); 20 | 21 | return { 22 | insertElement: function(node, next, done) { 23 | var klass = IN_CLASS; 24 | if (options.extra) 25 | klass += ' ' + options.extra(); 26 | 27 | $(node) 28 | .addClass(OFFSCREEN_CLASS) 29 | .addClass(klass) 30 | .insertBefore(next); 31 | 32 | Deps.afterFlush(function() { 33 | // call width to force the browser to draw before we do anything 34 | $(node).width() 35 | 36 | var finish = _.once(function() { 37 | $(node).removeClass(klass); 38 | done(); 39 | }); 40 | 41 | $(node).removeClass(OFFSCREEN_CLASS); 42 | 43 | // XXX: We have to bind the event listener to the PARENT of the node, 44 | // because Blaze runs jQ.cleanNode before "removing" the node. 45 | // 46 | // What this means is, that if we are mid-way through our "add" 47 | // transition, and then the node is "removed", then even though 48 | // we may not actually remove the node from the DOM, this 49 | // event will never fire, meaning classes and junk will be left on 50 | // the node. This seems a pretty reasonable workaround. 51 | // 52 | // If the parent itself is "removed".. well then we'll have a problem. 53 | $(node).parent() 54 | .on(EVENTS, function(e) { 55 | if (e.target === node) { 56 | $(this).off(e); 57 | finish() 58 | } 59 | }); 60 | 61 | if (options.timeout) 62 | Meteor.setTimeout(finish, options.timeout); 63 | }); 64 | }, 65 | // we could do better I guess? 66 | moveElement: function(node, next, done) { 67 | var self = this; 68 | self.removeElement(node, function() { 69 | self.insertElement(node, next, done); 70 | }); 71 | }, 72 | removeElement: function(node, done) { 73 | var klass = OUT_CLASS; 74 | if (options.extra) 75 | klass += ' ' + options.extra(); 76 | 77 | // add the out class and redraw 78 | $(node) 79 | .addClass(klass) 80 | .width(); 81 | 82 | var finish = _.once(function() { 83 | $(node).remove(); 84 | done(); 85 | }); 86 | 87 | // now make it transition off 88 | $(node) 89 | .addClass(OFFSCREEN_CLASS) 90 | .on(EVENTS, function(e) { 91 | if (e.target === node) { 92 | $(this).off(e); 93 | finish(); 94 | } 95 | }); 96 | 97 | if (options.timeout) 98 | Meteor.setTimeout(finish, options.timeout); 99 | } 100 | } 101 | }); 102 | 103 | -------------------------------------------------------------------------------- /plugins/fade.js: -------------------------------------------------------------------------------- 1 | Momentum.registerPlugin('fade', function(options) { 2 | return { 3 | insertElement: function(node, next) { 4 | $(node) 5 | .hide() 6 | .insertBefore(next) 7 | .velocity('fadeIn'); 8 | }, 9 | removeElement: function(node) { 10 | $(node).velocity('fadeOut', function() { 11 | $(this).remove(); 12 | }); 13 | } 14 | } 15 | }); -------------------------------------------------------------------------------- /plugins/growl.js: -------------------------------------------------------------------------------- 1 | Momentum.registerPlugin('growl', function(options) { 2 | options = _.extend({}, options, { 3 | duration: 500, 4 | easing: [ 250, 15 ], //spring physics 5 | // easing: [ 0.17, 0.67, 0.83, 0.67 ] //bezier curve 6 | }); 7 | 8 | return { 9 | insertElement: function(node, next) { 10 | var $node = $(node); 11 | 12 | $node 13 | .insertBefore(next) 14 | .velocity({ 15 | translateZ: 0, 16 | scale: [1, 0] 17 | }, { 18 | easing: options.easing, 19 | duration: options.duration, 20 | queue: false 21 | }) 22 | .velocity("fadeIn", { 23 | duration: options.duration, 24 | queue: false 25 | }); 26 | 27 | $(next).nextAll() 28 | .velocity({ 29 | translateZ: 0, 30 | translateY: [-1 * $node.outerHeight()] 31 | }, { 32 | duration: 0, 33 | queue: false 34 | }) 35 | .velocity({ 36 | translateY: [0] 37 | }, { 38 | duration: options.duration - 100, //speed it up a little 39 | queue: false 40 | }); 41 | }, 42 | moveElement: function(node, next) { 43 | this.removeElement(node); 44 | this.insertElement(node, next); 45 | }, 46 | removeElement: function(node) { 47 | var $node = $(node); 48 | 49 | $node 50 | .velocity("fadeOut", { 51 | duration: options.duration, 52 | complete: function() { 53 | $node.remove(); 54 | } 55 | }); 56 | } 57 | } 58 | }); 59 | 60 | -------------------------------------------------------------------------------- /plugins/none.js: -------------------------------------------------------------------------------- 1 | Momentum.registerPlugin('none', function(options) { 2 | return { 3 | insertElement: function(node, next, done) { 4 | next.parentNode.insertBefore(node, next); 5 | done(); 6 | }, 7 | moveElement: function(node, next, done) { 8 | next.parentNode.insertBefore(node, next); 9 | done(); 10 | }, 11 | removeElement: function(node, done) { 12 | node.parentNode.removeChild(node); 13 | done(); 14 | } 15 | } 16 | }); -------------------------------------------------------------------------------- /plugins/side-to-side.js: -------------------------------------------------------------------------------- 1 | // XXX: make this a plugin itself? 2 | var sideToSide = function(fromX, toX) { 3 | return function(options) { 4 | options = _.extend({ 5 | duration: 500, 6 | easing: 'ease-in-out' 7 | }, options); 8 | 9 | return { 10 | insertElement: function(node, next, done) { 11 | var $node = $(node); 12 | 13 | $node 14 | .css('transform', 'translateX(' + fromX + ')') 15 | .insertBefore(next) 16 | .velocity({ 17 | translateX: [0, fromX] 18 | }, { 19 | easing: options.easing, 20 | duration: options.duration, 21 | queue: false, 22 | complete: function() { 23 | $node.css('transform', ''); 24 | done(); 25 | } 26 | }); 27 | }, 28 | removeElement: function(node, done) { 29 | var $node = $(node); 30 | 31 | $node 32 | .velocity({ 33 | translateX: [toX] 34 | }, { 35 | duration: options.duration, 36 | easing: options.easing, 37 | complete: function() { 38 | $node.remove(); 39 | done(); 40 | } 41 | }); 42 | } 43 | } 44 | } 45 | } 46 | 47 | Momentum.registerPlugin('right-to-left', sideToSide('100%', '-100%')); 48 | Momentum.registerPlugin('left-to-right', sideToSide('-100%', '100%')); 49 | 50 | -------------------------------------------------------------------------------- /plugins/slide-height.js: -------------------------------------------------------------------------------- 1 | Momentum.registerPlugin('slide-height', function(options) { 2 | options = _.extend({ 3 | duration: 500, 4 | easing: 'ease-in-out' 5 | }, options); 6 | 7 | return { 8 | insertElement: function(node, next, done) { 9 | var $node = $(node); 10 | 11 | $node 12 | .insertBefore(next) 13 | .css('height', $node.height()) // set explicit height 14 | .velocity('slideDown', { 15 | easing: options.easing, 16 | duration: options.duration, 17 | queue: false, 18 | complete: function() { 19 | $node.css('height', ''); // remove explicit height 20 | done(); 21 | } 22 | }); 23 | }, 24 | removeElement: function(node, done) { 25 | var $node = $(node); 26 | 27 | $node.velocity('slideUp', { 28 | easing: options.easing, 29 | duration: options.duration, 30 | complete: function() { 31 | $node.remove(); 32 | done(); 33 | } 34 | }); 35 | } 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /plugins/velocity.js: -------------------------------------------------------------------------------- 1 | // WIP: Currently, this plugin is just a testing-ground 2 | 3 | Momentum.registerPlugin('velocity', function(options) { 4 | return { 5 | insertElement: function(node, next) { 6 | $(node) 7 | .insertBefore(next) 8 | // .velocity("fadeIn", { duration: 500 }); 9 | .velocity("transition.slideLeftIn", { duration: 500 }); 10 | }, 11 | moveElement: function(node, next) { 12 | this.removeElement(node); 13 | this.insertElement(node, next); 14 | }, 15 | removeElement: function(node) { 16 | $(node) 17 | .velocity("transition.slideLeftOut", { 18 | // .velocity("fadeOut", { 19 | duration: 500, 20 | complete: function() { 21 | $(node).remove() 22 | } 23 | }); 24 | } 25 | } 26 | }); 27 | 28 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "base64", 5 | "1.0.1" 6 | ], 7 | [ 8 | "blaze", 9 | "2.0.3" 10 | ], 11 | [ 12 | "check", 13 | "1.0.2" 14 | ], 15 | [ 16 | "deps", 17 | "1.0.5" 18 | ], 19 | [ 20 | "ejson", 21 | "1.0.4" 22 | ], 23 | [ 24 | "geojson-utils", 25 | "1.0.1" 26 | ], 27 | [ 28 | "htmljs", 29 | "1.0.2" 30 | ], 31 | [ 32 | "id-map", 33 | "1.0.1" 34 | ], 35 | [ 36 | "jquery", 37 | "1.0.1" 38 | ], 39 | [ 40 | "json", 41 | "1.0.1" 42 | ], 43 | [ 44 | "meteor", 45 | "1.1.3" 46 | ], 47 | [ 48 | "minimongo", 49 | "1.0.5" 50 | ], 51 | [ 52 | "observe-sequence", 53 | "1.0.3" 54 | ], 55 | [ 56 | "ordered-dict", 57 | "1.0.1" 58 | ], 59 | [ 60 | "random", 61 | "1.0.1" 62 | ], 63 | [ 64 | "reactive-var", 65 | "1.0.3" 66 | ], 67 | [ 68 | "templating", 69 | "1.0.9" 70 | ], 71 | [ 72 | "tracker", 73 | "1.0.3" 74 | ], 75 | [ 76 | "underscore", 77 | "1.0.1" 78 | ], 79 | [ 80 | "velocityjs:velocityjs", 81 | "1.2.1" 82 | ] 83 | ], 84 | "pluginDependencies": [], 85 | "toolVersion": "meteor-tool@1.0.35", 86 | "format": "1.0" 87 | } --------------------------------------------------------------------------------