tres 0.1.2 → 0.1.4

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.
@@ -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
+ // ---