Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adds login overlay as async dialog

This showcases the performAsync capabilities of sproutcore-statechart to handle animations in the ui.
  • Loading branch information...
commit 23b5eb75a6a07bcca97e034c9bc842fa0147fe03 1 parent 33134d3
Dominik Guzei authored
View
37 app/assets/javascripts/app/states/AuthenticationState.js.coffee
@@ -1,5 +1,6 @@
#= require app/mediators/authenticationMediator
+#= require app/views/LoginOverlayView
App = @App
@@ -9,6 +10,9 @@ App.states.AuthenticationState = SC.State.extend {
initialSubstate: 'LoggedOut'
+ logout: ->
+ @gotoState 'Authentication.LoggedOut'
+
LoggedOut: SC.State.extend {
enterState: ->
@@ -16,13 +20,41 @@ App.states.AuthenticationState = SC.State.extend {
authenticationMediator.set 'userName', null
authenticationMediator.set 'password', null
- authenticate: (userName, password) ->
+ login: ->
+ @gotoState 'Authentication.Authenticate'
+ }
+
+ Authenticate: SC.State.extend {
+
+ enterState: ->
+ unless App.views.loginOverlayView
+ App.views.loginOverlayView = App.views.LoginOverlayView.create()
+
+ @performAsync =>
+ App.views.loginOverlayView.show =>
+ @resumeGotoState()
+
+ exitState: ->
+ # reset notifications
+ App.mediators.authenticationMediator.set('notice', '')
+ App.mediators.authenticationMediator.set('error', '')
+
+ @performAsync =>
+ App.views.loginOverlayView.hide =>
+ @resumeGotoState()
+
+ authenticate: ((action, userName, password) ->
if userName is 'user' and password is 'password'
authenticationMediator.set 'userName', userName
authenticationMediator.set 'password', password
@gotoState 'Authentication.LoggedIn'
else
authenticationMediator.set 'error', 'username or password are wrong.'
+ ).handleActions('authenticate', 'loginFormSubmitted')
+
+ loginFormCancelled: ->
+ @get('statechart').sendAction 'logout'
+
}
LoggedIn: SC.State.extend {
@@ -31,8 +63,5 @@ App.states.AuthenticationState = SC.State.extend {
context = App.controllers.statechartController.get 'lastRouteContext'
if context then context.retry()
-
- logout: ->
- @gotoState 'Authentication.LoggedOut'
}
}
View
27 app/assets/javascripts/app/states/BrowsingState.js.coffee
@@ -2,7 +2,6 @@
#= require app/mediators/pagesMediator
#= require app/views/pages/ListNotesView
#= require app/views/pages/CreateNoteView
-#= require app/views/pages/LoginView
App = @App
pagesMediator = App.mediators.pagesMediator
@@ -12,6 +11,9 @@ App.states.BrowsingState = SC.State.extend {
initialSubstate: 'Public'
+ logout: ->
+ @gotoState 'Browsing.Public.ListNotes'
+
Public: SC.State.extend {
initialSubstate: 'Index'
@@ -34,24 +36,6 @@ App.states.BrowsingState = SC.State.extend {
pagesMediator.set 'currentPage', pages.ListNotesView
}
-
- Login: SC.State.extend {
-
- representRoute: 'user/login'
-
- enterState: ->
- # let SC.routes run first
- SC.run.next =>
- SC.routes.set('location', 'user/login')
-
- pagesMediator.set 'currentPage', pages.LoginView
-
- loginFormSubmitted: (userName, password) ->
- App.mediators.authenticationMediator.set('notice', '')
- App.mediators.authenticationMediator.set('error', '')
- @get('statechart').send('authenticate', userName, password)
-
- }
}
Restricted: SC.State.extend {
@@ -61,7 +45,7 @@ App.states.BrowsingState = SC.State.extend {
beforeFilter: ->
unless App.mediators.authenticationMediator.get('loggedIn')
App.mediators.authenticationMediator.set('notice', 'You have no access to this page. Please login!')
- @gotoState 'Browsing.Public.Login'
+ @get('statechart').sendAction('login')
# don't trigger transition to substate
return false
@@ -69,9 +53,6 @@ App.states.BrowsingState = SC.State.extend {
# trigger transition to substate
return true
- logout: ->
- @gotoState 'Browsing.Public.ListNotes'
-
CreateNote: SC.State.extend {
representRoute: 'notes/create'
View
0  ...ascripts/app/templates/pages/login.js.hjs → ...cripts/app/templates/login_overlay.js.hjs
File renamed without changes
View
2  app/assets/javascripts/app/templates/navigation.js.hjs
@@ -5,6 +5,6 @@
{{#if loggedIn}}
<li><button type="button" {{action "logout"}}>Logout</button></li>
{{else}}
- <li><a href="#user/login">Login</a></li>
+ <li><button type="button" {{action "login"}}>Login</button></li>
{{/if}}
</ul>
View
65 app/assets/javascripts/app/views/LoginOverlayView.js.coffee
@@ -0,0 +1,65 @@
+
+#= require app/templates/login_overlay
+
+App = @App
+
+App.views.LoginOverlayView = SC.View.extend {
+
+ templateName: 'app_templates_login_overlay'
+ elementId: 'login-overlay'
+
+ mediatorBinding: 'App.mediators.authenticationMediator'
+
+ didInsertElement: ->
+ @_showAsOverlay()
+
+ login: ->
+ userName = $('#login-username').val()
+ password = $('#login-password').val()
+
+ App.statechart.send 'loginFormSubmitted', userName, password
+
+ show: (@showCallback) ->
+ @append()
+
+ hide: (@hideCallback) ->
+ # when the dialog has been cancelled it
+ # doesn't need to be closed again
+ if @$().overlay().close
+ @$().overlay().close()
+ else
+ @hideCallback()
+
+ # show the login dialog as jQuery Tools Overlay
+ # to focus the user on the task
+ _showAsOverlay: ->
+ @$().overlay {
+ top: 260
+
+ mask: {
+ color: '#ccc'
+ opacity: 0.6
+ }
+
+ load: true
+
+ onLoad: =>
+ @showCallback()
+
+ onClose: => @_overlayClosed()
+ }
+
+ _overlayClosed: ->
+ @remove()
+
+ # When the user closes the dialog without
+ # logging in tell the statechart
+ if @hideCallback
+ @hideCallback()
+ else
+ App.statechart.send 'loginFormCancelled'
+
+ @hideCallback = null
+
+
+}
View
3  app/assets/javascripts/app/views/NavigationView.js.coffee
@@ -13,4 +13,7 @@ App.views.NavigationView = SC.View.extend {
logout: ->
App.statechart.send 'logout'
+ login: ->
+ App.statechart.send 'login'
+
}
View
19 app/assets/javascripts/app/views/pages/LoginView.js.coffee
@@ -1,19 +0,0 @@
-
-#= require app/templates/pages/login
-
-App = @App
-
-App.views.pages.LoginView = SC.View.extend {
-
- templateName: 'app_templates_pages_login'
- elementId: 'login-page'
-
- mediatorBinding: 'App.mediators.authenticationMediator'
-
- login: ->
- userName = $('#login-username').val()
- password = $('#login-password').val()
-
- App.statechart.send 'loginFormSubmitted', userName, password
-
-}
View
1  app/assets/javascripts/application.js
@@ -15,6 +15,7 @@
//= require lib/ember
//= require lib/sproutcore-routing
//= require lib/sproutcore-statechart
+//= require lib/jquery.tools.overlay.min
//= require app/App
//= require app/statechart
View
13 app/assets/javascripts/lib/jquery.tools.overlay.min.js
@@ -0,0 +1,13 @@
+/*!
+ * jQuery Tools v1.2.6 - The missing UI library for the Web
+ *
+ * overlay/overlay.js
+ * toolbox/toolbox.expose.js
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/
+ *
+ */
+(function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.overlay={addEffect:function(a,b,d){c[a]=[b,d]},conf:{close:null,closeOnClick:!0,closeOnEsc:!0,closeSpeed:"fast",effect:"default",fixed:!a.browser.msie||a.browser.version>6,left:"center",load:!1,mask:null,oneInstance:!0,speed:"normal",target:null,top:"10%"}};var b=[],c={};a.tools.overlay.addEffect("default",function(b,c){var d=this.getConf(),e=a(window);d.fixed||(b.top+=e.scrollTop(),b.left+=e.scrollLeft()),b.position=d.fixed?"fixed":"absolute",this.getOverlay().css(b).fadeIn(d.speed,c)},function(a){this.getOverlay().fadeOut(this.getConf().closeSpeed,a)});function d(d,e){var f=this,g=d.add(f),h=a(window),i,j,k,l=a.tools.expose&&(e.mask||e.expose),m=Math.random().toString().slice(10);l&&(typeof l=="string"&&(l={color:l}),l.closeOnClick=l.closeOnEsc=!1);var n=e.target||d.attr("rel");j=n?a(n):null||d;if(!j.length)throw"Could not find Overlay: "+n;d&&d.index(j)==-1&&d.click(function(a){f.load(a);return a.preventDefault()}),a.extend(f,{load:function(d){if(f.isOpened())return f;var i=c[e.effect];if(!i)throw"Overlay: cannot find effect : \""+e.effect+"\"";e.oneInstance&&a.each(b,function(){this.close(d)}),d=d||a.Event(),d.type="onBeforeLoad",g.trigger(d);if(d.isDefaultPrevented())return f;k=!0,l&&a(j).expose(l);var n=e.top,o=e.left,p=j.outerWidth({margin:!0}),q=j.outerHeight({margin:!0});typeof n=="string"&&(n=n=="center"?Math.max((h.height()-q)/2,0):parseInt(n,10)/100*h.height()),o=="center"&&(o=Math.max((h.width()-p)/2,0)),i[0].call(f,{top:n,left:o},function(){k&&(d.type="onLoad",g.trigger(d))}),l&&e.closeOnClick&&a.mask.getMask().one("click",f.close),e.closeOnClick&&a(document).bind("click."+m,function(b){a(b.target).parents(j).length||f.close(b)}),e.closeOnEsc&&a(document).bind("keydown."+m,function(a){a.keyCode==27&&f.close(a)});return f},close:function(b){if(!f.isOpened())return f;b=b||a.Event(),b.type="onBeforeClose",g.trigger(b);if(!b.isDefaultPrevented()){k=!1,c[e.effect][1].call(f,function(){b.type="onClose",g.trigger(b)}),a(document).unbind("click."+m).unbind("keydown."+m),l&&a.mask.close();return f}},getOverlay:function(){return j},getTrigger:function(){return d},getClosers:function(){return i},isOpened:function(){return k},getConf:function(){return e}}),a.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}}),i=j.find(e.close||".close"),!i.length&&!e.close&&(i=a("<a class=\"close\"></a>"),j.prepend(i)),i.click(function(a){f.close(a)}),e.load&&f.load()}a.fn.overlay=function(c){var e=this.data("overlay");if(e)return e;a.isFunction(c)&&(c={onBeforeLoad:c}),c=a.extend(!0,{},a.tools.overlay.conf,c),this.each(function(){e=new d(a(this),c),b.push(e),a(this).data("overlay",e)});return c.api?e:this}})(jQuery);
+(function(a){a.tools=a.tools||{version:"v1.2.6"};var b;b=a.tools.expose={conf:{maskId:"exposeMask",loadSpeed:"slow",closeSpeed:"fast",closeOnClick:!0,closeOnEsc:!0,zIndex:9998,opacity:.8,startOpacity:0,color:"#fff",onLoad:null,onClose:null}};function c(){if(a.browser.msie){var b=a(document).height(),c=a(window).height();return[window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,b-c<20?c:b]}return[a(document).width(),a(document).height()]}function d(b){if(b)return b.call(a.mask)}var e,f,g,h,i;a.mask={load:function(j,k){if(g)return this;typeof j=="string"&&(j={color:j}),j=j||h,h=j=a.extend(a.extend({},b.conf),j),e=a("#"+j.maskId),e.length||(e=a("<div/>").attr("id",j.maskId),a("body").append(e));var l=c();e.css({position:"absolute",top:0,left:0,width:l[0],height:l[1],display:"none",opacity:j.startOpacity,zIndex:j.zIndex}),j.color&&e.css("backgroundColor",j.color);if(d(j.onBeforeLoad)===!1)return this;j.closeOnEsc&&a(document).bind("keydown.mask",function(b){b.keyCode==27&&a.mask.close(b)}),j.closeOnClick&&e.bind("click.mask",function(b){a.mask.close(b)}),a(window).bind("resize.mask",function(){a.mask.fit()}),k&&k.length&&(i=k.eq(0).css("zIndex"),a.each(k,function(){var b=a(this);/relative|absolute|fixed/i.test(b.css("position"))||b.css("position","relative")}),f=k.css({zIndex:Math.max(j.zIndex+1,i=="auto"?0:i)})),e.css({display:"block"}).fadeTo(j.loadSpeed,j.opacity,function(){a.mask.fit(),d(j.onLoad),g="full"}),g=!0;return this},close:function(){if(g){if(d(h.onBeforeClose)===!1)return this;e.fadeOut(h.closeSpeed,function(){d(h.onClose),f&&f.css({zIndex:i}),g=!1}),a(document).unbind("keydown.mask"),e.unbind("click.mask"),a(window).unbind("resize.mask")}return this},fit:function(){if(g){var a=c();e.css({width:a[0],height:a[1]})}},getMask:function(){return e},isLoaded:function(a){return a?g=="full":g},getConf:function(){return h},getExposed:function(){return f}},a.fn.mask=function(b){a.mask.load(b);return this},a.fn.expose=function(b){a.mask.load(b,this);return this}})(jQuery);
View
5 app/assets/stylesheets/application.css → app/assets/stylesheets/application.css.scss
@@ -11,3 +11,8 @@
*= require_self
*= require_tree .
*/
+
+#login-overlay {
+ width: 400px;
+ display: none;
+}
View
2  config/environments/production.rb
@@ -45,7 +45,7 @@
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
- # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # Precompile additional assets (application.js, application.css.scss, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
# Disable delivery errors, bad email addresses will be ignored
Please sign in to comment.
Something went wrong with that request. Please try again.