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
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NGM2ZjIxMjMyNzViM2UzYjk0NzJjY2QxNGE1NGE2NDQwYzk1ZDIzYw==
5
+ data.tar.gz: !binary |-
6
+ ODliNTg3YTJkZmU0N2Y2MzM2Mzg3Y2QxYWQ3NTNiN2JmODcxNDAwMg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZTdhZjc0NjI0YzBiZWNjYzEzYzkwNTU1NDQwY2VhNzFjNGE3ZWVmMjQwYzA3
10
+ ZGM2ZDFlZWU4MGM2NjVkN2NlYTFkYWVmNGZjNTkzMDc3MDAwYWYzNDg2ZTg5
11
+ NjI4MjMwZjJmOWQ2NzBhMmU5MTU1ODkxMTZkNmUwMTNhODdiNzA=
12
+ data.tar.gz: !binary |-
13
+ YmFiZmZlMWMxZmFmNjhjNmRiYzliYjVhMmU2YmZiNTRhMzVlMzA2NTI5NjBm
14
+ NzVjOTk5NzU4NTQyZTIwMTNmZjRiNjk1MzYwOTRjOGZlNTUxYzUwYmE4NGIw
15
+ Nzc0ODAyODc1N2ZmMGJhNjhiNzlhY2YzNTJlOWUxZTU5Yjc2MWI=
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Yacine PETITPREZ
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,46 @@
1
+ = Help Anywhere
2
+
3
+ This plugin for rails provides a powerful help system for every websites
4
+ built with ruby on rails.
5
+
6
+ Users can read the help, and administrator can update help pages.
7
+
8
+ You can select on or more elements in your website,
9
+ add help box on it, save it, create "step by step" help page.
10
+
11
+ == Usage
12
+
13
+ Add in your Gemfile:
14
+
15
+
16
+ gem 'help-anywhere'
17
+
18
+
19
+ Then
20
+
21
+ bundle install
22
+ rake help-anywhere:install:migrations
23
+ rake help-anywhere:setup
24
+ rake db:migrate
25
+
26
+
27
+ Finish by setup css and javascript files in your application.js/css:
28
+
29
+ //= require help_anywhere
30
+
31
+
32
+ That's all!
33
+
34
+ Finally, update the file `config/help-anywhere.rb` to change the permission strategy and the help routes!
35
+ See help-anywhere-example project for more information!
36
+
37
+ Log-in has edit mode, and create new help page with our editor. Enjoy!
38
+
39
+ == Version
40
+
41
+ 0.1.1
42
+
43
+ - This is the first stable release!
44
+ - HelpAnywhere is provided with Bubble component, but you can also build your own. Check `app/javascript/bubble.coffee.js`
45
+ for more informations.
46
+ You can ignore this component. Just `require help_anywhere_core_only` in both application.js and .css
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'HelpAnywhere'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task :default => :test
@@ -0,0 +1,367 @@
1
+ ###
2
+ help-anywhere's bubble text content.
3
+
4
+ This is for now the only component bundled with the project.
5
+ It looks like a comic book bubble.
6
+
7
+ You can take as example for new components.
8
+ Here we code all logic for show and edit mode.
9
+ ###
10
+ do ( $ = jQuery ) ->
11
+ BUBBLE_TEMPLATE = -> """
12
+ <div class="ha-bubble-box">
13
+ <div class="ha-bubble-pointer"></div>
14
+ <div class="ha-bubble-content">#{@content}</div>
15
+ </div>
16
+ """
17
+
18
+ ANCHOR_TEMPLATE = (position) -> """
19
+ <div class="ha-bubble-anchor #{@position}">"
20
+ """
21
+
22
+ EDIT_TEMPLATE = -> """
23
+ <div class="ha-bubble-edit-box">
24
+ Position:
25
+ <select class="ha-bubble-position">
26
+ <option value="free">Free</option>
27
+ <option value="left">Left</option>
28
+ <option value="top">Top</option>
29
+ <option value="right">Right</option>
30
+ <option value="bottom">Bottom</option>
31
+ <option value="guess">Auto</option>
32
+ </select>
33
+ Selector:
34
+ <input type="text" class="ha-bubble-selector" value="">
35
+ <input type="button" class="ha-bubble-remove btn btn-danger" value="Remove">
36
+ </div>
37
+ """
38
+
39
+ # Just constants to get named into array for d&d of resize handling
40
+ X = 0
41
+ Y = 1
42
+ LEFT = 0
43
+ TOP = 1
44
+ WIDTH = 2
45
+ HEIGHT = 3
46
+
47
+ class Bubble extends window.HelpAnywhere.Component
48
+ constructor: ->
49
+ @content = "Add text here"
50
+ @drag =
51
+ hasStarted: no
52
+ startOffset: [0,0]
53
+ startCoords: [0,0,0,0]
54
+
55
+ load: (data) ->
56
+ $.extend this, data
57
+
58
+ build: (editMode) ->
59
+ @elm = $(BUBBLE_TEMPLATE.call(this))
60
+
61
+ @editMode = editMode
62
+
63
+ #Render the handlers for edition mode as needed
64
+ @buildEditMode() if editMode
65
+
66
+ @elm.addClass(@htmlClass) if @htmlClass?
67
+
68
+ #Temporary append to body to check the size.
69
+ $('body').append(@elm)
70
+
71
+ #Focusing element
72
+ if @editMode
73
+ @elm.on 'click', (evt) =>
74
+ @refresh()
75
+
76
+ evt.stopPropagation()
77
+
78
+ HelpAnywhereEditor.focus(this)
79
+
80
+ for k,anchor of @anchors
81
+ anchor.css display: 'block'
82
+
83
+ @control.css display: 'block'
84
+
85
+ if @isBinded()
86
+ @anchors.x.css display: 'none'
87
+
88
+ return @refresh()
89
+
90
+ isBinded: ->
91
+ @target and $(@target).length > 0 and @position isnt 'free'
92
+
93
+ blur: ->
94
+ @control.css display: 'none'
95
+
96
+ for k,anchor of @anchors
97
+ anchor.css display: 'none'
98
+
99
+ refresh: ->
100
+ @elm.css(height: @height) if @height!=null
101
+
102
+ @targetArea ?= $("<div class='ha-bubble-target-selected'>")
103
+
104
+ #We set a default width if no width is set, to avoid some render problem
105
+ if @width?
106
+ @elm.css(width: @width)
107
+ else
108
+ @elm.css(width: '150px')
109
+
110
+ if @isBinded()
111
+ pos = $(@target).first().offset()
112
+
113
+ objectSize = [$(@target).outerWidth(), $(@target).outerHeight()]
114
+ documentSize = [$("body").outerWidth(), $("body").outerHeight()]
115
+ bubbleSize = [@elm.outerWidth(), @elm.outerHeight()]
116
+
117
+ boundingBox =
118
+ x: parseFloat(pos.left)
119
+ y: parseFloat(pos.top)
120
+ w: parseFloat(objectSize[0])
121
+ h: parseFloat(objectSize[1])
122
+
123
+ boundingBox.x2 = boundingBox.x + boundingBox.w
124
+ boundingBox.y2 = boundingBox.y + boundingBox.h
125
+
126
+ docW = parseFloat(documentSize[0])
127
+ docH = parseFloat(documentSize[1])
128
+
129
+ unless @position || @position is 'guess' #Guess mode
130
+ ###
131
+ x,y-------x2,y
132
+ | |
133
+ | |
134
+ | |
135
+ x,y2-----x2,y2
136
+ ###
137
+ leftSpace = boundingBox.x
138
+ rightSpace = docW - boundingBox.x2
139
+
140
+ topSpace = boundingBox.y
141
+ bottomSpace = docH - boundingBox.y2
142
+
143
+ values = right: rightSpace, top: topSpace, bottom: bottomSpace
144
+
145
+ selectedPosition = 'left'
146
+ selectedValue = leftSpace
147
+ for k,v of values
148
+ if v > selectedValue
149
+ selectedValue = v
150
+ selectedPosition = k
151
+
152
+ finalPosition = selectedPosition
153
+ else
154
+ finalPosition = @position
155
+
156
+ switch finalPosition
157
+ when 'top'
158
+ @elm.css
159
+ top: (boundingBox.y - bubbleSize[1]) + "px"
160
+ left: boundingBox.x + Math.round((boundingBox.w-bubbleSize[0])/2) + "px"
161
+ 'margin-top': '-14px'
162
+ 'margin-left': '0px'
163
+ when 'bottom'
164
+ @elm.css
165
+ top: boundingBox.y2 + "px"
166
+ left: boundingBox.x + Math.round((boundingBox.w-bubbleSize[0])/2) + "px"
167
+ 'margin-top': '14px'
168
+ 'margin-left': '0px'
169
+ when 'right'
170
+ @elm.css
171
+ top: boundingBox.y + Math.round((boundingBox.h-bubbleSize[1])/2) + "px"
172
+ left: boundingBox.x2 + "px"
173
+ 'margin-left': '14px'
174
+ 'margin-top': '0px'
175
+ else
176
+ @position = 'left'
177
+ @elm.css
178
+ top: boundingBox.y + Math.round((boundingBox.h-bubbleSize[1])/2) + "px"
179
+ left: (boundingBox.x - bubbleSize[0]) + "px"
180
+ 'margin-left': '-14px'
181
+ 'margin-top': '0px'
182
+
183
+ #reset all pointer-* class and set the good one.
184
+ @elm.find('.ha-bubble-pointer').attr(class: 'ha-bubble-pointer').addClass("pointer-#{finalPosition}")
185
+
186
+ if @editMode
187
+ if @target
188
+ @control.find('.ha-bubble-position').val(@position || 'guess')
189
+ else
190
+ @control.find('.ha-bubble-position').val('free')
191
+
192
+ @control.find('.ha-bubble-selector').val(@target)
193
+
194
+ @elm.find('.ha-bubble-pointer').css display: 'block'
195
+
196
+ @targetArea.css
197
+ width: $(@target).outerWidth()
198
+ height: $(@target).outerHeight()
199
+ top: boundingBox.y
200
+ left: boundingBox.x
201
+ display: 'block'
202
+ else
203
+ @elm.css top: "#{@y}px", left: "#{@x}px"
204
+ @elm.find('.ha-bubble-pointer').css display: 'none'
205
+ @targetArea.css display: 'none'
206
+
207
+ return [@elm, @targetArea]
208
+
209
+ saveChanges: ->
210
+ HelpAnywhere.saveElement this
211
+
212
+
213
+ getData: ->
214
+ x: @x
215
+ y: @y
216
+ position: @position
217
+ width: @width
218
+ height: @height
219
+ target: @target
220
+ content: @content
221
+
222
+ ###
223
+ DRAG HANDLING
224
+ ###
225
+ handleMouseMove: (evt) ->
226
+ return unless @drag.hasStarted
227
+
228
+ drag_anchor = @drag.currentAnchor
229
+
230
+ decal = [
231
+ @drag.startOffset[X]-evt.pageX
232
+ @drag.startOffset[Y]-evt.pageY
233
+ ]
234
+
235
+ coords = [].concat @drag.startCoords
236
+
237
+ if ~drag_anchor.indexOf('x')
238
+ coords[LEFT] -= decal[X]
239
+ coords[TOP] -= decal[Y]
240
+ else
241
+ if ~drag_anchor.indexOf('n')
242
+ coords[TOP] -= decal[Y]
243
+ coords[HEIGHT] += decal[Y]
244
+ else if ~drag_anchor.indexOf('s')
245
+ coords[HEIGHT] -= decal[Y]
246
+
247
+ if ~drag_anchor.indexOf('e')
248
+ coords[WIDTH] -= decal[X]
249
+ else if ~drag_anchor.indexOf('w')
250
+ coords[LEFT] -= decal[X]
251
+ coords[WIDTH] += decal[X]
252
+
253
+ @x = coords[LEFT]
254
+ @y = coords[TOP]
255
+ @width = coords[WIDTH]
256
+ @height = coords[HEIGHT]
257
+
258
+ @refresh()
259
+
260
+ handleMouseUp: ->
261
+ if @drag.hasStarted
262
+ @drag.hasStarted = no
263
+ @saveChanges()
264
+
265
+ handleMouseDown: (evt) ->
266
+ @drag.hasStarted = yes
267
+
268
+ @drag.currentAnchor = $(evt.target).data('_position')
269
+
270
+ @drag.startOffset = [
271
+ evt.pageX
272
+ evt.pageY
273
+ ]
274
+
275
+ @drag.startCoords = [
276
+ @elm.position().left
277
+ @elm.position().top
278
+ @elm.width()
279
+ @elm.height()
280
+ ]
281
+
282
+ handleAnchorBehavior: (anchor) ->
283
+ drag_anchor = anchor.data('_position')
284
+
285
+ @handleMouseMoveInstance = @handleMouseMove.bind(this)
286
+ @handleMouseUpInstance = @handleMouseUp.bind(this)
287
+
288
+ $('body')
289
+ .on('mousemove', @handleMouseMoveInstance)
290
+ .on('mouseup', @handleMouseUpInstance)
291
+
292
+ anchor.on 'mousedown', @handleMouseDown.bind(this)
293
+
294
+ ###
295
+ END OF DRAG HANDLING
296
+ ###
297
+
298
+ remove: ->
299
+ $('body')
300
+ .off('mousemove', @handleMouseMoveInstance)
301
+ .off('mouseup', @handleMouseUpInstance)
302
+
303
+ @elm.remove()
304
+
305
+ buildEditMode: ->
306
+ self = this
307
+ @anchors = {}
308
+
309
+ for position in ['ne', 'n', 'nw', 'w', 'sw', 's', 'se', 'e', 'x']
310
+ anchor = @anchors[position] = $("<div class='ha-bubble-anchor #{position}'>")
311
+ anchor.data('_position', position)
312
+
313
+ @elm.append anchor
314
+ @handleAnchorBehavior anchor
315
+
316
+ @control = $(EDIT_TEMPLATE()).appendTo(@elm)
317
+
318
+ @control.find('.ha-bubble-position').on 'change', ->
319
+ self.position = $(this).val()
320
+ self.saveChanges()
321
+ self.refresh()
322
+
323
+ @control.find('.ha-bubble-selector').on 'change', ->
324
+ self.target = $(this).val()
325
+ self.saveChanges()
326
+ self.refresh()
327
+
328
+ @control.find('.ha-bubble-remove').on 'click', ->
329
+ HelpAnywhere.deleteItem(self)
330
+
331
+ content = @elm.find('.ha-bubble-content')
332
+
333
+ content
334
+ .attr('contenteditable', true)
335
+ .on 'focus', =>
336
+ # This test is a special case when user change focus from browser
337
+ if !content.data('_has_focus')
338
+ content.data('_has_focus', true)
339
+ #Get the html:
340
+ escapedHtml = content.text(content.html()).html()
341
+ #Replace the <br> by "\n"
342
+ # And &amp; by & (for &nbsp; for example)
343
+ escapedHtml = escapedHtml
344
+ .replace(/&lt;br&gt;/g, "<br>")
345
+ .replace(/&amp;/g, "&")
346
+ # Render the html, but keep the <br> correctly rendered
347
+ content.html(escapedHtml)
348
+ .on 'blur', =>
349
+ # This test is a special case when user change focus from browser
350
+ if content.data('_has_focus')
351
+ content.data('_has_focus', false)
352
+ content.html(
353
+ content.html()
354
+ .replace(/(<div>)(((?!<\/div>).)*)(<\/div>)/g, "<br>$2")
355
+ # We ignore the <br> before a markup
356
+ .replace(/<br>&lt;/g, '&nbsp;&lt;')
357
+ .replace(/&lt;/g, "<")
358
+ .replace(/&gt;/g, ">")
359
+ .replace(/&nbsp;/g, " ")
360
+ .replace("/&amp;/g", "&")
361
+ )
362
+ @content = content.html()
363
+
364
+ @saveChanges()
365
+
366
+ #Register generator to generators list
367
+ HelpAnywhere.Component.register('Bubble', Bubble)
@@ -0,0 +1,10 @@
1
+ do($=jQuery) ->
2
+ class window.HelpAnywhere.Component
3
+ @register: (class_name, class_obj) -> HelpAnywhere.components[class_name] = class_obj
4
+
5
+ @_NEED_IMPLEMENTATION: (function_names...) ->
6
+ for function_name in function_names
7
+ @::[function_name] = ->
8
+ throw new Error("You must implement #{function_name}")
9
+
10
+ @_NEED_IMPLEMENTATION 'build', 'getData', 'blur', 'remove'