├── .gitignore ├── tests └── tests.js ├── lib ├── at_famous_form.js ├── at_famous_oauth.js ├── at_famous_oauth.html ├── full_page_at_famous_form.html ├── full_page_at_famous_form.js ├── at_pwd_form_btn.js ├── at_configure_anim.js ├── default_animations.js ├── at_famous_form.html └── famous_wrapper.js ├── README.md ├── LICENSE └── package.js /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | versions.json 3 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | 2 | // TODO: write tests!!! -------------------------------------------------------------------------------- /lib/at_famous_form.js: -------------------------------------------------------------------------------- 1 | // Uses atFamousForm template in place of the original atForm 2 | Template.atFamousForm.replaces("atForm"); -------------------------------------------------------------------------------- /lib/at_famous_oauth.js: -------------------------------------------------------------------------------- 1 | // Uses atFamousOauth template in place of the original atOauth 2 | Template.atFamousOauth.replaces("atOauth"); -------------------------------------------------------------------------------- /lib/at_famous_oauth.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /lib/full_page_at_famous_form.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/full_page_at_famous_form.js: -------------------------------------------------------------------------------- 1 | // Uses this template in place of the original atForm 2 | Template.fullPageAtFamousForm.replaces("fullPageAtForm"); 3 | 4 | Template.fullPageAtFamousForm.helpers({ 5 | colSize: function() { 6 | var winW = windowSize().width; 7 | var width; 8 | if (winW > 400) 9 | width = 400 + (winW - 400) / 10; 10 | return [ width, undefined ]; 11 | }, 12 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | useraccounts:famous-wrapper 2 | =========================== 3 | 4 | A very smart [Famo.us](http://famo.us) wrapper which uses [famous-views](https://atmospherejs.com/gadicohen/famous-views) to wrap up any version of AccountsTemplates to give you animated sign up and sign in templates straight away! 5 | 6 | You can lear how to use it reading [Wrapping Up for Famo.us](https://github.com/meteor-useraccounts/core/blob/master/Guide.md#wrapping-up-for-famo.us) chapter inside the official [Guide](https://github.com/meteor-useraccounts/core/blob/master/Guide.md) for AccountsTemplates. 7 | 8 | Lets have a look at a [live demo application](http://accounts-templates-famous-wrapper.meteor.com)! 9 | 10 | 11 | ## Bring Your Own Famo.us 12 | 13 | Adding this package with `mrt add useraccounts:famous-wrapper` does not add any other packages providing Famo.us. This is to let you choose your preferred way to include Famo.us! 14 | -------------------------------------------------------------------------------- /lib/at_pwd_form_btn.js: -------------------------------------------------------------------------------- 1 | // Register a 'rendered' callback on the submit button 2 | // to attach _uihooks to the atPwdForm 3 | // this way, every time the atPwdForm content changed 4 | // the container fview is asked to recompute its height 5 | 6 | Template.atPwdFormBtn.rendered = function(){ 7 | var fview = FView.from(this); 8 | this.firstNode.parentNode._uihooks = { 9 | // everytime something is inserted... 10 | insertElement: function (node, next) { 11 | // ...asks to recompute the height 12 | fview.autoHeight(); 13 | // then simply insert the element as required 14 | $(node).insertBefore(next); 15 | }, 16 | // everytime something is removed... 17 | removeElement: function(node) { 18 | // ...asks to recompute the height 19 | fview.autoHeight(); 20 | // then simply remove the element as required 21 | $(node).remove(); 22 | } 23 | }; 24 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 splendido 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. -------------------------------------------------------------------------------- /lib/at_configure_anim.js: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Adds configureAnimations to AccountsTemplates 3 | // -------------------------------------------------- 4 | 5 | ANIMATION_SUB_PAT = { 6 | default: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 7 | atTitle: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 8 | atSigninLink: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 9 | atSocial: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 10 | atSep: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 11 | atError: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 12 | atResult: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 13 | atMessage: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 14 | atPwdForm: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 15 | atSignupLink: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 16 | atTermsLink: Match.Optional(Match.OneOf(null, Match.Where(_.isFunction))), 17 | }; 18 | 19 | ANIMATION_PAT = { 20 | render: Match.Optional(ANIMATION_SUB_PAT), 21 | destroy: Match.Optional(ANIMATION_SUB_PAT), 22 | state_change: Match.Optional(ANIMATION_SUB_PAT), 23 | animQueueDelay: Match.Optional(Number), 24 | animQueueStartDelay: Match.Optional(Number), 25 | setStateDelay: Match.Optional(Number), 26 | }; 27 | 28 | 29 | AccountsTemplates.configureAnimations = function(options){ 30 | if (Meteor.isClient){ 31 | check(options, ANIMATION_PAT); 32 | if (options.render) 33 | this.animations.render = _.defaults(options.render, this.animations.render); 34 | if (options.destroy) 35 | this.animations.destroy = _.defaults(options.destroy, this.animations.destroy); 36 | if (options.state_change) 37 | this.animations.state_change = _.defaults(options.state_change, this.animations.state_change); 38 | options = _.omit(options, "render", "destroy", "state_change"); 39 | this.animations = _.defaults(options, this.animations); 40 | } 41 | }; -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: 'Famo.us wrapper for Accounts Templates.', 3 | version: '1.13.1', 4 | name: 'useraccounts:famous-wrapper', 5 | git: 'https://github.com/meteor-useraccounts/famous-wrapper.git', 6 | }); 7 | 8 | Package.on_use(function(api, where) { 9 | api.versionsFrom('METEOR@1.0'); 10 | 11 | api.use([ 12 | 'reactive-dict', 13 | 'templating', 14 | ], 'client'); 15 | 16 | api.use([ 17 | 'check', 18 | 'underscore', 19 | 'useraccounts:core', 20 | 'gadicohen:famous-views', 21 | 'aldeed:template-extension' 22 | ], ['client', 'server']); 23 | 24 | api.imply([ 25 | 'useraccounts:core@1.13.1', 26 | 'gadicohen:famous-views@0.1.22', 27 | 'aldeed:template-extension@3.1.1', 28 | ], ['client', 'server']); 29 | 30 | //api.use('mjnetworks:famous@0.2.2-1', ['client']/*, { weak: true }*/); 31 | 32 | api.use('useraccounts:bootstrap@1.13.1', ['client', 'server'], { 33 | weak: true 34 | }); 35 | api.use('useraccounts:foundation@1.13.1', ['client', 'server'], { 36 | weak: true 37 | }); 38 | api.use('useraccounts:ionic@1.8.1', ['client', 'server'], { 39 | weak: true 40 | }); 41 | api.use('useraccounts:ratchet@1.8.1', ['client', 'server'], { 42 | weak: true 43 | }); 44 | api.use('useraccounts:semantic-ui@1.13.1', ['client', 'server'], { 45 | weak: true 46 | }); 47 | api.use('useraccounts:unstyled@1.13.1', ['client', 'server'], { 48 | weak: true 49 | }); 50 | 51 | api.add_files([ 52 | 'lib/at_famous_form.html', 53 | 'lib/at_famous_form.js', 54 | 'lib/at_famous_oauth.html', 55 | 'lib/at_famous_oauth.js', 56 | 'lib/at_pwd_form_btn.js', 57 | 'lib/full_page_at_famous_form.html', 58 | 'lib/full_page_at_famous_form.js', 59 | 'lib/default_animations.js', 60 | 'lib/famous_wrapper.js', 61 | ], ['client']); 62 | 63 | api.add_files([ 64 | 'lib/at_configure_anim.js', 65 | ], ['client', 'server']); 66 | }); 67 | 68 | Package.on_test(function(api) { 69 | api.use([ 70 | 'useraccounts:core@1.13.1', 71 | ]); 72 | api.use(['tinytest', 'test-helpers'], ['client', 'server']); 73 | api.add_files('tests/tests.js', ['client', 'server']); 74 | }); 75 | -------------------------------------------------------------------------------- /lib/default_animations.js: -------------------------------------------------------------------------------- 1 | var Transform; 2 | var Easing; 3 | FView.ready(function(require) { 4 | Transform = famous.core.Transform; 5 | Easing = famous.transitions.Easing; 6 | }); 7 | 8 | 9 | // --------------------------------------- 10 | // Default animation function for entrance 11 | // --------------------------------------- 12 | 13 | fallFromTop = function(fview){ 14 | fview.modifier.setTransform(Transform.translate(0, -$(window).height())); 15 | AccountsTemplates.pushToAnimationQueue(function() { 16 | fview.modifier.setTransform( 17 | Transform.translate(0,0), 18 | { duration : 450, curve: Easing.easeOutSine } 19 | ); 20 | }, false); 21 | }; 22 | 23 | 24 | // ------------------------------------------- 25 | // Default animation function for state change 26 | // ------------------------------------------- 27 | 28 | vFlip = function(fview){ 29 | fview.modifier.setTransform( 30 | Transform.rotate(Math.PI-0.05,0,0), 31 | { duration : AccountsTemplates.animations.setStateDelay, curve: "easeIn" }, 32 | function() { 33 | fview.modifier.setTransform( 34 | Transform.rotate(-0.1,0,0), 35 | { duration : AccountsTemplates.animations.setStateDelay, curve: "easeOut" } 36 | ); 37 | } 38 | ); 39 | }; 40 | 41 | 42 | // ------------------------------------------- 43 | // Default animation functions for destruction 44 | // ------------------------------------------- 45 | 46 | slideRightDestroy = function(fview){ 47 | fview.modifier.setTransform( 48 | Transform.translate($(window).width(),0), 49 | { duration : 250, curve: Easing.easeOutSine }, 50 | function() { fview.destroy();} 51 | ); 52 | }; 53 | 54 | blastOutDestroy = function(fview){ 55 | var angle = Math.random() * Math.PI; 56 | fview.modifier.setTransform( 57 | Transform.multiply( 58 | Transform.translate( Math.cos(angle) * 1.5 * $(window).height(), Math.sin(angle) * 1.5 * $(window).width()), 59 | Transform.aboutOrigin([200, 21, 0], Transform.rotate(0,0, Math.random() > 0.5 ? Math.PI : -Math.PI)) 60 | ), 61 | { duration : 400, curve: Easing.easeOutCirc}, 62 | function() { fview.destroy();} 63 | ); 64 | }; -------------------------------------------------------------------------------- /lib/at_famous_form.html: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /lib/famous_wrapper.js: -------------------------------------------------------------------------------- 1 | 2 | // --------------------------------------------------- 3 | // Adds to AccountsTemplates a list of templates to be 4 | // rendered within surfaces with corresponding helpers 5 | // from atForm to be used to determine whether the 6 | // template is visible or not 7 | // --------------------------------------------------- 8 | 9 | AccountsTemplates.surfaces = { 10 | atTitle: AccountsTemplates.atFormHelpers.showTitle, 11 | atSigninLink: AccountsTemplates.atFormHelpers.showSignInLink, 12 | atSocial: AccountsTemplates.atFormHelpers.showOauthServices, 13 | atSep: AccountsTemplates.atFormHelpers.showServicesSeparator, 14 | atError: AccountsTemplates.atFormHelpers.showError, 15 | atResult: AccountsTemplates.atFormHelpers.showResult, 16 | atMessage: AccountsTemplates.atFormHelpers.showMessage, 17 | atPwdForm: AccountsTemplates.atFormHelpers.showPwdForm, 18 | atSignupLink: AccountsTemplates.atFormHelpers.showSignUpLink, 19 | atTermsLink: AccountsTemplates.atFormHelpers.showTermsLink, 20 | }; 21 | 22 | 23 | // ----------------------------------------------------- 24 | // Adds an object to keep track of rendered famous views 25 | // ----------------------------------------------------- 26 | 27 | AccountsTemplates.fviews = {}; 28 | 29 | 30 | // -------------------------------------------- 31 | // Adds an animation queue to AccountsTemplates 32 | // -------------------------------------------- 33 | 34 | AccountsTemplates.animationQueue = []; 35 | 36 | AccountsTemplates.nextAnimation = function(){ 37 | var aq = this.animationQueue; 38 | if (aq.length){ 39 | aq.shift()(); 40 | if (aq.length) 41 | Meteor.setTimeout(function(){AccountsTemplates.nextAnimation();}, this.animations.animQueueDelay); 42 | } 43 | }; 44 | 45 | AccountsTemplates.pushToAnimationQueue = function(func, at_begin){ 46 | var aq = this.animationQueue; 47 | var firstAnim = !aq.length; 48 | if (at_begin === true) 49 | aq.unshift(func); 50 | else 51 | aq.push(func); 52 | if (firstAnim) 53 | Meteor.setTimeout(function(){AccountsTemplates.nextAnimation();}, this.animations.animQueueStartDelay); 54 | }; 55 | 56 | // ---------------------------------------------------------- 57 | // Adds default animation configurations to AccountsTemplates 58 | // ---------------------------------------------------------- 59 | 60 | AccountsTemplates.animations = { 61 | render: { 62 | default: fallFromTop, 63 | }, 64 | destroy: { 65 | //default: spinOutDestroy, 66 | default: slideRightDestroy, 67 | atSocial: blastOutDestroy, 68 | }, 69 | state_change: { 70 | default: vFlip, 71 | }, 72 | animQueueDelay: 150, // milliseconds 73 | animQueueStartDelay: 200, // milliseconds 74 | setStateDelay: 300, // milliseconds 75 | }; 76 | 77 | 78 | // ---------------------------------------------- 79 | // Add the animate functions to AccountsTemplates 80 | // ---------------------------------------------- 81 | 82 | AccountsTemplates.animate = function(fview, tmplt, kind){ 83 | // Retrieves configured animations for 'kind' 84 | // (which can be 'render', 'destroy', 'state_change') 85 | var anims = this.animations[kind]; 86 | // Takes the animation for the specified 'tmplt' 87 | // or falls back to the 'default' one (if any...) 88 | var animFunc = anims[tmplt] || anims.default; 89 | // Triggers the animation only in case one was found... 90 | if (animFunc) 91 | animFunc(fview); 92 | else if(kind === 'destroy') 93 | fview.destroy(); 94 | }; 95 | 96 | 97 | // ----------------------------------------------------------------------------------------- 98 | // Overrides the original setState method to be able to trigger switchState renderAnimations 99 | // ----------------------------------------------------------------------------------------- 100 | 101 | var originalSetState = AccountsTemplates.setState; 102 | AccountsTemplates.setState = function(state, callback) { 103 | Tracker.nonreactive(function(){ 104 | var delay = false; 105 | 106 | // Goes through each rendered surface... 107 | _.each(AccountsTemplates.fviews, function(tmplt, fview_id){ 108 | var fview = FView.byId(fview_id); 109 | var visible = AccountsTemplates.surfaces[tmplt]; 110 | // ...to check whether it is already visible and will 111 | // still be visible in next state... 112 | if (fview && visible() && visible(state)){ 113 | // ...so to trigger a state change animation. 114 | AccountsTemplates.animate(fview, tmplt, 'state_change'); 115 | delay = true; 116 | } 117 | }); 118 | 119 | // Calls the original setState method that AccountsTemplates overrides 120 | if (delay) 121 | // Applies the new state after a certain delay only 122 | // in case there is some state change animation to perform... 123 | Meteor.setTimeout(function(){ 124 | originalSetState.call(AccountsTemplates, state, callback); 125 | }, AccountsTemplates.animations.setStateDelay); 126 | else 127 | // ...otherwise changes state straight away! 128 | originalSetState.call(AccountsTemplates, state, callback); 129 | }); 130 | }; 131 | 132 | 133 | // Associates to each template to be rendered within a Surface 134 | // a 'rendered' function which triggers the animation for the 135 | // entrance and register another callback to trigger another 136 | // animation on destruction... 137 | 138 | _.each(_.keys(AccountsTemplates.surfaces), function(tmplt){ 139 | Template[tmplt].rendered = function(){ 140 | var fview = FView.from(this); 141 | // Register the fview among rendered ones... 142 | AccountsTemplates.fviews[fview.id] = tmplt; 143 | // Trigger entrance animation 144 | AccountsTemplates.animate(fview, tmplt, 'render'); 145 | // Asks not to destroy the surface right away... 146 | fview.preventDestroy(); 147 | // ...and register a destroy callback... 148 | fview.onDestroy = function(){ 149 | // ...to trigger destroy animation! 150 | AccountsTemplates.animate(fview, tmplt, 'destroy'); 151 | // Finally de-register the surface from the list of rendered ones 152 | delete AccountsTemplates.fviews[fview.id]; 153 | }; 154 | }; 155 | }); 156 | --------------------------------------------------------------------------------