tres 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,210 +1,282 @@
1
+ # Tres
2
+ # ====
3
+
4
+ # The whole framework should fit nicely in a single file. Avoid magic,
5
+ # avoid indirection
6
+
7
+ # Grab all variables to shorter references we'll use throughout
1
8
  Tres = {}
2
9
  $ = window.jQuery
3
10
  _ = window._
4
11
  Backbone = window.Backbone
5
12
  JST = window.JST
6
- $window = $(window)
7
- $body = $('body')
13
+ $window = $ window
14
+ $body = $ 'body'
8
15
  make = Backbone.View.prototype.make
9
16
 
10
- defaultTemplate = _.template("""
17
+ # Default screen template
18
+ defaultTemplate = _.template """
11
19
  <header></header>
12
20
  <h1>Tres</h1>
13
21
  <p>Welcome to Tres</p>
14
- """)
22
+ """
23
+ # ---
15
24
 
16
- # Tres.Device handles/exposes device events (orientation change, accellerometer, etc), screen width, and
17
- # other hardware-related goodies.
25
+ # Tres.Device handles/exposes device events (orientation change, accellerometer,
26
+ # etc), screen width, and other hardware-related goodies
18
27
  class Device
19
- constructor : ->
28
+ constructor: ->
20
29
  _.extend @, Backbone.Events
21
- $window.on 'orientationchange', _.bind @trigger, @, 'orientation:change'
30
+ $window.on 'orientationchange', =>
31
+ @trigger 'orientation:change'
32
+ # Fix <html> not stretching beyond 320px
33
+ $('html').width window.innerWidth
34
+ window.scrollTo 0, 0
22
35
 
23
- width : -> window.outerWidth
24
- height : -> window.outerHeight
25
- orientation : ->
36
+ width: -> window.outerWidth
37
+ height: -> window.outerHeight
38
+ orientation: ->
26
39
 
27
40
  Tres.Device = new Device
28
41
 
29
- # Tres.Screen is the base screen class. So you'll pretty have one of these for each "route" in your app.
42
+ # ---
43
+
44
+ # Tres.Screen is the base screen class. So you'll pretty have one of these for
45
+ # each "route" in your app
30
46
  class Tres.Screen extends Backbone.View
31
47
 
32
48
  # Defaults to using sections for screens
33
- tagName : 'section'
49
+ tagName: 'section'
34
50
 
35
- # Ensure one can still declare events in a view without getting in the way of the defaults.
36
- __events :
37
- # Click/touch on links will trigger pushState, but stay in the app. Except
38
- # for links with the "outlink" class.
39
- "click a[href]:not([href^='http://'])" : 'touchLink'
51
+ # Ensure one can still declare events in a view without getting in the
52
+ # way of the defaults
53
+ __events:
54
+ "click a[href]:not([href^='http://'])": 'touchLink'
40
55
 
41
- # Provide a convenience method `submit` which gets fired when you submit a form in a screen.
42
- # You can still trap forms normally. This is just a shortcut in case you have 1 form in a screen.
43
- "submit form" : '__submit'
56
+ # Provide a convenience method `submit` which gets fired when you
57
+ # submit a form in a screen. You can still trap forms normally.
58
+ # This is just a shortcut in case you have 1 form in a screen
59
+ "submit form": '__submit'
44
60
 
45
- events : {}
61
+ events: {}
46
62
 
47
- initialize : (options = {}) ->
63
+ initialize: (options = {}) ->
48
64
  _.extend @, options
49
65
 
50
- # Returns the title of the screen, which will only exist if there's a header with a <h1>
51
- # inside of it.
52
- title : (title = null) ->
66
+ # Returns the title of the screen, which will only exist if there's a
67
+ # header with a <h1> inside of it
68
+ title: (title = null) ->
53
69
  if title?
54
- @$el.find('h1').html(title)
70
+ @$el.find('h1').html title
55
71
  else
56
72
  @$el.find('h1').html()
57
73
 
58
- render : ->
59
- @$el.html( (@template or defaultTemplate)(@model) )
60
- @delegateEvents _.extend(@events, @__events)
74
+ # Renders the screen's template into the container element, and applies
75
+ # the screen's events and the default screen class
76
+ render: ->
77
+ @$el.html (@template or defaultTemplate)(@model)
78
+ @$el.addClass 'screen'
79
+ @delegateEvents _.extend @events, @__events
61
80
  @
62
81
 
63
- # Embeds a Tres.Screen into the <body>. Returns false in case it's already embedded.
64
- embed : ->
82
+ # Embeds a Tres.Screen into the <body>. Returns false in case it's already
83
+ # embedded
84
+ embed: ->
65
85
  return false if @embedded
66
86
  @render()
67
- $body.append @el
87
+ $body.prepend @el
68
88
  @embedded = true
69
89
  @
70
90
 
71
- touchLink : (event) ->
91
+ # Click/touch on links will trigger pushState, but stay in the app
92
+ touchLink: (event) ->
72
93
  event.preventDefault()
73
94
  Tres.Router.navigate $(event.currentTarget).attr('href'), true
74
95
 
75
- __submit : (event) ->
96
+ # Runs whatever @submit method is declared, with the added bonus of
97
+ # un-focusing any text fields that are currently focused
98
+ __submit: (event) ->
76
99
  @submit.apply @, arguments
77
100
  @$el.find(':focus').blur()
78
101
 
79
- submit : ->
80
-
81
- # Sets the class "current" to this screen, removing the class from whatever other sections
82
- # that have it. Call the `active` method in case a screen has one.
83
- activate : ->
84
- $body.find('>section').removeClass 'current'
102
+ # Default @submit to noop
103
+ submit: ->
104
+
105
+ # Sets the class "current" to this screen, removing the class from
106
+ # whatever other sections that have it. Call the `active` method in
107
+ # case a screen has one. Removes -webkit-transform to prevent Webkit from
108
+ # screwing a whole lot of stuff
109
+ activate: ->
110
+ if not @modal
111
+ $body.find('>section')
112
+ .removeClass('current')
113
+ .css '-webkit-transform', ''
85
114
  @$el.addClass 'current'
86
- @active.apply(@, arguments) if _.isFunction(@active)
115
+ @$el.addClass 'modal' if @modal
116
+ @$el.css
117
+ 'min-height': window.innerHeight+50
118
+ '-webkit-transform': 'none'
119
+ @active.apply @, arguments if _.isFunction @active
87
120
 
121
+ # ---
122
+
123
+ # This class is used as controller for each Tres.List item
88
124
  class Tres.ListEntry extends Backbone.View
89
125
  initialize : (options = {}) ->
90
126
  _.extend @, options
91
127
 
92
- render : ->
93
- @$el.html( @template(@model) )
94
- @delegateEvents('click' : 'touch') if _.isFunction(@url)
128
+ render: ->
129
+ @$el.html @template @model
130
+ @delegateEvents 'click': 'touch' if _.isFunction @url
95
131
  @
96
132
 
97
- touch : (event) ->
133
+ # If a @url method is defined, means we want the user to touch and
134
+ # visit the URL
135
+ touch: (event) ->
98
136
  Tres.Router.navigate @url(), true
99
137
 
138
+ # ---
100
139
 
101
- # Tres.List is a convenience class for rendering lists of things, interactible or not.
140
+ # Tres.List is a convenience class for rendering lists of things
102
141
  class Tres.List extends Backbone.View
142
+
143
+ # A map of likely child tags for each parent tag
103
144
  _tagMap :
104
145
  'UL' : 'LI'
105
146
  'OL' : 'LI'
106
147
  'DIV' : 'DIV'
107
148
 
108
- initialize : (options = {}) ->
149
+ # Keep the DOM in sync with all interactions made with the collection
150
+ initialize: (options = {}) ->
109
151
  _.extend @, options
110
- @setElement(@el)
152
+ @setElement @el
111
153
  @collection.on 'add', @__add, @
112
154
  @collection.on 'remove', @__remove, @
113
155
  @collection.on 'reset', @__addAll, @
114
156
 
115
- __add : (model) ->
157
+ # Adds one record to the list, wrapping it in a Tres.List
158
+ __add: (model) ->
116
159
  tag = @entry?.tagName or @_tagMap[@$el.get(0).tagName]
117
- template = new Tres.ListEntry(_.extend(@entry, { tagName : tag, model : model }))
160
+ template = new Tres.ListEntry _.extend(@entry, { tagName : tag, model : model })
118
161
  model.template = template
119
162
  @$el.append template.render().el
120
163
 
121
- __remove : (model) ->
164
+ # Removes the list from the DOM
165
+ __remove: (model) ->
122
166
  model.template.remove()
123
167
 
124
- __addAll : ->
168
+ # Adds all the elements the @collection to the list, removing any existing
169
+ # ones
170
+ __addAll: ->
125
171
  @$el.empty()
126
- @collection.each (model) => @__add(model)
172
+ @collection.each (model) => @__add model
127
173
 
128
- remove : ->
174
+ # Deletes the instance of Tres.List, ensuring any bound events to
175
+ # @collection are removed
176
+ remove: ->
129
177
  @collection.off() if @collection?
130
178
  super
131
179
 
180
+ # ---
181
+
182
+ # Handles form submissions and a few other helpers such as accessing
183
+ # fieldsets individually, and loading model data into fields
132
184
  class Tres.Form
133
- constructor : (@$el) ->
134
-
135
- fieldset : (filter) ->
136
- new Tres.Form(@$el.find('fieldset').filter(filter))
137
-
138
- attributes : (options = {})->
185
+
186
+ # Takes a jQuery-wrapped form object
187
+ constructor: (@$el) ->
188
+
189
+ # Returns a new instance of Tres.Form, narrowed down to a single
190
+ # fieldset
191
+ fieldset: (filter) ->
192
+ new Tres.Form @$el.find('fieldset').filter(filter)
193
+
194
+ # Returns all the form data in a single object, in key/value
195
+ # pairs by `name` attribute and value
196
+ attributes: (options = {})->
139
197
  attributes = {}
140
198
  if options.only?
141
- inputs = @$el.find("#{options.only} :input[name]")
199
+ inputs = @$el.find "#{options.only} :input[name]"
142
200
  else
143
- inputs = @$el.find(':input[name]')
201
+ inputs = @$el.find ':input[name]'
144
202
  _.each inputs, (el) ->
145
- $el = $(el)
146
- if $el.is(':checkbox')
147
- attributes[ $el.attr('name') ] = $el.is(':checked')
203
+ $el = $ el
204
+ if $el.is ':checkbox'
205
+ attributes[ $el.attr 'name' ] = $el.is ':checked'
148
206
  else
149
- attributes[ $el.attr('name') ] = $el.val()
207
+ attributes[ $el.attr 'name' ] = $el.val()
150
208
  attributes
151
-
152
- setFromModel : (model) ->
153
- _.each _.keys(model.attributes), (key) =>
154
- $el = @$el.find("[name=\"#{key}\"]")
155
- if $el.is(':checkbox') and model.attributes[key] is true
156
- $el.attr('checked', 'checked')
209
+
210
+ # Fills the form's fields whose `name` match the model's attributes
211
+ setFromModel: (model) ->
212
+ _.each _.keys model.attributes, (key) =>
213
+ $el = @$el.find "[name=\"#{key}\"]"
214
+ if $el.is ':checkbox' and model.attributes[key] is true
215
+ $el.attr 'checked', 'checked'
157
216
  else
158
217
  $el.val model.attributes[key]
159
-
160
- clear : ->
161
- _.each @$el.find(':input'), (el) ->
162
- $el = $(el)
163
- $el.removeAttr('checked') if $el.is(':checkbox')
164
- $el.val('')
165
- @
166
218
 
167
- # Tres.Notifier: handles displaying in-app notifications
219
+ # Clears the form
220
+ clear: ->
221
+ _.each @$el.find ':input', (el) ->
222
+ $el = $ el
223
+ $el.removeAttr 'checked' if $el.is ':checkbox'
224
+ $el.val ''
225
+
226
+ # ---
227
+
228
+ # Handles displaying in-app notifications
168
229
  Tres.Notifier =
169
- $el : $(make('ul', id : 'notifications'))
230
+ $el: $ make('ul', id: 'notifications')
170
231
 
171
232
  notify : (message, options = { duration : 5000, type : 'exclamation-sign' }) ->
172
233
  @$el.appendTo $body
173
- $li = $(make('li', { class : "icon-#{options.type}"}, message))
234
+ $li = $ make 'li', { class: "icon-#{options.type}" }, message
174
235
  @$el.append $li
175
236
  $li.slideDown 250, =>
176
237
  _.delay =>
177
238
  $li.slideUp => $li.remove()
178
239
  , options.duration
179
240
 
180
- # Just a regular Backbone.Router for now. By default you'll have one for application
241
+ # ---
242
+
243
+ # Just a regular Backbone.Router for now. You'll have one for application
181
244
  class Router extends Backbone.Router
182
245
 
183
- # Make the router it accessible
184
246
  Tres.Router = new Router
185
247
 
248
+ # ---
249
+
186
250
  class Tres.App
187
- constructor : (options = {}) ->
251
+
252
+ constructor: (options = {}) ->
188
253
  _.extend @, options
189
254
 
190
- screens : []
255
+ # All the screens that are currently instanced
256
+ screens: []
191
257
 
192
- on : (map = {}) ->
258
+ # Takes a hash to use as a mapping of URLs -> Tres.Screen objects
259
+ on: (map = {}) ->
193
260
  _.each _.keys(map), (url) =>
194
261
  Tres.Router.route url, _.uniqueId('r'), =>
195
262
  screen = map[url]
196
263
  screen.embed() unless screen.embedded is true
197
264
  args = arguments
198
- _.defer => screen.activate.apply(screen, args)
265
+ window.scroll 0, 0
266
+ _.defer => screen.activate.apply screen, args
199
267
 
200
- boot : (options = {}) ->
268
+ # Boots the app, executing the routes and adding a handy `before`
269
+ # event to the router
270
+ boot: (options = {}) ->
201
271
  __super = Backbone.history.loadUrl
202
272
  Backbone.history.loadUrl = =>
203
- Tres.Router.before.call(@) if _.isFunction(Tres.Router.before)
273
+ if _.isFunction Tres.Router.before
274
+ Tres.Router.before Backbone.history.getFragment()
204
275
  Tres.Router.trigger 'navigate'
205
- window.scroll(0, 0)
206
276
  __super.apply Backbone.history, arguments
207
- Backbone.history.start(_.extend(options, pushState : true))
277
+ Backbone.history.start _.extend(options, pushState : true)
208
278
 
279
+ # ---
209
280
 
210
- window.Tres = Tres
281
+ # Export to `window`
282
+ window.Tres = Tres
@@ -19,10 +19,13 @@ module Tres
19
19
  env.append_path path.to_s
20
20
  end
21
21
  env.append_path Tres.styles_dir
22
- env.append_path Tres.scripts_dir
22
+ env.append_path Tres.scripts_dir
23
23
  env.append_path @assets/'javascripts'
24
24
  env.append_path @assets/'stylesheets'
25
25
  env.append_path @assets
26
+ env.context_class.class_eval do
27
+ def asset_path path, options = {}; path end
28
+ end
26
29
  end
27
30
  end
28
31
 
@@ -42,4 +45,4 @@ module Tres
42
45
  def coffeescript?
43
46
  end
44
47
  end
45
- end
48
+ end
data/lib/tres.rb CHANGED
@@ -3,7 +3,6 @@ $:.unshift File.dirname(__FILE__)
3
3
 
4
4
  require 'rubygems'
5
5
  require 'logger'
6
- require 'json/pure'
7
6
  require 'ext/string'
8
7
  require 'ext/filemethods'
9
8
  require 'tres/app'
@@ -21,15 +20,15 @@ module Tres
21
20
  def quiet!
22
21
  @quiet = true
23
22
  end
24
-
23
+
25
24
  def quiet?
26
25
  !!@quiet
27
26
  end
28
-
27
+
29
28
  def verbose!
30
29
  @quiet = false
31
30
  end
32
-
31
+
33
32
  def say something
34
33
  STDOUT.puts(OUTPUT_FORMAT % something) unless quiet?
35
34
  yield if block_given?
data/sass/tres/base.scss CHANGED
@@ -1,131 +1,169 @@
1
- @import 'font-awesome';
1
+ // Tres base grid and grid related settings
2
+ // ========================================
2
3
 
3
- // Quick FontAwesome fix so icons push the sentence
4
- // a bit to the right
4
+ @import 'mixins';
5
+
6
+ // Resets
7
+
8
+ // Quick FontAwesome fix so icons push the sentence a bit to the right
5
9
  [class^="icon-"]:before { margin-right : .5rem }
10
+ input[class^="icon-"] {
11
+ position: relative;
12
+
13
+ &:before {
14
+ @include translate(0, -50%);
15
+ position: absolute;
16
+ top: 50%
17
+ }
18
+ }
6
19
 
7
- * {
8
- margin : 0;
9
- padding : 0;
10
- outline : 0;
11
- border : 0;
12
- -webkit-box-sizing : border-box
20
+ * {
21
+ margin : 0;
22
+ padding : 0;
23
+ outline : 0;
24
+ border : 0;
25
+ -webkit-box-sizing : border-box;
26
+ box-sizing: border-box;
13
27
  }
14
28
 
15
- ul { list-style : none }
29
+ ul { list-style : none }
16
30
  input, button, textarea { font-size : 100% }
17
31
 
18
- html { height : 100%; min-height : 420px; font-size : 62.5%; overflow-x : hidden }
19
- html, body { height : 99.9% }
32
+ // ---
33
+
34
+ $spacing: .5rem;
35
+
36
+ // Structural settings for getting screens to use the whole vertical space.
37
+
38
+ html {
39
+ min-height: 420px;
40
+ min-width: 100%;
41
+ font-size : 62.5%;
42
+ }
43
+ html, body { overflow: hidden; width: 100%; height: 100% }
20
44
  body {
21
- font-size : 1.5rem;
45
+ position: fixed;
46
+ top: 0;
47
+ right: 0;
48
+ bottom: 0;
49
+ left: 0;
22
50
  line-height : 2.3rem;
23
- width : 100%;
24
51
  -webkit-perspective : 800;
25
52
  -webkit-text-size-adjust : none;
26
53
  -webkit-transform-style : preserve-3d;
27
- -webkit-user-select : none;
54
+ -webkit-user-select : none
28
55
  }
29
56
 
57
+ // ---
58
+
30
59
  // A base section unit. Think of this as a "screen".
31
- body > section {
32
- display : block;
33
- position : absolute;
34
- padding : 1.6rem 0;
60
+ .screen {
61
+ -webkit-transition : all 0.25s ease-out;
62
+ -webkit-transform-style : preserve-3d;
63
+ -webkit-transform : translate3d(100%,0%,0);
64
+ position : fixed;
35
65
  top : 0;
66
+ right : 0;
67
+ bottom : 0;
36
68
  left : 0;
37
- width : 100%;
38
69
  opacity : 0;
39
70
  overflow : auto;
40
- overflow-x : hidden;
41
71
  pointer-events : none;
42
- -webkit-transition : all 0.25s ease-in-out;
43
- -webkit-transform-style : preserve-3d;
44
- -webkit-transform : translate3d(100%,0%,0);
45
- &>*:not(header) {
46
- margin-left : 1rem !important;
47
- margin-right : 1rem !important;
48
- }
72
+
49
73
  &.current {
50
74
  opacity : 1;
51
75
  pointer-events : auto;
52
76
  -webkit-transform : translate3d(0%,0%, 0)
53
77
  }
78
+
79
+ &.modal { z-index: 1000 }
54
80
  }
55
81
 
56
- // Floating header, header stuffs
57
- body > section > header {
82
+ // ---
83
+
84
+ // Headers
85
+
86
+ .screen > header {
87
+ position : fixed;
88
+ top : 0;
89
+ right : 0;
90
+ left : 0;
58
91
  margin : 0;
59
92
  text-align : center;
60
- &.fixed {
61
- position : fixed;
62
- top : 0;
63
- left : 0;
64
- width : 100%;
65
- z-index : 10000
66
- }
67
- & + * { margin-top : 4.6rem !important }
93
+ overflow : hidden;
94
+ z-index : 100;
95
+
96
+ & + .content { top : 60px }
97
+
98
+ & > * { margin: $spacing*2 0 }
68
99
  h1 {
69
- font-size : 1.9rem;
70
- margin : 0.9rem 0;
100
+ font-size : 1.6rem;
101
+ margin : 1.1rem 0;
71
102
  white-space : nowrap
72
103
  }
73
- a {
74
- position : absolute;
75
- font-size : 1.9rem;
76
- margin-top : 0.9rem;
104
+ &>a, &>button {
105
+ position: absolute;
106
+ top : 0;
107
+ width : 2.9rem;
108
+ font-size : 2.1rem;
77
109
  text-decoration : none;
78
- &.left { left : 1.6rem }
79
- &.right { right : 1.6rem }
110
+
111
+
112
+ &.left { left: $spacing }
113
+ &.right { right: $spacing }
80
114
  }
115
+ menu {
116
+ @extend .spans-horizontally;
117
+ a, button {
118
+ font-size: 2.3rem
119
+ }
120
+ }
121
+ }
122
+
123
+ // ---
124
+
125
+ // Screen content
126
+
127
+ .screen > .content {
128
+ position: fixed;
129
+ top: 0;
130
+ right: 0;
131
+ bottom: 0;
132
+ left: 0;
133
+ overflow: auto;
134
+ -webkit-overflow-scrolling: touch;
81
135
  }
82
136
 
137
+ // ---
138
+
83
139
  // Type margins and sizes
84
- body > section {
140
+ .screen {
85
141
  h1 { font-size: 2.9rem; margin-bottom : 1.6rem }
86
142
  h2 { font-size: 2.6rem; margin: 1.3rem 0 }
87
- p, input, textarea, button, li { font-size : 1.8rem }
88
- p { margin : 0.9rem 0 }
143
+ p, input, textarea, button, li { font-size : 1.3rem }
144
+ p { margin : 1.1rem 0 }
89
145
  }
90
146
 
147
+ // ---
148
+
149
+ // Default form settings
150
+
91
151
  form {
92
152
  margin : 1.3rem 0;
93
- label {
94
- display : block;
95
- margin : 0.25em 0;
96
- font-weight : bold;
97
- }
98
- input, textarea {
153
+ input:not([type="checkbox"]), textarea {
99
154
  display : block;
100
155
  padding : 0.9rem;
101
156
  width : 100%;
102
157
  font-family : inherit;
103
158
  }
104
- }
105
-
106
- ul.common {
107
- li {
108
- padding : 0.9rem;
159
+ input[type="checkbox"] {
160
+ width: 1rem;
161
+ height: 1rem
162
+ }
163
+ input[type="checkbox"] + label {
164
+ display: inline-block;
165
+ margin-left: $spacing;
109
166
  }
110
167
  }
111
168
 
112
- // Rules for opting-in
113
- .t-centered { text-align : center }
114
- .t-padded-top { padding-top : 5rem }
115
- .t-left-align { text-align : left }
116
- h1.t-bigger { font-size : 3.9rem }
117
-
118
-
119
- #notifications {
120
- position : fixed;
121
- right : 0;
122
- bottom : 0;
123
- left : 0;
124
- li {
125
- display : none;
126
- padding : 0.6rem 1rem;
127
- font-weight : bold;
128
- font-size : 1.6rem;
129
- &:before { margin-right : 1rem }
130
- }
131
- }
169
+ // ---