houston-core 0.8.4 → 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +72 -78
  4. data/app/assets/javascripts/houston/app/models/role.coffee +4 -0
  5. data/app/assets/javascripts/houston/app/views/nested_resources.coffee +44 -0
  6. data/app/assets/javascripts/houston/app/views/team_roles_view.coffee +10 -0
  7. data/app/assets/javascripts/houston/application.js +3 -1
  8. data/app/assets/javascripts/houston/core/ajax_helpers.coffee +26 -0
  9. data/app/assets/javascripts/houston/core/app.coffee +0 -45
  10. data/app/assets/javascripts/houston/core/errors.coffee +1 -12
  11. data/app/assets/javascripts/houston/core/handlebars_helpers.coffee +8 -70
  12. data/app/assets/javascripts/houston/core/jquery_extensions.coffee +0 -138
  13. data/app/assets/javascripts/houston/core/timeline_helpers.coffee +44 -0
  14. data/app/assets/javascripts/houston/core/uploader_helpers.coffee +99 -0
  15. data/app/assets/javascripts/houston/vendor.js +26 -9
  16. data/app/assets/stylesheets/houston/application/actions.scss +22 -0
  17. data/app/assets/stylesheets/houston/application/navigation.scss +2 -2
  18. data/app/assets/stylesheets/houston/application/{freight_train.css.scss → nested_resources.scss} +1 -0
  19. data/app/assets/stylesheets/houston/core/alerts.scss +0 -4
  20. data/app/assets/stylesheets/houston/core/timeline.scss +204 -0
  21. data/app/assets/templates/houston/teams/roles/index.hbs +1 -0
  22. data/app/assets/templates/houston/teams/roles/show.hbs +19 -0
  23. data/app/channels/events_channel.rb +1 -1
  24. data/app/concerns/houston/props.rb +3 -2
  25. data/app/controllers/actions_controller.rb +8 -3
  26. data/app/controllers/application_controller.rb +9 -12
  27. data/app/controllers/authorizations_controller.rb +41 -14
  28. data/app/controllers/errors_controller.rb +3 -3
  29. data/app/controllers/project_follows_controller.rb +23 -0
  30. data/app/controllers/project_options_controller.rb +1 -1
  31. data/app/controllers/user_options_controller.rb +1 -1
  32. data/app/helpers/actions_helper.rb +12 -0
  33. data/app/helpers/application_helper.rb +0 -10
  34. data/app/helpers/layout_helper.rb +20 -0
  35. data/app/helpers/markdown_helper.rb +2 -10
  36. data/app/helpers/navigation_helper.rb +5 -5
  37. data/app/helpers/project_helper.rb +6 -0
  38. data/app/helpers/url_helper.rb +4 -4
  39. data/app/helpers/view_extensions_helper.rb +20 -0
  40. data/app/models/action.rb +61 -44
  41. data/app/models/authorization.rb +55 -10
  42. data/app/models/follow.rb +6 -0
  43. data/app/models/persistent_trigger.rb +11 -1
  44. data/app/models/project.rb +5 -27
  45. data/app/models/user.rb +15 -59
  46. data/app/presenters/project_presenter.rb +2 -2
  47. data/app/views/actions/_actions.html.erb +8 -5
  48. data/app/views/actions/index.html.erb +1 -1
  49. data/app/views/actions/running.html.erb +9 -15
  50. data/app/views/actions/show.html.erb +6 -3
  51. data/app/views/actions/unqueued.html.erb +41 -0
  52. data/app/views/authorizations/_form.html.erb +28 -16
  53. data/app/views/authorizations/index.html.erb +9 -4
  54. data/app/views/{oauth/providers/edit.html.erb → authorizations/oauth2_callback.html.erb} +3 -2
  55. data/app/views/devise/sessions/new.html.erb +0 -8
  56. data/app/views/errors/index.html.erb +9 -5
  57. data/app/views/layouts/_navigation.html.erb +9 -0
  58. data/app/views/layouts/application.html.erb +11 -6
  59. data/app/views/layouts/dashboard.html.erb +9 -0
  60. data/app/views/projects/_form.html.erb +7 -18
  61. data/app/views/projects/_header.html.erb +2 -6
  62. data/app/views/projects/index.html.erb +4 -4
  63. data/app/views/teams/_form.html.erb +7 -17
  64. data/app/views/teams/index.html.erb +1 -1
  65. data/app/views/users/_form.html.erb +1 -38
  66. data/config/application.rb +8 -5
  67. data/config/initializers/devise.rb +0 -14
  68. data/config/initializers/secret_token.rb +8 -13
  69. data/config/routes.rb +12 -28
  70. data/db/migrate/20130706141443_drop_deprecated_project_roles.rb +5 -1
  71. data/db/migrate/20170118005958_remove_antecedents_from_versions_of_tickets.rb +1 -1
  72. data/db/migrate/20170130011016_drop_users_environments_subscribed_to.rb +9 -0
  73. data/db/migrate/20170205004452_drop_settings.rb +12 -0
  74. data/db/migrate/20170206002030_drop_extension_hstore.rb +9 -0
  75. data/db/migrate/20170206002732_drop_legacy_columns_from_users.rb +10 -0
  76. data/db/migrate/20170209022159_rename_projects_color_to_color_name.rb +5 -0
  77. data/db/migrate/20170213001453_change_providers_from_models_to_extensions.rb +27 -0
  78. data/db/migrate/20170215012012_add_props_to_authorizations.rb +5 -0
  79. data/db/migrate/20170216041034_add_user_to_persistent_triggers.rb +5 -0
  80. data/db/migrate/20170226201504_create_follows.rb +20 -0
  81. data/db/migrate/20170301014051_drop_name_from_authorizations.rb +9 -0
  82. data/db/migrate/20170307032041_add_created_at_to_actions.rb +11 -0
  83. data/db/migrate/20170307035755_allow_actions_started_at_to_be_null.rb +5 -0
  84. data/db/migrate/20170310024505_replace_authorizations_provider_name_with_type.rb +12 -0
  85. data/db/migrate/20170329030329_drop_consumer_tokens.rb +9 -0
  86. data/db/structure.sql +187 -212
  87. data/houston-core.gemspec +10 -13
  88. data/lib/houston/boot.rb +1 -4
  89. data/lib/houston/boot/actions.rb +24 -21
  90. data/lib/houston/boot/configuration.rb +46 -113
  91. data/lib/houston/boot/extensions.rb +54 -341
  92. data/lib/houston/boot/extensions/deprecated.rb +194 -0
  93. data/lib/houston/boot/extensions/dsl.rb +99 -0
  94. data/lib/houston/boot/extensions/events.rb +81 -0
  95. data/lib/houston/boot/extensions/features.rb +42 -0
  96. data/lib/houston/boot/extensions/layout.rb +70 -0
  97. data/lib/houston/boot/extensions/navigation.rb +42 -0
  98. data/lib/houston/boot/extensions/oauth.rb +62 -0
  99. data/lib/houston/boot/extensions/serializers.rb +29 -0
  100. data/lib/houston/boot/extensions/view.rb +34 -0
  101. data/lib/houston/boot/observer.rb +10 -5
  102. data/{app/models/oauth → lib/houston/boot}/provider.rb +7 -5
  103. data/lib/houston/boot/running_as.rb +0 -5
  104. data/lib/houston/boot/serializer.rb +12 -6
  105. data/lib/houston/boot/{active_record_serializer.rb → serializers/active_record_serializer.rb} +0 -2
  106. data/lib/houston/boot/{readonly_hash_serializer.rb → serializers/readonly_hash_serializer.rb} +0 -2
  107. data/lib/houston/boot/timer.rb +10 -0
  108. data/lib/houston/boot/triggers.rb +27 -8
  109. data/lib/houston/version.rb +1 -1
  110. data/templates/new-instance/.gitignore +0 -4
  111. data/templates/new-instance/config/main.rb +8 -10
  112. data/templates/new-module/test/dummy/houston.rb +1 -0
  113. data/test/acceptance/layout_test.rb +58 -0
  114. data/test/acceptance/updating_props_test.rb +72 -0
  115. data/test/support/config.rb +1 -0
  116. data/test/unit/extensions/events_extension_test.rb +33 -0
  117. data/test/unit/extensions/layout_extension_test.rb +74 -0
  118. data/test/unit/extensions/navigation_extension_test.rb +62 -0
  119. data/test/unit/extensions/oauth_extension_test.rb +91 -0
  120. data/test/unit/extensions/project_features_extension_test.rb +79 -0
  121. data/test/unit/extensions/serializers_extension_test.rb +47 -0
  122. data/test/unit/extensions/view_extension_test.rb +98 -0
  123. data/test/unit/models/actions_test.rb +11 -5
  124. data/test/unit/models/configuration_test.rb +0 -8
  125. data/test/unit/models/observer_test.rb +16 -0
  126. data/test/unit/models/persistent_trigger_test.rb +29 -2
  127. data/test/unit/models/serializer_test.rb +6 -0
  128. data/test/unit/models/timer_test.rb +88 -0
  129. metadata +87 -168
  130. data/app/assets/font/octicons.eot +0 -0
  131. data/app/assets/font/octicons.svg +0 -198
  132. data/app/assets/font/octicons.ttf +0 -0
  133. data/app/assets/font/octicons.woff +0 -0
  134. data/app/assets/font/roboto-black-webfont.eot +0 -0
  135. data/app/assets/font/roboto-black-webfont.svg +0 -675
  136. data/app/assets/font/roboto-black-webfont.ttf +0 -0
  137. data/app/assets/font/roboto-black-webfont.woff +0 -0
  138. data/app/assets/font/roboto-blackitalic-webfont.eot +0 -0
  139. data/app/assets/font/roboto-blackitalic-webfont.svg +0 -677
  140. data/app/assets/font/roboto-blackitalic-webfont.ttf +0 -0
  141. data/app/assets/font/roboto-blackitalic-webfont.woff +0 -0
  142. data/app/assets/font/roboto-bold-webfont.eot +0 -0
  143. data/app/assets/font/roboto-bold-webfont.svg +0 -675
  144. data/app/assets/font/roboto-bold-webfont.ttf +0 -0
  145. data/app/assets/font/roboto-bold-webfont.woff +0 -0
  146. data/app/assets/font/roboto-bolditalic-webfont.eot +0 -0
  147. data/app/assets/font/roboto-bolditalic-webfont.svg +0 -677
  148. data/app/assets/font/roboto-bolditalic-webfont.ttf +0 -0
  149. data/app/assets/font/roboto-bolditalic-webfont.woff +0 -0
  150. data/app/assets/font/roboto-italic-webfont.eot +0 -0
  151. data/app/assets/font/roboto-italic-webfont.svg +0 -668
  152. data/app/assets/font/roboto-italic-webfont.ttf +0 -0
  153. data/app/assets/font/roboto-italic-webfont.woff +0 -0
  154. data/app/assets/font/roboto-light-webfont.eot +0 -0
  155. data/app/assets/font/roboto-light-webfont.svg +0 -666
  156. data/app/assets/font/roboto-light-webfont.ttf +0 -0
  157. data/app/assets/font/roboto-light-webfont.woff +0 -0
  158. data/app/assets/font/roboto-lightitalic-webfont.eot +0 -0
  159. data/app/assets/font/roboto-lightitalic-webfont.svg +0 -668
  160. data/app/assets/font/roboto-lightitalic-webfont.ttf +0 -0
  161. data/app/assets/font/roboto-lightitalic-webfont.woff +0 -0
  162. data/app/assets/font/roboto-medium-webfont.eot +0 -0
  163. data/app/assets/font/roboto-medium-webfont.svg +0 -675
  164. data/app/assets/font/roboto-medium-webfont.ttf +0 -0
  165. data/app/assets/font/roboto-medium-webfont.woff +0 -0
  166. data/app/assets/font/roboto-mediumitalic-webfont.eot +0 -0
  167. data/app/assets/font/roboto-mediumitalic-webfont.svg +0 -677
  168. data/app/assets/font/roboto-mediumitalic-webfont.ttf +0 -0
  169. data/app/assets/font/roboto-mediumitalic-webfont.woff +0 -0
  170. data/app/assets/font/roboto-regular-webfont.eot +0 -0
  171. data/app/assets/font/roboto-regular-webfont.svg +0 -666
  172. data/app/assets/font/roboto-regular-webfont.ttf +0 -0
  173. data/app/assets/font/roboto-regular-webfont.woff +0 -0
  174. data/app/assets/font/roboto-thin-webfont.eot +0 -0
  175. data/app/assets/font/roboto-thin-webfont.svg +0 -666
  176. data/app/assets/font/roboto-thin-webfont.ttf +0 -0
  177. data/app/assets/font/roboto-thin-webfont.woff +0 -0
  178. data/app/assets/font/roboto-thinitalic-webfont.eot +0 -0
  179. data/app/assets/font/roboto-thinitalic-webfont.svg +0 -668
  180. data/app/assets/font/roboto-thinitalic-webfont.ttf +0 -0
  181. data/app/assets/font/roboto-thinitalic-webfont.woff +0 -0
  182. data/app/assets/images/bug-fixed-128.png +0 -0
  183. data/app/assets/images/bug-fixed-32.png +0 -0
  184. data/app/assets/images/bug-fixed-48.png +0 -0
  185. data/app/assets/images/bug-new-128.png +0 -0
  186. data/app/assets/images/bug-new-32.png +0 -0
  187. data/app/assets/images/bug-new-48.png +0 -0
  188. data/app/assets/images/bug-open-32.png +0 -0
  189. data/app/assets/images/bug-zero-128.png +0 -0
  190. data/app/assets/images/bug-zero-48.png +0 -0
  191. data/app/assets/images/drag-grip.png +0 -0
  192. data/app/assets/javascripts/houston/core/burndown_chart.coffee +0 -111
  193. data/app/assets/javascripts/houston/core/stacked_area_graph.coffee +0 -113
  194. data/app/assets/javascripts/houston/core/stacked_bar_graph.coffee +0 -108
  195. data/app/assets/stylesheets/houston/application/project_tiles.scss +0 -26
  196. data/app/assets/stylesheets/houston/application/tips.scss +0 -5
  197. data/app/assets/stylesheets/houston/core/octicons-icons.scss +0 -221
  198. data/app/assets/stylesheets/houston/core/octicons.scss.erb +0 -9
  199. data/app/assets/stylesheets/houston/core/roboto.scss.erb +0 -131
  200. data/app/concerns/historical_weekly_stats.rb +0 -15
  201. data/app/concerns/nosync.rb +0 -21
  202. data/app/controllers/oauth/providers_controller.rb +0 -45
  203. data/app/controllers/project_roles_controller.rb +0 -22
  204. data/app/controllers/settings_controller.rb +0 -14
  205. data/app/controllers/tester_bar_controller.rb +0 -12
  206. data/app/controllers/user_credentials_controller.rb +0 -24
  207. data/app/models/role.rb +0 -33
  208. data/app/models/setting.rb +0 -10
  209. data/app/models/settings.rb +0 -38
  210. data/app/models/slackdown.rb +0 -23
  211. data/app/models/user_credentials.rb +0 -27
  212. data/app/views/errors/_actions.html.erb +0 -17
  213. data/app/views/layouts/_tester_bar.html.erb +0 -6
  214. data/app/views/layouts/minimal.html.erb +0 -50
  215. data/app/views/layouts/naked.html.erb +0 -47
  216. data/app/views/layouts/naked_dashboard.html.erb +0 -50
  217. data/app/views/oauth/providers/_form.html.erb +0 -54
  218. data/app/views/oauth/providers/index.html.erb +0 -41
  219. data/app/views/oauth/providers/new.html.erb +0 -7
  220. data/config/initializers/add_navigation_renderers.rb +0 -5
  221. data/config/initializers/vestal_versions.rb +0 -9
  222. data/db/migrate/20130519163615_create_user_credentials.rb +0 -18
  223. data/lib/houston/boot/events.rb +0 -10
  224. data/lib/tasks/keypair.rake +0 -17
  225. data/vendor/assets/javascripts/jquery.pjax.js +0 -817
@@ -1,18 +1,7 @@
1
1
  class window.Errors
2
2
 
3
3
  constructor: (response)->
4
- if response.status == 401
5
- if response.getResponseHeader('X-Credentials') == 'Missing Credentials'
6
- @missingCredentials = true
7
- else if response.getResponseHeader('X-Credentials') == 'Invalid Credentials'
8
- @invalidCredentials = true
9
- else if response.getResponseHeader('X-Credentials') == 'Oauth'
10
- @oauthLocation = response.getResponseHeader('Location')
11
- else
12
- message = response.responseText ? "You are not authorized"
13
- @errors = {base: [message]}
14
- else
15
- @errors = JSON.parse(response.responseText)
4
+ @errors = JSON.parse(response.responseText)
16
5
 
17
6
  renderToAlert: ->
18
7
  sentences = []
@@ -42,26 +42,6 @@ Handlebars.registerHelper 'markdown', (markdown)-> App.mdown(markdown)
42
42
 
43
43
  Handlebars.registerHelper 'emojify', (string)-> App.emojify(string)
44
44
 
45
- Handlebars.registerHelper 'classForAge', (seconds)->
46
- if seconds < (6 * Duration.HOUR)
47
- 'infant'
48
- else if seconds < (2 * Duration.DAY)
49
- 'child'
50
- else if seconds < (7 * Duration.DAY)
51
- 'adult'
52
- else if seconds < (4 * Duration.WEEK)
53
- 'senior'
54
- else if seconds < (26 * Duration.WEEK)
55
- 'old'
56
- else
57
- 'ancient'
58
-
59
- Handlebars.registerHelper 'radioButton', (object, id, name, value, selectedValue)->
60
- id = "#{object}_#{id}_#{name}_#{value}"
61
- input = "<input type=\"radio\" id=\"#{id}\" name=\"#{name}\" value=\"#{value}\""
62
- input = input + ' checked="checked"' if value == selectedValue
63
- "#{input} />"
64
-
65
45
  Handlebars.registerHelper 'userAvatar', (size)->
66
46
  user = window.user
67
47
  gravatarUrl = "https://www.gravatar.com/avatar/#{MD5(user.get('email').toLowerCase().trim())}?r=g&d=retro&s=#{size * 2}"
@@ -83,56 +63,6 @@ Handlebars.registerHelper 'ifEq', (v1, v2, block)->
83
63
 
84
64
 
85
65
 
86
- Handlebars.registerHelper 'timelineDateRange', (lastDate, date)->
87
- return Handlebars.helpers.timelineDate(date) unless lastDate
88
- days = (lastDate - date) / Duration.DAY
89
- return Handlebars.helpers.timelineDateAfterGap(date) if days >= 3
90
-
91
- _.inject [0...days],
92
- ((html, i)-> html + Handlebars.helpers.timelineDate((i + 1).days().before(lastDate)))
93
- , ''
94
-
95
- Handlebars.registerHelper 'timelineDate', (date)->
96
- format = d3.time.format """
97
- <div class="timeline-date">
98
- <span class="weekday">%a</span>
99
- <span class="month">%b</span>
100
- <span class="day">%-d</span>
101
- <span class="year">%Y</span>
102
- </div>
103
- """
104
- format toDate(date)
105
-
106
- Handlebars.registerHelper 'timelineTime', (time)->
107
- format = d3.time.format('<span class="timeline-event-time">%-I:%M%p</span>')
108
- format toDate(time)
109
- .replace(/[AP]M/, (str)-> str.toLowerCase()[0])
110
-
111
- Handlebars.registerHelper 'timelineDateAfterGap', (date)->
112
- '<div class="timeline-date-gap"></div>' + Handlebars.helpers.timelineDate(date)
113
-
114
- Handlebars.registerHelper 'timeline', (events, options)->
115
- lastDate = null
116
- events ?= []
117
- if events.length > 0
118
- html = '<div class="timeline">'
119
- for event in events
120
- date = App.truncateDate(toDate(event.date || event.time))
121
- html += Handlebars.helpers.timelineDateRange(lastDate, date)
122
- html += options.fn(event)
123
- lastDate = date
124
- html += '</div>'
125
- html
126
-
127
- Handlebars.registerHelper 'renderTaskWorker', (task)->
128
- if task.checkedOutByMe and !task.historical and task.open
129
- '<button class="btn btn-danger check-out-button active" data-toggle="button">Check in</button>'
130
- else if task.checkedOutBy
131
- Handlebars.helpers.avatar(task.checkedOutBy.email, 24, task.checkedOutBy.firstName) +
132
- " #{task.checkedOutBy.firstName}"
133
- else if task.open
134
- '<button class="btn btn-info check-out-button" data-toggle="button">Check out</button>'
135
-
136
66
  Handlebars.registerHelper 'coalesce', (value, valueIfBlank)->
137
67
  value ? valueIfBlank
138
68
 
@@ -142,6 +72,14 @@ Handlebars.registerHelper 'ifMe', (user, block)->
142
72
  else
143
73
  block.inverse(@)
144
74
 
75
+ Handlebars.registerHelper 'ifEql', (value1, value2, options)->
76
+ if value1 == value2
77
+ options.fn(@)
78
+
79
+ Handlebars.registerHelper 'ifIn', (value, array, options)->
80
+ if _.contains(array, value)
81
+ options.fn(@)
82
+
145
83
  Handlebars.registerHelper 'renderKeyCombos', (keys)->
146
84
  _.map(keys, Handlebars.helpers.renderKeyCombo).join('<i> or </i>')
147
85
 
@@ -38,141 +38,3 @@ $.fn.extend
38
38
 
39
39
  enable: ->
40
40
  $(@).find('input[type="submit"], input[type="reset"], button').removeAttr('disabled').end()
41
-
42
- getCursorPosition: ->
43
- input = @get(0)
44
- return unless input
45
- if input.selectionStart # Standard-compliant browsers
46
- input.selectionStart
47
- else if document.selection # IE
48
- input.focus()
49
- sel = document.selection.createRange()
50
- selLen = document.selection.createRange().text.length
51
- sel.moveStart 'character', -input.value.length
52
- sel.text.length - selLen
53
-
54
- appendAsAlert: ->
55
- $alerts = $('#body .alert')
56
- $newAlert = $(@)
57
- if $alerts.length > 0
58
- $('#body .alert').fadeOut
59
- complete: ->
60
- $(@).remove()
61
- $newAlert.prependTo($('#body')).alert()
62
- else
63
- $newAlert.prependTo($('#body')).alert()
64
-
65
- putCursorAtEnd: ->
66
- @each ->
67
- $(@).focus()
68
-
69
- # If this function exists...
70
- if @setSelectionRange
71
- # ... then use it (Doesn't work in IE)
72
-
73
- # Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh.
74
- len = $(@).val().length * 2
75
-
76
- @setSelectionRange(len, len)
77
- else
78
-
79
- # ... otherwise replace the contents with itself
80
- # (Doesn't work in Google Chrome)
81
-
82
- $(@).val($(@).val())
83
-
84
- insertAtCursor: (text)->
85
- textarea = @[0]
86
-
87
- # IE support
88
- if document.selection
89
- textarea.focus()
90
- sel = document.selection.createRange()
91
- sel.text = text
92
-
93
- # MOZILLA and others
94
- else if textarea.selectionStart || textarea.selectionStart == '0'
95
- startPos = textarea.selectionStart
96
- endPos = textarea.selectionEnd
97
- textarea.value = textarea.value.substring(0, startPos) + text + textarea.value.substring(endPos, textarea.value.length)
98
- textarea.selectionStart = startPos + text.length
99
- textarea.selectionEnd = startPos + text.length
100
-
101
- else
102
- textarea.value += text
103
-
104
- supportImages: ->
105
- $el = @
106
-
107
- $el.append '''
108
- <div class="drag-and-drop">
109
- Attach files or images by dragging &amp; dropping them or <a class="dz-selector">selecting them</a>.
110
- </div>
111
- <div class="upload-progress"></div>
112
- <div class="upload-error"></div>
113
- '''
114
-
115
- bucket = App.meta('s3-bucket')
116
- $el.dropzone
117
- maxFilesize: 13 # MB
118
- clickable: '.dz-selector'
119
- acceptedFiles: '.pdf,.zip,image/jpeg,image/png,image/gif'
120
- url: "//#{bucket}.s3.amazonaws.com"
121
- uploadprogress: (file, progress)->
122
- $el.find('.upload-progress').html "Uploading #{file.name} (#{progress.toFixed(0)}% complete)"
123
-
124
- complete: ->
125
- if @getUploadingFiles().length is 0 and @getQueuedFiles().length is 0
126
- $el.removeClass('uploading')
127
-
128
- error: (file, errorMessage)->
129
- $el.addClass('error')
130
- $el.find('.upload-error').html """
131
- <span class="message">#{errorMessage}</span>
132
- <a class="dz-selector">Try again</a>"
133
- """
134
-
135
- sending: (file, xhr, formData)=>
136
- $el.removeClass('error').addClass('uploading')
137
- $el.find('.upload-progress').html "Uploading #{file.name}..."
138
- $.ajax
139
- url: '/uploads/policies',
140
- data: {name: file.name, size: file.size, type: file.type},
141
- type: 'POST',
142
- async: false, # because we need this response before dropzone can continue
143
- success: (params)=>
144
- src = "https://s3.amazonaws.com/#{bucket}/#{params.key}"
145
- link_markdown = "[#{file.name}](#{src})"
146
- link_markdown = "!#{link_markdown}" unless _.contains(["application/pdf", "application/zip"], file.type)
147
- @.find('textarea').insertAtCursor link_markdown
148
- $.each params, (key, value)->
149
- formData.append(key, value)
150
-
151
-
152
-
153
- $.extend
154
-
155
- # Extend jQuery with functions for PUT and DELETE requests.
156
- put: (url, data, callback, type)->
157
- if jQuery.isFunction(data)
158
- callback = data
159
- data = {}
160
-
161
- data = data || {}
162
- data['_method'] = 'put'
163
- jQuery.post(url, data, callback, type)
164
-
165
- destroy: (url, data, callback, type)->
166
- if jQuery.isFunction(data)
167
- callback = data
168
- data = {}
169
-
170
- data = data || {}
171
- data._method = 'delete'
172
- jQuery.post(url, data, callback, type)
173
-
174
-
175
-
176
- $(document).ajaxSend (e, jqxhr, settings)->
177
- return if settings.type is 'GET'
178
- jqxhr.setRequestHeader 'X-CSRF-Token', App.meta('csrf-token')
@@ -0,0 +1,44 @@
1
+ toDate = (timestamp)->
2
+ return timestamp if _.isDate(timestamp)
3
+ new Date(timestamp)
4
+
5
+ Handlebars.registerHelper 'timelineDateRange', (lastDate, date)->
6
+ return Handlebars.helpers.timelineDate(date) unless lastDate
7
+ days = (lastDate - date) / Duration.DAY
8
+ return Handlebars.helpers.timelineDateAfterGap(date) if days >= 3
9
+
10
+ _.inject [0...days],
11
+ ((html, i)-> html + Handlebars.helpers.timelineDate((i + 1).days().before(lastDate)))
12
+ , ''
13
+
14
+ Handlebars.registerHelper 'timelineDate', (date)->
15
+ format = d3.time.format """
16
+ <div class="timeline-date">
17
+ <span class="weekday">%a</span>
18
+ <span class="month">%b</span>
19
+ <span class="day">%-d</span>
20
+ <span class="year">%Y</span>
21
+ </div>
22
+ """
23
+ format toDate(date)
24
+
25
+ Handlebars.registerHelper 'timelineTime', (time)->
26
+ format = d3.time.format('<span class="timeline-event-time">%-I:%M%p</span>')
27
+ format toDate(time)
28
+ .replace(/[AP]M/, (str)-> str.toLowerCase()[0])
29
+
30
+ Handlebars.registerHelper 'timelineDateAfterGap', (date)->
31
+ '<div class="timeline-date-gap"></div>' + Handlebars.helpers.timelineDate(date)
32
+
33
+ Handlebars.registerHelper 'timeline', (events, options)->
34
+ lastDate = null
35
+ events ?= []
36
+ if events.length > 0
37
+ html = '<div class="timeline">'
38
+ for event in events
39
+ date = App.truncateDate(toDate(event.date || event.time))
40
+ html += Handlebars.helpers.timelineDateRange(lastDate, date)
41
+ html += options.fn(event)
42
+ lastDate = date
43
+ html += '</div>'
44
+ html
@@ -0,0 +1,99 @@
1
+ $.fn.extend
2
+
3
+ getCursorPosition: ->
4
+ input = @get(0)
5
+ return unless input
6
+ if input.selectionStart # Standard-compliant browsers
7
+ input.selectionStart
8
+ else if document.selection # IE
9
+ input.focus()
10
+ sel = document.selection.createRange()
11
+ selLen = document.selection.createRange().text.length
12
+ sel.moveStart 'character', -input.value.length
13
+ sel.text.length - selLen
14
+
15
+ putCursorAtEnd: ->
16
+ @each ->
17
+ $(@).focus()
18
+
19
+ # If this function exists...
20
+ if @setSelectionRange
21
+ # ... then use it (Doesn't work in IE)
22
+
23
+ # Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh.
24
+ len = $(@).val().length * 2
25
+
26
+ @setSelectionRange(len, len)
27
+ else
28
+
29
+ # ... otherwise replace the contents with itself
30
+ # (Doesn't work in Google Chrome)
31
+
32
+ $(@).val($(@).val())
33
+
34
+ insertAtCursor: (text)->
35
+ textarea = @[0]
36
+
37
+ # IE support
38
+ if document.selection
39
+ textarea.focus()
40
+ sel = document.selection.createRange()
41
+ sel.text = text
42
+
43
+ # MOZILLA and others
44
+ else if textarea.selectionStart || textarea.selectionStart == '0'
45
+ startPos = textarea.selectionStart
46
+ endPos = textarea.selectionEnd
47
+ textarea.value = textarea.value.substring(0, startPos) + text + textarea.value.substring(endPos, textarea.value.length)
48
+ textarea.selectionStart = startPos + text.length
49
+ textarea.selectionEnd = startPos + text.length
50
+
51
+ else
52
+ textarea.value += text
53
+
54
+ supportImages: ->
55
+ $el = @
56
+
57
+ $el.append '''
58
+ <div class="drag-and-drop">
59
+ Attach files or images by dragging &amp; dropping them or <a class="dz-selector">selecting them</a>.
60
+ </div>
61
+ <div class="upload-progress"></div>
62
+ <div class="upload-error"></div>
63
+ '''
64
+
65
+ bucket = App.meta('s3-bucket')
66
+ $el.dropzone
67
+ maxFilesize: 13 # MB
68
+ clickable: '.dz-selector'
69
+ acceptedFiles: '.pdf,.zip,image/jpeg,image/png,image/gif'
70
+ url: "//#{bucket}.s3.amazonaws.com"
71
+ uploadprogress: (file, progress)->
72
+ $el.find('.upload-progress').html "Uploading #{file.name} (#{progress.toFixed(0)}% complete)"
73
+
74
+ complete: ->
75
+ if @getUploadingFiles().length is 0 and @getQueuedFiles().length is 0
76
+ $el.removeClass('uploading')
77
+
78
+ error: (file, errorMessage)->
79
+ $el.addClass('error')
80
+ $el.find('.upload-error').html """
81
+ <span class="message">#{errorMessage}</span>
82
+ <a class="dz-selector">Try again</a>"
83
+ """
84
+
85
+ sending: (file, xhr, formData)=>
86
+ $el.removeClass('error').addClass('uploading')
87
+ $el.find('.upload-progress').html "Uploading #{file.name}..."
88
+ $.ajax
89
+ url: '/uploads/policies',
90
+ data: {name: file.name, size: file.size, type: file.type},
91
+ type: 'POST',
92
+ async: false, # because we need this response before dropzone can continue
93
+ success: (params)=>
94
+ src = "https://s3.amazonaws.com/#{bucket}/#{params.key}"
95
+ link_markdown = "[#{file.name}](#{src})"
96
+ link_markdown = "!#{link_markdown}" unless _.contains(["application/pdf", "application/zip"], file.type)
97
+ @.find('textarea').insertAtCursor link_markdown
98
+ $.each params, (key, value)->
99
+ formData.append(key, value)
@@ -10,20 +10,37 @@
10
10
  // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
11
  // about supported directives.
12
12
  //
13
+ //* Houston's client-side stack
13
14
  //= require underscore
14
15
  //= require backbone
15
16
  //= require handlebars
16
- //= require d3
17
- //= require jquery-ui
17
+ //= require neat
18
18
  //= require bootstrap
19
- //= require jquery.pjax.js
20
- //= require jquery.tablesorter.js
21
- //= require jquery.autosize.js
22
- //= require jquery.timeago.js
23
- //= require mousetrap
24
- //= require nested_editor_for
19
+ //= require jquery-ui
20
+ //
21
+ //* for graphics
22
+ //= require d3
23
+ //
24
+ //* for rendering Markdown
25
25
  //= require showdown
26
- //= require neat
26
+ //
27
+ //* for showing Flash messages
27
28
  //= require alertify
29
+ //
30
+ //* for accepting files
28
31
  //= require dropzone
32
+ //
33
+ //* for Houston's mobile navigation
29
34
  //= require slideout
35
+ //
36
+ //* for rendering simple tables
37
+ //= require jquery.tablesorter.js
38
+ //
39
+ //* for formatting times as elapsed, `formatTimeAgo` helper
40
+ //= require jquery.timeago.js
41
+ //
42
+ //* for defining keyboard shortcuts
43
+ //= require mousetrap
44
+ //
45
+ //* for autogrowing textareas
46
+ //= require jquery.autosize.js