ende 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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