ende 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/README.md +29 -0
  5. data/Rakefile +1 -0
  6. data/WTFP-LICENSE +13 -0
  7. data/component.json +31 -0
  8. data/ende.gemspec +23 -0
  9. data/lib/assets/.gitkeep +0 -0
  10. data/lib/assets/javascripts/aura/extensions/devise.js.coffee +110 -0
  11. data/lib/assets/javascripts/aura/extensions/loader.js.coffee +48 -0
  12. data/lib/assets/javascripts/aura/extensions/mediator.js +98 -0
  13. data/lib/assets/javascripts/aura/extensions/models.js.coffee.erb +56 -0
  14. data/lib/assets/javascripts/aura/extensions/rivets.js.coffee +251 -0
  15. data/lib/assets/javascripts/aura/extensions/states.js.coffee +62 -0
  16. data/lib/assets/javascripts/aura/extensions/widget/eventable.js.coffee +66 -0
  17. data/lib/assets/javascripts/aura/extensions/widget/lifecycleable.js.coffee +75 -0
  18. data/lib/assets/javascripts/config/initializers/jquery.js.coffee +6 -0
  19. data/lib/assets/javascripts/config/initializers/load_components.js.coffee +73 -0
  20. data/lib/assets/javascripts/config/initializers/requirejs.js.coffee +3 -0
  21. data/lib/assets/javascripts/ende.js.coffee +1 -0
  22. data/lib/assets/javascripts/widgets/authenticator/main.js.coffee +95 -0
  23. data/lib/assets/javascripts/widgets/authenticator/presenter.js.coffee +35 -0
  24. data/lib/assets/javascripts/widgets/authenticator/states/default.html +7 -0
  25. data/lib/assets/javascripts/widgets/authenticator/states/index.js.coffee +3 -0
  26. data/lib/assets/javascripts/widgets/authenticator/states/passwords.html +6 -0
  27. data/lib/assets/javascripts/widgets/list/main.js.coffee +82 -0
  28. data/lib/assets/javascripts/widgets/list/presenter.js.coffee +34 -0
  29. data/lib/assets/javascripts/widgets/list/states/default.html +4 -0
  30. data/lib/assets/javascripts/widgets/list/states/index.js.coffee +2 -0
  31. data/lib/assets/javascripts/widgets/viewer/main.js.coffee +139 -0
  32. data/lib/assets/javascripts/widgets/viewer/presenter.js.coffee +54 -0
  33. data/lib/assets/javascripts/widgets/viewer/states/default.html +6 -0
  34. data/lib/assets/javascripts/widgets/viewer/states/index.js.coffee +2 -0
  35. data/lib/assets/stylesheets/application/modules/authenticator.css.styl +7 -0
  36. data/lib/assets/stylesheets/application/modules/widgets/structure/list.css.styl +9 -0
  37. data/lib/assets/stylesheets/application/modules/widgets/structure/viewer.css.styl +21 -0
  38. data/lib/assets/stylesheets/application/modules/widgets/structure/widget.css.styl +5 -0
  39. data/lib/assets/stylesheets/filter.styl +10 -0
  40. data/lib/assets/stylesheets/helpers/button.styl +61 -0
  41. data/lib/assets/stylesheets/helpers/general.styl +36 -0
  42. data/lib/assets/stylesheets/helpers/index.styl +4 -0
  43. data/lib/assets/stylesheets/helpers/link.styl +6 -0
  44. data/lib/assets/stylesheets/helpers/url.styl +5 -0
  45. data/lib/assets/stylesheets/modules/button.styl +42 -0
  46. data/lib/assets/stylesheets/sprite.styl +40 -0
  47. data/lib/assets/stylesheets/ssprites.styl +1 -0
  48. data/lib/ende/version.rb +3 -0
  49. data/lib/ende.rb +21 -0
  50. data/lib/tasks/.gitkeep +0 -0
  51. data/lib/tasks/component.thor +63 -0
  52. data/lib/tasks/sprite.thor +62 -0
  53. data/vendor/assets/javascripts/stampit/stampit.js +392 -0
  54. metadata +126 -0
@@ -0,0 +1,251 @@
1
+ define 'aura/extensions/rivets', ->
2
+
3
+ 'use strict';
4
+
5
+ extend = require 'segmentio-extend'
6
+
7
+ rivets = require 'mikeric-rivets/dist/rivets'
8
+
9
+ Rivets = rivets._
10
+
11
+ observable_configuration = require 'indefinido-observable/lib/adapters/rivets'
12
+
13
+ rivets.configure observable_configuration
14
+ rivets.configure
15
+ templateDelimiters: ['{{', '}}']
16
+
17
+ # Custom rivets view because we don't want to prefix attributes
18
+ # Rivets.View
19
+ # -----------
20
+ # A collection of bindings built from a set of parent nodes.
21
+ class Rivets.View
22
+ # The DOM elements and the model objects for binding are passed into the
23
+ # constructor along with any local options that should be used throughout the
24
+ # context of the view and it's bindings.
25
+ constructor: (@els, @models, @options = {}) ->
26
+ @els = [@els] unless (@els.jquery || @els instanceof Array)
27
+
28
+ for option in ['config', 'binders', 'formatters']
29
+ @[option] = {}
30
+ @[option][k] = v for k, v of @options[option] if @options[option]
31
+ @[option][k] ?= v for k, v of Rivets[option]
32
+
33
+ @build()
34
+
35
+ # Regular expression used to match binding attributes.
36
+ bindingRegExp: =>
37
+ prefix = @config.prefix
38
+ if prefix then new RegExp("^data-#{prefix}-") else /^data-/
39
+
40
+ # Regular expression used to match component nodes.
41
+ componentRegExp: =>
42
+ new RegExp "^#{@config.prefix?.toUpperCase() ? 'RV'}-"
43
+
44
+ # Parses the DOM tree and builds `Rivets.Binding` instances for every matched
45
+ # binding declaration.
46
+ build: =>
47
+ @bindings = []
48
+ skipNodes = []
49
+ bindingRegExp = @bindingRegExp()
50
+ componentRegExp = @componentRegExp()
51
+
52
+
53
+ buildBinding = (binding, node, type, declaration) =>
54
+ options = {}
55
+
56
+ pipes = (pipe.trim() for pipe in declaration.split '|')
57
+ context = (ctx.trim() for ctx in pipes.shift().split '<')
58
+ path = context.shift()
59
+ splitPath = path.split /\.|:/
60
+ options.formatters = pipes
61
+ options.bypass = path.indexOf(':') != -1
62
+
63
+ if splitPath[0]
64
+ key = splitPath.shift()
65
+ else
66
+ key = null
67
+ splitPath.shift()
68
+
69
+ keypath = splitPath.join '.'
70
+
71
+ if dependencies = context.shift()
72
+ options.dependencies = dependencies.split /\s+/
73
+
74
+ if @models[key]
75
+ @bindings.push new Rivets[binding] @, node, type, key, keypath, options
76
+ else
77
+ console.warn "Model with key '#{key}' not found for binding of type '#{type}' on keypath '#{keypath}'.", @
78
+
79
+ parse = (node) =>
80
+ unless node in skipNodes
81
+ if node.nodeType is Node.TEXT_NODE
82
+ parser = Rivets.TextTemplateParser
83
+
84
+ if delimiters = @config.templateDelimiters
85
+ if (tokens = parser.parse(node.data, delimiters)).length
86
+ unless tokens.length is 1 and tokens[0].type is parser.types.text
87
+ [startToken, restTokens...] = tokens
88
+ node.data = startToken.value
89
+
90
+ if startToken.type is 0
91
+ node.data = startToken.value
92
+ else
93
+ buildBinding 'TextBinding', node, null, startToken.value
94
+
95
+ for token in restTokens
96
+ text = document.createTextNode token.value
97
+ node.parentNode.appendChild text
98
+
99
+ if token.type is 1
100
+ buildBinding 'TextBinding', text, null, token.value
101
+ else if componentRegExp.test node.tagName
102
+ type = node.tagName.replace(componentRegExp, '').toLowerCase()
103
+ @bindings.push new Rivets.ComponentBinding @, node, type
104
+
105
+ else if node.attributes?
106
+ for attribute in node.attributes
107
+ if bindingRegExp.test attribute.name
108
+ type = attribute.name.replace bindingRegExp, ''
109
+ unless binder = @binders[type]
110
+ for identifier, value of @binders
111
+ if identifier isnt '*' and identifier.indexOf('*') isnt -1
112
+ regexp = new RegExp "^#{identifier.replace('*', '.+')}$"
113
+ if regexp.test type
114
+ binder = value
115
+
116
+ binder or= @binders['*']
117
+
118
+ if binder.block
119
+ skipNodes.push n for n in node.childNodes
120
+ attributes = [attribute]
121
+
122
+ for attribute in attributes or node.attributes
123
+ if bindingRegExp.test attribute.name
124
+ type = attribute.name.replace bindingRegExp, ''
125
+ buildBinding 'Binding', node, type, attribute.value
126
+
127
+ parse childNode for childNode in node.childNodes
128
+
129
+ parse el for el in @els
130
+
131
+ return
132
+
133
+ # Returns an array of bindings where the supplied function evaluates to true.
134
+ select: (fn) =>
135
+ binding for binding in @bindings when fn binding
136
+
137
+ # Binds all of the current bindings for this view.
138
+ bind: =>
139
+ binding.bind() for binding in @bindings
140
+
141
+ # Unbinds all of the current bindings for this view.
142
+ unbind: =>
143
+ binding.unbind() for binding in @bindings
144
+
145
+ # Syncs up the view with the model by running the routines on all bindings.
146
+ sync: =>
147
+ binding.sync() for binding in @bindings
148
+
149
+ # Publishes the input values from the view back to the model (reverse sync).
150
+ publish: =>
151
+ binding.publish() for binding in @select (b) -> b.binder.publishes
152
+
153
+ # Updates the view's models along with any affected bindings.
154
+ update: (models = {}) =>
155
+ @models[key] = model for key, model of models
156
+ binding.update models for binding in @bindings
157
+
158
+
159
+ # Treat backspace, enter and other case
160
+ rivets.binders.spell ||=
161
+ publishes: true
162
+ bind: (el) ->
163
+
164
+ @options.publisher ||= (event) =>
165
+ value = Rivets.Util.getInputValue @el
166
+
167
+ # TODO more controllable enter handling
168
+ return if event.which == 13
169
+
170
+ value += String.fromCharCode event.which || event.keyCode || event.charCode
171
+
172
+ for formatter in @formatters.slice(0).reverse()
173
+ args = formatter.split /\s+/
174
+ id = args.shift()
175
+
176
+ if @view.formatters[id]?.publish
177
+ value = @view.formatters[id].publish value, args...
178
+
179
+ @view.config.adapter.publish @model, @keypath, value
180
+ event.preventDefault()
181
+
182
+ if window.jQuery?
183
+ # TODO Rivets.Util.bindEvent el, 'keypress change', @options.publisher
184
+ Rivets.Util.bindEvent el, 'keypress', @options.publisher
185
+ Rivets.Util.bindEvent el, 'change' , @publish
186
+ else
187
+ Rivets.Util.bindEvent el, 'keypress', @options.publisher
188
+ Rivets.Util.bindEvent el, 'change' , @publish
189
+
190
+ unbind: (el) ->
191
+
192
+ if window.jQuery?
193
+ # TODO Rivets.Util.unbindEvent el, 'keypress change', @options.publisher
194
+ Rivets.Util.unbindEvent el, 'keypress', @options.publisher
195
+ Rivets.Util.unbindEvent el, 'change', @publish
196
+ else
197
+ # TODO Rivets.Util.unbindEvent el, 'change' , @options.publisher
198
+ Rivets.Util.unbindEvent el, 'keypress', @options.publisher
199
+ Rivets.Util.unbindEvent el, 'change', @publish
200
+
201
+ routine: (el, value) ->
202
+ if window.jQuery?
203
+ el = jQuery el
204
+
205
+ if value?.toString() isnt el.val()?.toString()
206
+ el.val if value? then value else ''
207
+ else
208
+
209
+ if el.type is 'select-multiple'
210
+ o.selected = o.value in value for o in el if value?
211
+ else if value?.toString() isnt el.value?.toString()
212
+ el.value = if value? then value else ''
213
+
214
+
215
+
216
+ rivets.formatters.float = (value) ->
217
+ throw new TypeError "Invalid value passed to float formatter: #{value}" unless value?
218
+
219
+ # Blank value and impossible to convert to string
220
+ (!value || !(value + '')) && (value = 0)
221
+
222
+ # Force getter reading on IE
223
+ value = parseFloat value + ''
224
+
225
+ # Handle NaN
226
+ (isNaN(value)) && (value = 0)
227
+
228
+ # Format value
229
+ value.toFixed(2).toString().replace '.', ','
230
+
231
+ rivets.formatters.currency = (value) ->
232
+ 'R$ ' + rivets.formatters.float value
233
+
234
+
235
+ (application) ->
236
+
237
+ initialize: (application) ->
238
+
239
+ extend application.sandbox,
240
+ view: rivets
241
+
242
+ extend application.core.Widgets.Base.prototype,
243
+ bind: (presentation, options) ->
244
+ if presentation.presented
245
+ presented = presentation.presented
246
+ delete presentation.presented
247
+
248
+ @view = rivets.bind @$el, presentation, options
249
+
250
+ presented(@view) if presented?
251
+
@@ -0,0 +1,62 @@
1
+ define 'aura/extensions/states', ['application/states'], (states) ->
2
+
3
+ 'use strict'
4
+
5
+ (application) ->
6
+ core = application.core
7
+ logger = application.logger
8
+ mediator = core.mediator
9
+ _ = core.util._
10
+
11
+
12
+ state =
13
+ current: 'default'
14
+ list: []
15
+ previous: null
16
+ change: (transition) ->
17
+ if state.current != transition.to
18
+ from = core.state
19
+ to = if transition.to == 'previous' then state.previous else transition.to
20
+ state.previous = state.current
21
+ state.current = to
22
+ mediator.emit 'state.changed', to: to, from: from
23
+ else
24
+ mediator.emit 'state.errored', to: transition.to, message: 'Application already in this state!'
25
+ changed: (transition) ->
26
+ core.dom.find('html').addClass(transition.to).removeClass(transition.from)
27
+
28
+ # Set default intial state
29
+ core.dom.find('html').addClass state.current
30
+
31
+ # Application flow control
32
+ flow =
33
+
34
+ changed: (transition) ->
35
+ widget_configurations = states[transition.to]
36
+ if widget_configurations
37
+ widgets = []
38
+
39
+ for name, options of widget_configurations
40
+ widgets.push
41
+ name: options.name || name
42
+ options: options
43
+
44
+ delete options.name
45
+
46
+ core.inject(widgets).fail flow.failed
47
+
48
+ delete states[transition.to]
49
+ failed: (exception) ->
50
+ logger.error "states.flow.failed: Failed autostarting widget #{@} \n Message: #{exception.message}", exception
51
+
52
+
53
+ initialize: (application) ->
54
+ mediator.on 'state.change' , state.change
55
+ mediator.on 'state.changed', state.changed
56
+
57
+ # TODO load widgets before state.changed, load on state.change
58
+ mediator.on 'state.changed', flow.changed
59
+
60
+ Object.defineProperty core, 'state',
61
+ set: (to) -> state.change to: to
62
+ get: -> state.current
@@ -0,0 +1,66 @@
1
+ define 'aura/extensions/widget/eventable', ->
2
+
3
+ 'use strict';
4
+
5
+ extend = require 'segmentio-extend'
6
+
7
+ extractor = /.*?\$(.*?)@(.*?)\+(.*?)/
8
+
9
+ translations = new Map()
10
+ translations.set 'transition.end',
11
+ 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend'
12
+
13
+ translations.set 'transition.start',
14
+ 'webkitTransitionStart otransitionstart oTransitionStart msTransitionStart transitionstart'
15
+
16
+ translations.set 'animation.end',
17
+ 'webkitAnimationEnd oanimationend oAnimationEnd msAnimationEnd animationend'
18
+
19
+ translations.set 'animation.start',
20
+ 'webkitAnimationStart oanimationstart oAnimationStart msAnimationStart animationstart'
21
+
22
+
23
+ create_handler = (widget, event_name) ->
24
+ (event) ->
25
+ widget.sandbox.emit "#{widget.name}.#{widget.identifier}.#{event_name}ed", @
26
+ event.preventDefault()
27
+ false
28
+
29
+
30
+ (application) ->
31
+
32
+ initialize: (application) ->
33
+
34
+ extend application.core.Widgets.Base.prototype,
35
+ # TODO implement rivets compatibility, instead of generic binding events, alter html
36
+ handles: (event_name, widget_event_name = event_name, selector = @$el) ->
37
+ unless @name
38
+ message = "Widget name must be provided in order to use handlers, but this.name is '#{@name}' \n"
39
+ message = "Also you may have forgotten to set the type of your widget to 'Base'"
40
+ throw message
41
+
42
+ context = @$el unless selector == @$el
43
+
44
+ event_name = translations.get(event_name) ? event_name
45
+
46
+ @sandbox.dom.find(selector, context).on event_name, create_handler(@, widget_event_name || event_name)
47
+
48
+ parent = application.core.Widgets.Base
49
+
50
+ # TODO pass this extensions to the identifiable extension
51
+ eventableize = (options) ->
52
+ matches = extractor.exec options._ref
53
+ @name = matches[1]
54
+ @identifier = options.resource ? matches[2]
55
+
56
+ parent.call @, options
57
+
58
+ @sandbox.identifier = @identifier
59
+
60
+ @
61
+
62
+ eventableize.prototype = new parent _ref: ''
63
+ extend eventableize, parent
64
+ application.core.Widgets.Base = eventableize
65
+
66
+
@@ -0,0 +1,75 @@
1
+ define 'aura/extensions/widget/lifecycleable', ->
2
+
3
+ 'use strict'
4
+
5
+ # TODO Remove jquery and use core dependencies
6
+ jQuery = require 'jquery'
7
+
8
+ lifecycleable =
9
+ injection: (definition) ->
10
+ options = definition.options
11
+
12
+ # TODO check for existing widgets before convert options, and
13
+ # not only if type is object
14
+ for subwidget_name, suboptions of options
15
+ # TODO if isWidget subwidget_name
16
+ if $.type(suboptions) == 'object'
17
+
18
+ for name, suboption of suboptions
19
+ options["#{subwidget_name}#{@capitalize name}"] = suboption
20
+
21
+ # TODO delete options[subwidget_name]
22
+
23
+ ref = definition.name.split "@"
24
+ widgetName = @decamelize ref[0]
25
+ widgetSource = ref[1] || "default"
26
+
27
+ requireContext = require.s.contexts._
28
+ widgetsPath = @sources[widgetSource] || "widgets"
29
+
30
+ # Register the widget a s requirejs package...
31
+ # TODO: packages are not supported by almond, should we find another way to do this ?
32
+ options.ref = '__widget__$' + widgetName + "@" + widgetSource
33
+ options.baseUrl = widgetsPath + "/" + widgetName
34
+ options.require = options.require || {}
35
+ options.require.packages = options.require.packages || []
36
+ options.require.packages.push name: options.ref, location: widgetsPath + "/" + widgetName
37
+ options.name = widgetName;
38
+
39
+ unless options.el
40
+ options.el = jQuery '<div class="widget"></div>'
41
+ @root.append options.el
42
+
43
+ @find(options.el).attr options.attributes if options.attributes?
44
+
45
+ definition
46
+
47
+ (application) ->
48
+
49
+ initialize: (application) ->
50
+ core = application.core
51
+
52
+ # TODO use indemma inflections module instead
53
+ core.util.capitalize = (string) ->
54
+ string.charAt(0).toUpperCase() + string.slice(1);
55
+
56
+
57
+ # Cache usefull methods
58
+ lifecycleable.sources = application.config.widgets.sources
59
+ lifecycleable.find = core.dom.find
60
+ lifecycleable.root = core.dom.find app.startOptions.widgets
61
+ lifecycleable.decamelize = core.util.decamelize
62
+ lifecycleable.capitalize = core.util.capitalize
63
+
64
+
65
+ # Add injection functiono for widgets
66
+ core.inject = (name = options.name, options) ->
67
+ if jQuery.isArray name
68
+ widgets = name
69
+ injections = core.util._.map widgets, lifecycleable.injection, lifecycleable
70
+ return core.start injections
71
+
72
+ else if not name
73
+ throw new TypeError "app.core.inject: No widget name provided" unless name?
74
+
75
+ core.start = lifecycleable.injection options
@@ -0,0 +1,6 @@
1
+ # window.$ require 'jquery'
2
+ #
3
+ # window.$.ajaxSetup
4
+ # beforeSend: (xhr) ->
5
+ # token = $('meta[name="csrf-token"]').attr('content')
6
+ # xhr.setRequestHeader 'X-CSRF-Token', token
@@ -0,0 +1,73 @@
1
+ #= require require/require
2
+ #= require build
3
+
4
+ root = exports ? this
5
+
6
+ # Little object class to merge component require and requirejs require
7
+ loader =
8
+ shim: ->
9
+ # Store loaders functions
10
+ loader.loaders.component = require
11
+ loader.loaders.requirejs = requirejs
12
+ loader.activate.define = root.define
13
+
14
+ # Expand require fuction with requirejs configurations
15
+ # so we can require without great problems
16
+ loader.require.config = requirejs.config
17
+ loader.require.s = requirejs.s
18
+
19
+ initialize: ->
20
+ extend = require 'segmentio-extend'
21
+ extend loader.require, require
22
+
23
+ # Override global require for ower one
24
+ root.require = loader.require
25
+ root.loader = loader
26
+
27
+ # Resource loaders compatibility
28
+ loaders:
29
+ requirejs : null
30
+ component : null
31
+ discovered: null
32
+
33
+ discover: (params...) ->
34
+
35
+ if params[0] instanceof Array
36
+ requirer = 'requirejs'
37
+ else
38
+ requirer = 'component'
39
+
40
+ @activate (requirer) and requirer
41
+
42
+ activate: (requirer) ->
43
+ switch requirer
44
+ when 'component'
45
+ root.define = null
46
+ when 'requirejs'
47
+ root.define = @activate.define
48
+ else
49
+ false
50
+
51
+ @loaders.discovered = @loaders[requirer]
52
+
53
+ true
54
+
55
+ require: (params...) ->
56
+ using = loader.discover params...
57
+
58
+ try
59
+
60
+ module = loader.loaders.discovered.apply @, params
61
+
62
+ catch e
63
+ console.warn 'Failed to load \'', params[0], "' with #{using}: Error: '", e.message, '\'. Trying with requirejs.'
64
+ loader.activate 'requirejs'
65
+ module = loader.loaders.discovered.apply @, params unless module
66
+
67
+ # Always let requirjs active by default
68
+ loader.activate 'requirejs'
69
+
70
+ module
71
+
72
+ loader.shim()
73
+ loader.initialize()
@@ -0,0 +1,3 @@
1
+ requirejs.config({
2
+ baseUrl: '/assets'
3
+ });
@@ -0,0 +1 @@
1
+ #= require_tree ./config/initializers
@@ -0,0 +1,95 @@
1
+ define ['./states/index', './presenter'], (templates, presenter) ->
2
+
3
+ # If some extension provides you can use the type defined in there
4
+ # to extend your widget. Defaults to Base constructor.
5
+ #
6
+ # type: 'Base'
7
+
8
+ # Default values for the options passed to this widget
9
+ #
10
+ # Note: the options are passed thorught the html element data
11
+ # attributes for this widget: <div data-aura-amount="3"></div>
12
+ #
13
+ # options: {}
14
+
15
+
16
+ # Widget initialization method, will be called upon loading, options
17
+ # are already filled with defaults
18
+ initialize: (options) ->
19
+ sandbox = @sandbox
20
+ sandbox.logger.log "initialized!"
21
+
22
+ base =
23
+ status: null
24
+ classes: =>
25
+ "#{@state} #{@status} authenticator"
26
+ toggle_state: (event) =>
27
+ @state = if @state == 'default' then 'passwords' else 'default'
28
+ false
29
+
30
+ # Forward the models to the presenter
31
+ authenticator =
32
+ message: null
33
+ email: null
34
+ password: null
35
+ # TODO copiar do modacad
36
+ # button_label: 'Entrar'
37
+
38
+ # Authentication state
39
+ authenticate: (event) ->
40
+ base.status = "loading" # replace all status
41
+ sandbox.emit 'user.sign_in', authentication
42
+
43
+ # Listeners
44
+ authenticated: (session) ->
45
+ base.status = "success"
46
+
47
+ unauthorized: ->
48
+ authentication.message = "Ops... seu e-mail ou senha estão incorretos. Tente mais uma vez!<br><a href='#'>Esqueceu sua senha? Clique aqui.</a>"
49
+ base.status = "error"
50
+
51
+
52
+ recoverer =
53
+ email: null
54
+ message: null
55
+
56
+ # TODO copiar do modacad
57
+ # button_label: 'Entrar'
58
+
59
+ # Recovery state
60
+ recover: (event, models) ->
61
+ base.status = 'loading'
62
+ sandbox.emit 'user.recover_password', recovery
63
+
64
+ recovered: (password) ->
65
+ base.status = 'success'
66
+
67
+
68
+ # Bind and unbind events depending on state
69
+ sandbox.on 'user.signed_in' , authenticator.authenticated
70
+ sandbox.on 'user.unauthorized' , authenticator.unauthorized
71
+ sandbox.on 'user.password_recovered' , recoverer.recovered
72
+
73
+
74
+ # Will also initialize sandbox!
75
+ @$el.attr 'data-class', 'base.classes < base.status'
76
+ @presentation = presenter authenticator, recoverer, base
77
+ authentication = @presentation.authenticator
78
+ recovery = @presentation.recoverer
79
+
80
+ # TODO Extract to stateable widget
81
+ # TODO use observable
82
+ @observed ||= {}
83
+ Object.defineProperty @, 'state',
84
+ get: => @observed.state
85
+ set: @transition
86
+
87
+ @state = 'default'
88
+
89
+
90
+ # TODO Extract to stateable widget
91
+ # TODO use observable
92
+ transition: (to) ->
93
+ @html templates[to]
94
+ @bind @presentation
95
+ @observed.state = to
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+ define ->
3
+ observable = require('observable').mixin
4
+
5
+ (authenticator, recoverer, base) ->
6
+
7
+ authenticator: observable Object.create null,
8
+ status:
9
+ configurable: true
10
+ get: -> authenticator.status
11
+ set: (status) -> authenticator.status = status
12
+ message:
13
+ configurable: true
14
+ get: -> authenticator.message
15
+ set: (message) -> authenticator.message = message
16
+ classes:
17
+ configurable: true
18
+ value: ->
19
+ "widget authenticator #{authenticator.status}"
20
+ email:
21
+ configurable: true
22
+ set: (email) -> authenticator.email = email
23
+ get: -> authenticator.email
24
+ password:
25
+ configurable: true
26
+ set: (password) -> authenticator.password = password
27
+ get: -> authenticator.password
28
+ authenticate:
29
+ configurable: true
30
+ value: authenticator.authenticate
31
+
32
+ # TODO split into two presenters
33
+ recoverer: observable recoverer
34
+
35
+ base: observable base
@@ -0,0 +1,7 @@
1
+ <div class="message" data-html="authenticator.message"> </div>
2
+ <input type="email" data-value="authenticator.email" placeholder="Login" />
3
+ <input type="password" data-value="authenticator.password" placeholder="Password" />
4
+ <button data-on-click="authenticator.authenticate">
5
+ Authenticate!
6
+ </button>
7
+ <a data-on-click="base.toggle_state">Recuperar Senha</a>
@@ -0,0 +1,3 @@
1
+ define ['text!./default.html', 'text!./passwords.html'], (default_state, passwords_state) ->
2
+ default: default_state
3
+ passwords: passwords_state