help-anywhere 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +46 -0
  4. data/Rakefile +38 -0
  5. data/app/assets/javascripts/help_anywhere/components/bubble.js.coffee +367 -0
  6. data/app/assets/javascripts/help_anywhere/components/component.js.coffee +10 -0
  7. data/app/assets/javascripts/help_anywhere/help_anywhere.js.coffee +254 -0
  8. data/app/assets/javascripts/help_anywhere/help_anywhere_editor.js.coffee +41 -0
  9. data/app/assets/javascripts/help_anywhere/hooks.js.coffee +28 -0
  10. data/app/assets/javascripts/help_anywhere.js.coffee +2 -0
  11. data/app/assets/javascripts/help_anywhere_core_only.js.coffee +11 -0
  12. data/app/assets/stylesheets/generators/bubble.css.scss +195 -0
  13. data/app/assets/stylesheets/help_anywhere.css.scss +4 -0
  14. data/app/assets/stylesheets/help_anywhere_core_only.css.scss +181 -0
  15. data/app/controllers/help_anywhere/base_controller.rb +10 -0
  16. data/app/controllers/help_anywhere/help_anywhere_controller.rb +5 -0
  17. data/app/controllers/help_anywhere/pages_controller.rb +30 -0
  18. data/app/controllers/help_anywhere/resources_controller.rb +84 -0
  19. data/app/helpers/help_anywhere_helper.rb +5 -0
  20. data/app/models/help_anywhere/item.rb +6 -0
  21. data/app/models/help_anywhere/page.rb +34 -0
  22. data/app/models/help_anywhere/resource.rb +14 -0
  23. data/app/models/help_anywhere.rb +2 -0
  24. data/config/routes.rb +11 -0
  25. data/db/migrate/20130825093318_create_help_anywhere_resources.rb +9 -0
  26. data/db/migrate/20130825093414_create_help_anywhere_pages.rb +11 -0
  27. data/db/migrate/20130825093611_create_help_anywhere_items.rb +19 -0
  28. data/db/migrate/20130927171847_refactor_help_anywhere_items.rb +11 -0
  29. data/lib/help-anywhere/configuration.rb +75 -0
  30. data/lib/help-anywhere/engine.rb +10 -0
  31. data/lib/help-anywhere/version.rb +3 -0
  32. data/lib/help-anywhere.rb +9 -0
  33. data/lib/tasks/help-anywhere_tasks.rake +4 -0
  34. data/test/dummy/README.rdoc +261 -0
  35. data/test/dummy/Rakefile +7 -0
  36. data/test/dummy/app/assets/javascripts/application.js +15 -0
  37. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  38. data/test/dummy/app/controllers/application_controller.rb +3 -0
  39. data/test/dummy/app/helpers/application_helper.rb +2 -0
  40. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  41. data/test/dummy/config/application.rb +59 -0
  42. data/test/dummy/config/boot.rb +10 -0
  43. data/test/dummy/config/database.yml +25 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +37 -0
  46. data/test/dummy/config/environments/production.rb +67 -0
  47. data/test/dummy/config/environments/test.rb +37 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/dummy/config/initializers/inflections.rb +15 -0
  50. data/test/dummy/config/initializers/mime_types.rb +5 -0
  51. data/test/dummy/config/initializers/secret_token.rb +7 -0
  52. data/test/dummy/config/initializers/session_store.rb +8 -0
  53. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  54. data/test/dummy/config/locales/en.yml +5 -0
  55. data/test/dummy/config/routes.rb +58 -0
  56. data/test/dummy/config.ru +4 -0
  57. data/test/dummy/public/404.html +26 -0
  58. data/test/dummy/public/422.html +26 -0
  59. data/test/dummy/public/500.html +25 -0
  60. data/test/dummy/public/favicon.ico +0 -0
  61. data/test/dummy/script/rails +6 -0
  62. data/test/help-anywhere_test.rb +7 -0
  63. data/test/test_helper.rb +15 -0
  64. metadata +165 -0
@@ -0,0 +1,254 @@
1
+ do($ = jQuery) ->
2
+ HELP_TEMPLATE = (args) ->
3
+ """
4
+ <div id="help-anywhere-widget">
5
+ <div class="ha-std-button" title="Help">?</div>
6
+ </div>
7
+ """
8
+
9
+ HELP_INTERFACE_TEMPLATE = (pages, edit_mode) ->
10
+ """
11
+ <div id="help-anywhere-layout">
12
+ <div class="ha-page-content"></div>
13
+ #{
14
+ if edit_mode
15
+ HELP_INTERFACE_EDIT_MODE(pages)
16
+ else ''
17
+ }
18
+ </div>
19
+ """
20
+
21
+ HELP_INTERFACE_EDIT_MODE = (pages) ->
22
+ """
23
+ <div class="ha-edit-bottom-layout">
24
+ <div class="ha-new-component">
25
+ Add component:
26
+ <select class="ha-add-component">
27
+ #{HELP_SELECT_COMPONENT()}
28
+ </select>
29
+ <input type="button" class="ha-save-btn btn btn-primary" value="Save"></input>
30
+ <input type="button" class="ha-remove-page-btn btn btn-danger" value="Remove page"></input>
31
+ <input type="button" class="ha-exit-btn btn btn-danger" value="Exit"></input>
32
+ </div>
33
+ <div class="ha-page-list">
34
+ #{HELP_INTERFACE_PAGES(pages)}
35
+ </div>
36
+ </div>
37
+ """
38
+
39
+ HELP_SELECT_COMPONENT = ->
40
+ "<option value='' selected='selected'>Select a component</option>" + (
41
+ for k,v of HelpAnywhere.components
42
+ "<option value='#{k}'>#{k}</option>"
43
+ ).join("")
44
+
45
+ HELP_INTERFACE_PAGES = (pages) ->
46
+ (for page, idx in pages
47
+ HELP_INTERFACE_PAGE(idx) ).join('') +
48
+ """
49
+ <div class="ha-new-page">+</div>
50
+ """
51
+
52
+ HELP_INTERFACE_PAGE = (idx) -> """<div class="ha-page" data-idx="#{idx}">#{idx}</div>"""
53
+
54
+ class HelpAnywhere
55
+ @API_URL: "/help_anywhere"
56
+
57
+ @render: ->
58
+ @instance = new HelpAnywhere
59
+ @instance.init()
60
+ @show: (edit_mode=false) ->
61
+ if @instance
62
+ @instance.openHelpInterface(edit_mode)
63
+ return true
64
+ else
65
+ return false
66
+ @hide: ->
67
+ @instance.closeHelpInterface()
68
+ @edit: -> @show(yes)
69
+
70
+ @saveElement: (elm) ->
71
+ for p in @instance.pageList
72
+ for e, idx in p.items
73
+ if e.id is elm.id
74
+ @instance.elementsChanged = true
75
+ p.items[idx].content = elm.getData()
76
+ return
77
+
78
+ @addRoutes: (routes) ->
79
+ @routes = (@routes ? [])
80
+
81
+ if not routes instanceof Array
82
+ routes = [routes]
83
+
84
+ for [regexp, value] in routes
85
+ regexp = '^' +
86
+ regexp
87
+ .replace('/', '\\/')
88
+ .replace('.', '\\.')
89
+ .replace('**', '.*')
90
+ .replace('*', '[^\\/]*')
91
+ .replace('??', '(.*)')
92
+ .replace('?', '([^\\/]*)') + '$'
93
+ @routes.push [new RegExp(regexp), value]
94
+
95
+ @deleteItem: (elm) ->
96
+ for p in @instance.pageList
97
+ for e, idx in p.items
98
+ if e.id is elm.id
99
+ p.items.splice(idx, 1)
100
+ elm.remove()
101
+ return true
102
+
103
+ selectRoute: (path) ->
104
+ for [regexp, value] in HelpAnywhere.routes
105
+ return path.replace(regexp, value) if regexp.test(path)
106
+
107
+ return false
108
+
109
+ elementsChanged: false
110
+
111
+ init: ->
112
+ @fixBody()
113
+ @retrieveResource()
114
+
115
+ fixBody: ->
116
+ $("body").css
117
+ position: "absolute"
118
+ left: 0
119
+ top: 0
120
+ "min-width": "100%"
121
+ "min-height": "100%"
122
+ margin: 0
123
+
124
+ buildInterface: ->
125
+ unless @help?
126
+ @help = $(HELP_TEMPLATE())
127
+ @help.find('.ha-std-button').on 'click', =>
128
+ @openHelpInterface()
129
+ @help.css(display: 'block', opacity: '0')
130
+ .animate(opacity: '1', 2000)
131
+ .appendTo($('body'))
132
+
133
+ showPage: (pageIndex) ->
134
+ @helpInterface.find('.ha-page-content').empty()
135
+
136
+ @helpInterface.find('.ha-page').removeClass('selected')
137
+ @helpInterface.find(".ha-page:nth-child(#{Number(pageIndex)+1})").addClass('selected')
138
+
139
+ items = @pageList[pageIndex].items
140
+
141
+ for item in items
142
+ g = new HelpAnywhere.components[item.class_id]()
143
+ g.id = item.id
144
+ g.class_id = item.class_id
145
+ g.load(item.content)
146
+
147
+ @helpInterface.find('.ha-page-content').append(g.build(@edit_mode))
148
+
149
+ movePage: (fromIndex, toIndex) ->
150
+ #Magic coffee <3
151
+ [@pageList[fromIndex], @pageList[toIndex]] = [@pageList[toIndex], @pageList[fromIndex]]
152
+ @currentPage = toIndex
153
+ @showPage(@currentPage) #Refreshing interface for edition mode
154
+
155
+
156
+ deletePage: ->
157
+ pageIndex = Number(@helpInterface.find('.ha-page.selected').remove().attr('data-idx'))
158
+ @pageList.splice(pageIndex, 1)
159
+
160
+ #Refreshing the indices
161
+ for page, idx in @helpInterface.find('.ha-page')
162
+ $(page).text(idx).attr('data-idx', idx)
163
+
164
+ @currentPage = pageIndex - 1
165
+
166
+ if @currentPage < 0
167
+ @currentPage = 0
168
+
169
+ if @pageList.length > 0
170
+ @showPage(@currentPage)
171
+
172
+ createNewPage: ->
173
+ @pageList.push items: [], number: @pageList[@pageList.length-1].number+1
174
+
175
+ newPage = $(HELP_INTERFACE_PAGE(@pageList.length-1))
176
+ newPage.insertBefore(@helpInterface.find('.ha-new-page'))
177
+ newPage.on 'click', =>
178
+ @currentPage = newPage.attr('data-idx')
179
+ @showPage(@currentPage)
180
+ @showPage(newPage.attr('data-idx'))
181
+
182
+ temporaryId: (pageIndex) ->
183
+ @_uid ?= 0
184
+ return "_#{@_uid++}" #Temporary ids are discriminated by adding underscore before.
185
+
186
+ createNewComponent: (pageIndex, componentClass) ->
187
+ if componentClass? and componentClass isnt ''
188
+ component = new HelpAnywhere.components[componentClass]()
189
+ @helpInterface.find('.ha-page-content').append(component.build(@edit_mode))
190
+
191
+ component.class_id = componentClass
192
+ component.id = @temporaryId()
193
+
194
+ @pageList[pageIndex].items.push {id: component.id, class_id: componentClass}
195
+ closeHelpInterface: ->
196
+ if @elementsChanged
197
+ return unless confirm("Are you sure? All changes will be losts...")
198
+ @helpInterface.remove()
199
+ @helpInterface = null
200
+
201
+ openHelpInterface: (@edit_mode=false)->
202
+ @helpInterface ?= $ HELP_INTERFACE_TEMPLATE(@pageList, @edit_mode)
203
+ @helpInterface.appendTo($("body"))
204
+ @currentPage = 0
205
+ self = this
206
+
207
+ unless @edit_mode
208
+ @helpInterface.on 'click', =>
209
+ @currentPage += 1
210
+
211
+ if @currentPage >= @pageList.length
212
+ @helpInterface.remove()
213
+ @helpInterface = null
214
+ else
215
+ @showPage(@currentPage)
216
+ else
217
+ @helpInterface.on 'click', -> HelpAnywhereEditor.focus(null)
218
+ @helpInterface.find('.ha-page').on 'click', ->
219
+ self.currentPage = $(this).attr('data-idx')
220
+ self.showPage(self.currentPage)
221
+ @helpInterface.find(".ha-new-page").on 'click', ->
222
+ self.createNewPage()
223
+ @helpInterface.find('.ha-add-component').on 'change', ->
224
+ self.createNewComponent(self.currentPage, $(this).val())
225
+ $(this).val('')
226
+ @helpInterface.find('.ha-save-btn').on 'click', @saveHelp.bind(@)
227
+ @helpInterface.find('.ha-exit-btn').on 'click', @closeHelpInterface.bind(@)
228
+
229
+ @helpInterface.find('.ha-remove-page-btn').on 'click', @deletePage.bind(@)
230
+
231
+ @showPage(@currentPage)
232
+
233
+ saveHelp: ->
234
+ $.post("#{HelpAnywhere.API_URL}/resources/save?name=#{@resource}", content: JSON.stringify(@pageList))
235
+ .success =>
236
+ @elementsChanged = false
237
+ .error =>
238
+ alert('Sorry, but something went wrong')
239
+
240
+ retrieveResource: (resource) ->
241
+ @resource = resource || @selectRoute window.location.pathname
242
+
243
+ if @help?
244
+ @help.css(display: 'none')
245
+
246
+
247
+ if @resource
248
+ $.get("#{HelpAnywhere.API_URL}/resources/?name=#{@resource}").success (data) =>
249
+ if data.pages? and data.pages.length > 0
250
+ @pageList = data.pages
251
+ @buildInterface()
252
+
253
+ window.HelpAnywhere = HelpAnywhere
254
+ window.HelpAnywhere.components = {}
@@ -0,0 +1,41 @@
1
+ do($ = jQuery) ->
2
+ current_mouse_move_target = null
3
+ hook_enabled = true
4
+
5
+ HelpAnywhereEditor =
6
+ focus: (elm) ->
7
+ @focused?.blur?()
8
+ @focused = elm
9
+ render: ->
10
+ @hook()
11
+ hook: ->
12
+ @hook_mouse_move()
13
+ @hook_mouse_click()
14
+ hook_mouse_move: ->
15
+ $('body').mousemove (evt) ->
16
+ return unless hook_enabled
17
+
18
+ $(current_mouse_move_target).removeClass('ha-hover')
19
+
20
+ id = $(evt.target).attr('id')
21
+
22
+ if id? and id isnt ''
23
+ $(evt.target).addClass('ha-hover')
24
+ current_mouse_move_target = evt.target
25
+ else
26
+ nearest_identifiable = do ->
27
+ parents = $(evt.target).parents()
28
+ console.log parents.toArray().map((x) -> $(x).attr('id')).join(', ')
29
+ for ancestor in parents
30
+ ancestor_id = $(ancestor).attr('id')
31
+ if ancestor_id? and id isnt ''
32
+ return ancestor
33
+ return null
34
+ $(nearest_identifiable).addClass('ha-hover')
35
+ current_mouse_move_target = nearest_identifiable
36
+
37
+ hook_mouse_click: ->
38
+ $('body').mousemove (evt) ->
39
+ return unless hook_enabled
40
+
41
+ window.HelpAnywhereEditor = HelpAnywhereEditor
@@ -0,0 +1,28 @@
1
+ #TODO
2
+ jQuery ($) ->
3
+
4
+ current_mouse_move_target = null
5
+
6
+ $('html, body').mousemove (evt) ->
7
+ return unless HelpAnywhereEditor.hook_enabled
8
+
9
+ $(current_mouse_move_target).removeClass('ha-hover')
10
+
11
+ id = $(evt.target).attr('id')
12
+
13
+ if id? and id isnt ''
14
+ $(evt.target).addClass('ha-hover')
15
+ current_mouse_move_target = evt.target
16
+ else
17
+ nearest_identifiable = do ->
18
+ parents = $(evt.target).parents()
19
+ for ancestor in parents
20
+ ancestor_id = $(ancestor).attr('id')
21
+ if ancestor_id? and id isnt ''
22
+ return ancestor
23
+ return null
24
+
25
+ $('html, body').click (evt) ->
26
+ return unless HelpAnywhereEditor.hook_enabled
27
+
28
+
@@ -0,0 +1,2 @@
1
+ #= require help_anywhere_core_only
2
+ #= require help_anywhere/components/bubble
@@ -0,0 +1,11 @@
1
+ #
2
+ # This is the entry point for help_anywhere plugin
3
+ #
4
+ #
5
+ #= require help_anywhere/help_anywhere
6
+ #= require help_anywhere/help_anywhere_editor
7
+ #= require help_anywhere/components/component
8
+ jQuery ($) ->
9
+ $.get('/help_anywhere/routes').success (data)->
10
+ HelpAnywhere.addRoutes(data)
11
+ HelpAnywhere.render()
@@ -0,0 +1,195 @@
1
+ .ha-bubble-box {
2
+ -webkit-touch-callout: none;
3
+ -webkit-user-select: none;
4
+ -khtml-user-select: none;
5
+ -moz-user-select: none;
6
+ -ms-user-select: none;
7
+ user-select: none;
8
+
9
+ padding: 10px;
10
+ background-color: white;
11
+ border-radius: 12px;
12
+ border: solid 3px black;
13
+ box-shadow: 4px 4px 9px rgba(0,0,0,0.3);
14
+ position: absolute;
15
+ display: block;
16
+ width: 100px;
17
+
18
+ .ha-bubble-anchor {
19
+ position: absolute;
20
+ display: none;
21
+ width: 6px;
22
+ height: 6px;
23
+
24
+ border: dotted 2px blue;
25
+ margin: -4px;
26
+
27
+ cursor: pointer;
28
+
29
+ &:hover {
30
+ border: solid 2px blue;
31
+ background-color: white;
32
+ }
33
+
34
+ &.x {
35
+ border: solid 2px blue;
36
+ border-radius: 50%;
37
+ left: 50%; top: 50%;
38
+ cursor: move;
39
+ }
40
+
41
+ &.nw {
42
+ top: 0%; left: 0%;
43
+ cursor: nw-resize;
44
+ }
45
+ &.n {
46
+ top: 0%; left: 50%;
47
+ cursor: n-resize;
48
+ }
49
+
50
+ &.ne {
51
+ top: 0; left: 100%;
52
+ cursor: ne-resize;
53
+ }
54
+
55
+ &.e {
56
+ top: 50%; left: 100%;
57
+ }
58
+
59
+ &.se {
60
+ top: 100%; left: 100%;
61
+ cursor: se-resize;
62
+ }
63
+
64
+ &.s {
65
+ top: 100%; left: 50%;
66
+ cursor: s-resize;
67
+ }
68
+
69
+ &.sw {
70
+ top: 100%; left: 0%;
71
+ cursor: sw-resize;
72
+ }
73
+
74
+ &.w {
75
+ top: 50%; left: 0%;
76
+ cursor: w-resize;
77
+ }
78
+ }
79
+
80
+ .ha-bubble-content {
81
+ &:focus {
82
+ -webkit-touch-callout: auto;
83
+ -webkit-user-select: auto;
84
+ -khtml-user-select: auto;
85
+ -moz-user-select: auto;
86
+ -ms-user-select: auto;
87
+ user-select: auto;
88
+ text-align: left;
89
+ }
90
+
91
+ text-align: justify;
92
+ padding: 0;
93
+ margin: 0;
94
+ }
95
+
96
+ .ha-bubble-edit-box {
97
+ display: none;
98
+ background-color: #333;
99
+ color: white;
100
+
101
+ width: 175px;
102
+ height: auto;
103
+ padding: 5px;
104
+
105
+ position: absolute;
106
+ margin-left: 10px;
107
+ top: 0;
108
+ left: 100%;
109
+ z-index: 1;
110
+
111
+
112
+ opacity: 0.8;
113
+
114
+ transition: opacity ease-in-out 0.3s;
115
+ -moz-transition: opacity ease-in-out 0.3s;
116
+ -o-transition: opacity ease-in-out 0.3s;
117
+ -webkit-transition: opacity ease-in-out 0.3s;
118
+ -ms-transition: opacity ease-in-out 0.3s;
119
+
120
+ &:hover {
121
+ opacity: 1;
122
+ }
123
+
124
+ select, input {
125
+ width: 100%;
126
+ }
127
+
128
+ input {
129
+ border: solid 1px #EEE;
130
+ }
131
+ }
132
+
133
+ .ha-bubble-pointer {
134
+ position: absolute;
135
+
136
+ width: 15px;
137
+ height: 15px;
138
+ background-color: white;
139
+
140
+ -webkit-transform: rotate(-45deg);
141
+ -moz-transform: rotate(-45deg);
142
+ -o-transform: rotate(-45deg);
143
+ -ms-transform: rotate(-45deg);
144
+ transform: rotate(-45deg);
145
+
146
+ /*
147
+ * Note: right, left, bottom and top refer at the position
148
+ * of the target element, not the pointer itself.
149
+ */
150
+ &.pointer-right {
151
+ border-top: solid 3px black;
152
+ border-left: solid 3px black;
153
+
154
+ margin-top: -9px;
155
+ margin-left: -11px;
156
+
157
+
158
+ top: 50%;
159
+ left: 0;
160
+ }
161
+
162
+ &.pointer-left {
163
+ border-bottom: solid 3px black;
164
+ border-right: solid 3px black;
165
+
166
+ margin-top: -9px;
167
+ margin-right: -11px;
168
+
169
+ top: 50%;
170
+ right: 0;
171
+ }
172
+
173
+ &.pointer-bottom {
174
+ border-top: solid 3px black;
175
+ border-right: solid 3px black;
176
+
177
+ margin-top: -11px;
178
+ margin-left: -9px;
179
+
180
+ top: 0;
181
+ left: 50%;
182
+ }
183
+
184
+ &.pointer-top {
185
+ border-bottom: solid 3px black;
186
+ border-left: solid 3px black;
187
+
188
+ margin-bottom: -11px;
189
+ margin-left: -9px;
190
+
191
+ bottom: 0;
192
+ left: 50%;
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,4 @@
1
+ /*
2
+ *=require help_anywhere_core_only
3
+ *=require generators/bubble
4
+ */