droom 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +23 -0
- data/Rakefile +27 -0
- data/app/assets/images/droom/arrows.png +0 -0
- data/app/assets/images/droom/big_icons.png +0 -0
- data/app/assets/images/droom/blueblob.png +0 -0
- data/app/assets/images/droom/close.png +0 -0
- data/app/assets/images/droom/cross.png +0 -0
- data/app/assets/images/droom/download.png +0 -0
- data/app/assets/images/droom/greyblob.png +0 -0
- data/app/assets/images/droom/help/goodreader.jpg +0 -0
- data/app/assets/images/droom/ical.png +0 -0
- data/app/assets/images/droom/icons.png +0 -0
- data/app/assets/images/droom/medium_icons.png +0 -0
- data/app/assets/images/droom/medium_object_icons.png +0 -0
- data/app/assets/images/droom/minimonth.png +0 -0
- data/app/assets/images/droom/minisymbols.png +0 -0
- data/app/assets/images/droom/object_icons.png +0 -0
- data/app/assets/images/droom/pinkblob.png +0 -0
- data/app/assets/images/droom/place_busy.png +0 -0
- data/app/assets/images/droom/place_quiet.png +0 -0
- data/app/assets/images/droom/plus_bullet.png +0 -0
- data/app/assets/images/droom/search.png +0 -0
- data/app/assets/images/droom/setup.png +0 -0
- data/app/assets/images/droom/small_go.png +0 -0
- data/app/assets/images/droom/smallbullet.png +0 -0
- data/app/assets/images/droom/smallspinner.gif +0 -0
- data/app/assets/images/droom/spinner.gif +0 -0
- data/app/assets/images/droom/spr_toolbar_icons_r2.png +0 -0
- data/app/assets/images/droom/tablesort.png +0 -0
- data/app/assets/images/droom/tick.png +0 -0
- data/app/assets/images/droom/tinysymbols.png +0 -0
- data/app/assets/images/droom/twister.png +0 -0
- data/app/assets/images/droom/vcard.png +0 -0
- data/app/assets/images/droom/venue_bullet.png +0 -0
- data/app/assets/javascripts/droom.js.coffee +88 -0
- data/app/assets/javascripts/droom/calendar.js.coffee +121 -0
- data/app/assets/javascripts/droom/drag_sort.js.coffee +22 -0
- data/app/assets/javascripts/droom/forms.js.coffee +746 -0
- data/app/assets/javascripts/droom/lib/extensions.js.coffee +60 -0
- data/app/assets/javascripts/droom/lib/jquery.animate-colors.js +109 -0
- data/app/assets/javascripts/droom/lib/jquery.cookie.js +71 -0
- data/app/assets/javascripts/droom/lib/jquery.sortable.js +97 -0
- data/app/assets/javascripts/droom/lib/kalendae.js +1692 -0
- data/app/assets/javascripts/droom/lib/modernizr.js +4 -0
- data/app/assets/javascripts/droom/lib/parser_rules/advanced.js +553 -0
- data/app/assets/javascripts/droom/lib/parser_rules/simple.js +32 -0
- data/app/assets/javascripts/droom/lib/wysihtml5.js +9550 -0
- data/app/assets/javascripts/droom/map.js.coffee +123 -0
- data/app/assets/javascripts/droom/sort.js.coffee +126 -0
- data/app/assets/javascripts/droom/suggester.js.coffee +230 -0
- data/app/assets/stylesheets/droom.css.sass +1151 -0
- data/app/assets/stylesheets/lib/_kalendae.css.sass +142 -0
- data/app/assets/stylesheets/lib/_toolbar.css.sass +192 -0
- data/app/controllers/droom/dashboard_controller.rb +27 -0
- data/app/controllers/droom/document_attachments_controller.rb +19 -0
- data/app/controllers/droom/documents_controller.rb +116 -0
- data/app/controllers/droom/engine_controller.rb +43 -0
- data/app/controllers/droom/events_controller.rb +120 -0
- data/app/controllers/droom/group_invitations_controller.rb +47 -0
- data/app/controllers/droom/groups_controller.rb +67 -0
- data/app/controllers/droom/invitations_controller.rb +43 -0
- data/app/controllers/droom/memberships_controller.rb +47 -0
- data/app/controllers/droom/pages_controller.rb +61 -0
- data/app/controllers/droom/people_controller.rb +92 -0
- data/app/controllers/droom/suggestions_controller.rb +58 -0
- data/app/controllers/droom/venues_controller.rb +39 -0
- data/app/helpers/droom/droom_helper.rb +74 -0
- data/app/models/droom/agenda_category.rb +8 -0
- data/app/models/droom/category.rb +27 -0
- data/app/models/droom/document.rb +110 -0
- data/app/models/droom/document_attachment.rb +71 -0
- data/app/models/droom/document_link.rb +31 -0
- data/app/models/droom/event.rb +409 -0
- data/app/models/droom/event_set.rb +6 -0
- data/app/models/droom/group.rb +66 -0
- data/app/models/droom/group_invitation.rb +23 -0
- data/app/models/droom/invitation.rb +30 -0
- data/app/models/droom/membership.rb +27 -0
- data/app/models/droom/page.rb +26 -0
- data/app/models/droom/person.rb +302 -0
- data/app/models/droom/personal_document.rb +98 -0
- data/app/models/droom/recurrence_rule.rb +82 -0
- data/app/models/droom/venue.rb +125 -0
- data/app/views/droom/dashboard/_marginalia.html.haml +3 -0
- data/app/views/droom/dashboard/_my_future_events.html.haml +9 -0
- data/app/views/droom/dashboard/_my_group_documents.html.haml +6 -0
- data/app/views/droom/dashboard/_my_past_events.haml +6 -0
- data/app/views/droom/dashboard/index.html.haml +5 -0
- data/app/views/droom/documents/_created.html.haml +2 -0
- data/app/views/droom/documents/_document.html.haml +14 -0
- data/app/views/droom/documents/_document_line.html.haml +2 -0
- data/app/views/droom/documents/_documents_list.html.haml +31 -0
- data/app/views/droom/documents/_documents_table.html.haml +13 -0
- data/app/views/droom/documents/_event_document_form.html.haml +15 -0
- data/app/views/droom/documents/_form.html.haml +22 -0
- data/app/views/droom/documents/_listing.html.haml +8 -0
- data/app/views/droom/documents/_suggested.html.haml +9 -0
- data/app/views/droom/documents/_table_document.html.haml +31 -0
- data/app/views/droom/documents/edit.html.haml +1 -0
- data/app/views/droom/documents/index.html.haml +29 -0
- data/app/views/droom/documents/new.html.haml +1 -0
- data/app/views/droom/errors/bang.html.haml +12 -0
- data/app/views/droom/errors/not_allowed.html.haml +12 -0
- data/app/views/droom/errors/not_found.html.haml +12 -0
- data/app/views/droom/events/_attachment.html.haml +1 -0
- data/app/views/droom/events/_attachment_list.html.haml +4 -0
- data/app/views/droom/events/_calendar.html.haml +54 -0
- data/app/views/droom/events/_created.html.haml +2 -0
- data/app/views/droom/events/_event.html.haml +77 -0
- data/app/views/droom/events/_event_line.html.haml +13 -0
- data/app/views/droom/events/_events.html.haml +2 -0
- data/app/views/droom/events/_form.html.haml +35 -0
- data/app/views/droom/events/_invitations.html.haml +20 -0
- data/app/views/droom/events/_other_page_parts.html.haml +0 -0
- data/app/views/droom/events/_popup_event.html.haml +6 -0
- data/app/views/droom/events/_suggested.html.haml +12 -0
- data/app/views/droom/events/_views.html.haml +7 -0
- data/app/views/droom/events/edit.html.haml +1 -0
- data/app/views/droom/events/index.html.haml +12 -0
- data/app/views/droom/events/index.rss.builder +20 -0
- data/app/views/droom/events/new.html.haml +1 -0
- data/app/views/droom/events/show.html.haml +4 -0
- data/app/views/droom/group_invitations/_attending_groups.html.haml +8 -0
- data/app/views/droom/group_invitations/_created.html.haml +3 -0
- data/app/views/droom/group_invitations/_form.html.haml +4 -0
- data/app/views/droom/group_invitations/new.html.haml +1 -0
- data/app/views/droom/groups/_created.html.haml +3 -0
- data/app/views/droom/groups/_form.html.haml +12 -0
- data/app/views/droom/groups/_group.html.haml +11 -0
- data/app/views/droom/groups/_groups.html.haml +3 -0
- data/app/views/droom/groups/edit.html.haml +1 -0
- data/app/views/droom/groups/index.html.haml +7 -0
- data/app/views/droom/groups/show.html.haml +13 -0
- data/app/views/droom/invitations/_attending_people.html.haml +8 -0
- data/app/views/droom/invitations/_created.html.haml +3 -0
- data/app/views/droom/invitations/_form.html.haml +5 -0
- data/app/views/droom/invitations/new.html.haml +1 -0
- data/app/views/droom/memberships/_button.html.haml +11 -0
- data/app/views/droom/memberships/_created.html.haml +4 -0
- data/app/views/droom/memberships/_form.html.haml +7 -0
- data/app/views/droom/memberships/_member.html.haml +33 -0
- data/app/views/droom/memberships/_memberships.html.haml +4 -0
- data/app/views/droom/pages/_contents.html.haml +10 -0
- data/app/views/droom/pages/_form.html.haml +36 -0
- data/app/views/droom/pages/_full_page.html.haml +17 -0
- data/app/views/droom/pages/_page.html.haml +5 -0
- data/app/views/droom/pages/_pages.html.haml +2 -0
- data/app/views/droom/pages/admin.html.haml +24 -0
- data/app/views/droom/pages/edit.html.haml +1 -0
- data/app/views/droom/pages/index.html.haml +10 -0
- data/app/views/droom/pages/new.html.haml +4 -0
- data/app/views/droom/pages/show.html.haml +5 -0
- data/app/views/droom/people/_created.html.haml +6 -0
- data/app/views/droom/people/_form.html.haml +40 -0
- data/app/views/droom/people/_people.html.haml +29 -0
- data/app/views/droom/people/_person.html.haml +34 -0
- data/app/views/droom/people/_suggested.html.haml +9 -0
- data/app/views/droom/people/edit.html.haml +1 -0
- data/app/views/droom/people/index.html.haml +11 -0
- data/app/views/droom/people/new.html.haml +1 -0
- data/app/views/droom/people/show.html.haml +1 -0
- data/app/views/droom/shared/_calendar_and_search.html.haml +2 -0
- data/app/views/droom/shared/_calendar_holder.haml +3 -0
- data/app/views/droom/shared/_controls.html.haml +8 -0
- data/app/views/droom/shared/_navigation.html.haml +8 -0
- data/app/views/droom/shared/_search_form.html.haml +4 -0
- data/app/views/droom/shared/_suggestions.html.haml +11 -0
- data/app/views/droom/shared/_toolbar.html.haml +17 -0
- data/app/views/droom/venues/_suggested.html.haml +6 -0
- data/app/views/droom/venues/index.html.haml +6 -0
- data/app/views/droom/venues/show.html.haml +21 -0
- data/app/views/layouts/droom/application.html.haml +36 -0
- data/config/initializers/dav.rb +2 -0
- data/config/initializers/paperclip.rb +26 -0
- data/config/initializers/snail.rb +2 -0
- data/config/locales/en.yml +191 -0
- data/config/routes.rb +51 -0
- data/db/migrate/20120910075016_create_droom_data.rb +134 -0
- data/db/migrate/20120917095804_agenda_sections.rb +13 -0
- data/db/migrate/20120918121352_add_postal_address_to_people.rb +10 -0
- data/db/migrate/20121009075049_give_groups_descriptions.rb +5 -0
- data/db/migrate/20121009105244_more_names.rb +5 -0
- data/db/migrate/20121009145944_event_agenda_sections.rb +13 -0
- data/db/migrate/20121011091230_create_group_invitations.rb +10 -0
- data/db/migrate/20121012144720_give_people_positions.rb +5 -0
- data/db/migrate/20121012154558_help_pages.rb +13 -0
- data/db/migrate/20121012163201_category_slugs.rb +5 -0
- data/db/migrate/20121101160102_document_links.rb +14 -0
- data/db/migrate/20121101181247_people_visibility.rb +7 -0
- data/db/migrate/20121102094738_shy_people.rb +6 -0
- data/db/migrate/20121102095856_visibility_defaults.rb +8 -0
- data/lib/droom.rb +73 -0
- data/lib/droom/dav_resource.rb +36 -0
- data/lib/droom/engine.rb +8 -0
- data/lib/droom/helpers.rb +25 -0
- data/lib/droom/monkeys.rb +15 -0
- data/lib/droom/renderers.rb +13 -0
- data/lib/droom/validators.rb +5 -0
- data/lib/droom/version.rb +3 -0
- data/lib/generators/droom/dashboard/dashboard_generator.rb +16 -0
- data/lib/generators/droom/install/USAGE +9 -0
- data/lib/generators/droom/install/install_generator.rb +15 -0
- data/lib/generators/droom/install/templates/droom_initializer.rb +6 -0
- data/lib/generators/droom/views/views_generator.rb +9 -0
- data/lib/tasks/droom_tasks.rake +4 -0
- metadata +635 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
jQuery ($) ->
|
2
|
+
$.infowindows = []
|
3
|
+
|
4
|
+
class Map
|
5
|
+
constructor: (element) ->
|
6
|
+
@container = $(element)
|
7
|
+
lat = @container.attr('data-lat') || 22.280147
|
8
|
+
lng = @container.attr('data-lng') || 114.158302
|
9
|
+
zoom = @container.attr('data-zoom') || 12
|
10
|
+
@map = new google.maps.Map element,
|
11
|
+
center: new google.maps.LatLng lat, lng
|
12
|
+
zoom: parseInt(zoom)
|
13
|
+
mapTypeId: google.maps.MapTypeId.ROADMAP
|
14
|
+
mapTypeControlOptions:
|
15
|
+
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
16
|
+
|
17
|
+
getMap: () =>
|
18
|
+
@map
|
19
|
+
|
20
|
+
class Point
|
21
|
+
constructor: (point, map) ->
|
22
|
+
@_lat = point.lat
|
23
|
+
@_lng = point.lng
|
24
|
+
@_map = map
|
25
|
+
@_events = point.events
|
26
|
+
@_postcode = point.postcode
|
27
|
+
@id = point.id
|
28
|
+
@_name = point.name
|
29
|
+
@_address = point.address
|
30
|
+
@_editable = $.ed
|
31
|
+
@_marker = new google.maps.Marker
|
32
|
+
position: new google.maps.LatLng @_lat, @_lng
|
33
|
+
map: @_map
|
34
|
+
icon: $.set_icon(@_events.length)
|
35
|
+
draggable: $.map_editable
|
36
|
+
@infowindow()
|
37
|
+
google.maps.event.addListener @_marker, "click", @click
|
38
|
+
google.maps.event.addListener @_marker, "dragstart", @dragstart
|
39
|
+
google.maps.event.addListener @_marker, "dragend", @dragend
|
40
|
+
@_infowindow.open(@_map) if parseInt($.urlParam("id"), 10) == @id
|
41
|
+
|
42
|
+
|
43
|
+
infowindow: () =>
|
44
|
+
content = $("<div class='window'><h2>#{@_name}</h2>#{@_address.replace(/\n/g, ",")}<div class='window_venue_events'></div></div>")
|
45
|
+
$.each @_events, (i, evt) =>
|
46
|
+
content.find('.window_venue_events').append "<li><a href='/events/#{evt.id}'>#{evt.name}</a> <span class='pale'>#{evt.datestring}</span></li>"
|
47
|
+
@_infowindow = new google.maps.InfoWindow
|
48
|
+
position: new google.maps.LatLng @_lat, @_lng
|
49
|
+
content: content.prop('outerHTML')
|
50
|
+
marker: @_marker
|
51
|
+
$.infowindows.push @_infowindow
|
52
|
+
|
53
|
+
click: (e) =>
|
54
|
+
$.each $.infowindows, (i, iw) =>
|
55
|
+
iw.close()
|
56
|
+
@_infowindow.open(@_map)
|
57
|
+
|
58
|
+
dragstart: (e) =>
|
59
|
+
@_infowindow?.close()
|
60
|
+
|
61
|
+
dragend: (e) =>
|
62
|
+
position = @_marker.getPosition()
|
63
|
+
$.ajax
|
64
|
+
type: 'POST'
|
65
|
+
url: "/venues/#{@id}",
|
66
|
+
data:
|
67
|
+
_method: "PUT"
|
68
|
+
venue:
|
69
|
+
lat: position.lat()
|
70
|
+
lng: position.lng()
|
71
|
+
dataType: 'json'
|
72
|
+
success: () ->
|
73
|
+
console.log "venue updated"
|
74
|
+
|
75
|
+
|
76
|
+
$.fn.init_map = () ->
|
77
|
+
@each ->
|
78
|
+
$.gmap = new Map(@).getMap()
|
79
|
+
$.map_editable = $(@).hasClass('editable')
|
80
|
+
if $(@).hasClass('small')
|
81
|
+
$(@).show_venue()
|
82
|
+
else
|
83
|
+
$(@).show_venues()
|
84
|
+
|
85
|
+
$.fn.show_venues = () ->
|
86
|
+
@each ->
|
87
|
+
map = $.gmap
|
88
|
+
if src = $(@).attr("data-url")
|
89
|
+
$.getJSON src, (response) =>
|
90
|
+
bounds = new google.maps.LatLngBounds()
|
91
|
+
$.each response, (i, venue) =>
|
92
|
+
latlng = new google.maps.LatLng venue.lat, venue.lng
|
93
|
+
bounds.extend latlng
|
94
|
+
venue = new Point venue, map
|
95
|
+
|
96
|
+
temporary_zoom_limiter = google.maps.event.addListener map, 'bounds_changed', ->
|
97
|
+
map.setZoom(17) if map.getZoom() > 17
|
98
|
+
google.maps.event.removeListener(temporary_zoom_limiter);
|
99
|
+
|
100
|
+
map.fitBounds bounds
|
101
|
+
|
102
|
+
$.fn.show_venue = () ->
|
103
|
+
@each ->
|
104
|
+
map = $.gmap
|
105
|
+
if src = $(@).attr("data-url")
|
106
|
+
$.getJSON src, (venue) =>
|
107
|
+
latlng = new google.maps.LatLng venue.lat, venue.lng
|
108
|
+
marker = new google.maps.Marker
|
109
|
+
position: latlng
|
110
|
+
map: map
|
111
|
+
icon: $.set_icon(venue.events.length)
|
112
|
+
map.setCenter(latlng)
|
113
|
+
|
114
|
+
|
115
|
+
$.urlParam = (name) ->
|
116
|
+
results = new RegExp("[\\?&]" + name + "=([^&#]*)").exec(window.location.href)
|
117
|
+
return 0 unless results
|
118
|
+
results[1] or 0
|
119
|
+
|
120
|
+
$.set_icon = (size) ->
|
121
|
+
name = if size > 0 then "place_busy" else "place_quiet"
|
122
|
+
new google.maps.MarkerImage "/assets/droom/#{name}.png", new google.maps.Size(36, 36), new google.maps.Point(0, 0), new google.maps.Point(18, 18)
|
123
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
jQuery ($) ->
|
2
|
+
$.headers = []
|
3
|
+
|
4
|
+
$.params = (name) ->
|
5
|
+
decodeURIComponent $.urlParam(name)
|
6
|
+
|
7
|
+
class TableSort
|
8
|
+
constructor: (element, options) ->
|
9
|
+
@order = options.order
|
10
|
+
@sort = options.sort
|
11
|
+
@table = $(element)
|
12
|
+
@body = @table.find('tbody')
|
13
|
+
@_original_content = @body.children()
|
14
|
+
@sort = $.params("sort")
|
15
|
+
@order = $.params("order")
|
16
|
+
@headers = @table.find('th a.sorter')
|
17
|
+
$.each @headers, (i, header) =>
|
18
|
+
header = new SortLink header, @
|
19
|
+
$.headers.push header
|
20
|
+
@activate()
|
21
|
+
@table.bind "refresh", @update
|
22
|
+
if Modernizr.history
|
23
|
+
$(window).bind 'popstate', @restoreState
|
24
|
+
|
25
|
+
resort: (sort, order) =>
|
26
|
+
sort ?= "name"
|
27
|
+
order ?= "ASC"
|
28
|
+
@sort = sort
|
29
|
+
@order = order
|
30
|
+
@get("/documents.js?sort=#{sort}&order=#{order}")
|
31
|
+
|
32
|
+
update: () =>
|
33
|
+
@body.fadeTo('fast', 0.2)
|
34
|
+
$.ajax
|
35
|
+
url: "/documents.js?sort=#{@sort}&order=#{@order}"
|
36
|
+
dataType: "html"
|
37
|
+
success: @display
|
38
|
+
|
39
|
+
get: (url) =>
|
40
|
+
@body.fadeTo('fast', 0.2)
|
41
|
+
$.ajax
|
42
|
+
url: (url)
|
43
|
+
dataType: "html"
|
44
|
+
success: @refresh
|
45
|
+
|
46
|
+
refresh: (data, textStatus, jqXHR) =>
|
47
|
+
@clear()
|
48
|
+
@saveState(data) if Modernizr.history
|
49
|
+
@display(data)
|
50
|
+
|
51
|
+
display: (data) =>
|
52
|
+
replacement = $(data)
|
53
|
+
@body.after(replacement)
|
54
|
+
@body.remove()
|
55
|
+
@body = replacement
|
56
|
+
@body.fadeTo('fast', 1)
|
57
|
+
@body.activate()
|
58
|
+
@activate()
|
59
|
+
|
60
|
+
activate: () =>
|
61
|
+
@body.find('a.popup').popup_remote_content()
|
62
|
+
@body.find('.pagination').find('a').retable(@)
|
63
|
+
@body.refresher()
|
64
|
+
|
65
|
+
clear: () =>
|
66
|
+
@body.children().remove()
|
67
|
+
|
68
|
+
saveState: (results) =>
|
69
|
+
url = window.location.pathname + '?sort=' + encodeURIComponent(@sort) + '&order=' + encodeURIComponent(@order)
|
70
|
+
state =
|
71
|
+
html: results
|
72
|
+
sort: @sort
|
73
|
+
order: @order
|
74
|
+
history.pushState state, "New page title", url
|
75
|
+
|
76
|
+
restoreState: (e) =>
|
77
|
+
event = e.originalEvent
|
78
|
+
if event.state? && event.state.html?
|
79
|
+
@clear()
|
80
|
+
@order = event.state.order
|
81
|
+
@sort = event.state.sort
|
82
|
+
@display(event.state.html)
|
83
|
+
$.each $.headers, (i, header) =>
|
84
|
+
header.check()
|
85
|
+
|
86
|
+
|
87
|
+
class SortLink
|
88
|
+
constructor: (element, table) ->
|
89
|
+
@table = table
|
90
|
+
@_link = $(element)
|
91
|
+
@_sort = @_link.attr("data-sort")
|
92
|
+
@_link.bind "click", @click
|
93
|
+
@check()
|
94
|
+
@_order = @_link.attr("data-order")
|
95
|
+
|
96
|
+
check: () =>
|
97
|
+
if @table.sort == @_sort
|
98
|
+
@table.headers.removeAttr('data-order')
|
99
|
+
@_link.attr("data-order", @table.order)
|
100
|
+
|
101
|
+
click: (e) =>
|
102
|
+
e.preventDefault()
|
103
|
+
@table.headers.removeAttr('data-order')
|
104
|
+
if @_order == "ASC"
|
105
|
+
@_link.attr('data-order', 'DESC')
|
106
|
+
@_order = "DESC"
|
107
|
+
else
|
108
|
+
@_link.attr('data-order', 'ASC')
|
109
|
+
@_order = "ASC"
|
110
|
+
@table.resort(@_sort, @_order)
|
111
|
+
|
112
|
+
|
113
|
+
$.fn.table_sort = (options) ->
|
114
|
+
@each ->
|
115
|
+
new TableSort @, options
|
116
|
+
@
|
117
|
+
|
118
|
+
$.fn.retable = (table) ->
|
119
|
+
@click (e) ->
|
120
|
+
e.preventDefault() if e
|
121
|
+
url = $(@).attr('href')
|
122
|
+
table.get(url)
|
123
|
+
@
|
124
|
+
|
125
|
+
|
126
|
+
|
@@ -0,0 +1,230 @@
|
|
1
|
+
jQuery ($) ->
|
2
|
+
|
3
|
+
# The Suggester is a polymorphic suggestion engine able to support any number of object types
|
4
|
+
# without really knowing what they are.
|
5
|
+
#
|
6
|
+
# * calling
|
7
|
+
# * data structure
|
8
|
+
# * object types
|
9
|
+
#
|
10
|
+
$.fn.suggestible = (options) ->
|
11
|
+
options = $.extend(
|
12
|
+
submit_form: true
|
13
|
+
threshold: 3
|
14
|
+
, options)
|
15
|
+
@each ->
|
16
|
+
new Suggester(@, options)
|
17
|
+
@
|
18
|
+
|
19
|
+
$.fn.venue_picker = (options) ->
|
20
|
+
options = $.extend(
|
21
|
+
submit_form: true
|
22
|
+
threshold: 1
|
23
|
+
type: 'venue'
|
24
|
+
, options)
|
25
|
+
@each ->
|
26
|
+
new Suggester(@, options)
|
27
|
+
@
|
28
|
+
|
29
|
+
$.fn.person_picker = (options) ->
|
30
|
+
options = $.extend(
|
31
|
+
submit_form: true
|
32
|
+
threshold: 1
|
33
|
+
type: 'person'
|
34
|
+
, options)
|
35
|
+
@each ->
|
36
|
+
target = $(@).siblings('.person_picker_target')
|
37
|
+
$(@).bind "keyup", () =>
|
38
|
+
target.val null
|
39
|
+
suggester = new Suggester(@, options)
|
40
|
+
suggester.options.afterSelect = () ->
|
41
|
+
id = JSON.parse(suggester.request.responseText)[0].id
|
42
|
+
target.val id
|
43
|
+
suggester.form.submit()
|
44
|
+
@
|
45
|
+
|
46
|
+
$.fn.group_picker = (options) ->
|
47
|
+
options = $.extend(
|
48
|
+
submit_form: true
|
49
|
+
threshold: 1
|
50
|
+
type: 'group'
|
51
|
+
, options)
|
52
|
+
@each ->
|
53
|
+
target = $(@).siblings('.group_picker_target')
|
54
|
+
$(@).bind "keyup", () =>
|
55
|
+
target.val null
|
56
|
+
suggester = new Suggester(@, options)
|
57
|
+
suggester.options.afterSelect = () ->
|
58
|
+
id = JSON.parse(suggester.request.responseText)[0].id
|
59
|
+
target.val id
|
60
|
+
suggester.form.submit()
|
61
|
+
@
|
62
|
+
|
63
|
+
|
64
|
+
class Suggester
|
65
|
+
constructor: (element, options) ->
|
66
|
+
@prompt = $(element)
|
67
|
+
@type = @prompt.attr('data-type')
|
68
|
+
@form = @prompt.parents("form")
|
69
|
+
if options.type
|
70
|
+
@url = "/suggestions/#{options.type}"
|
71
|
+
else
|
72
|
+
@url = "/suggestions"
|
73
|
+
@options = $.extend(
|
74
|
+
url: @url
|
75
|
+
fill_field: true
|
76
|
+
empty_field: false
|
77
|
+
submit_form: false
|
78
|
+
threshold: 2
|
79
|
+
afterSuggest: null
|
80
|
+
afterSelect: null
|
81
|
+
, options)
|
82
|
+
@container = $("<ul class=\"suggestions\"></ul>").insertAfter(@prompt)
|
83
|
+
@button = @form.find("a.search")
|
84
|
+
@previously = null
|
85
|
+
@request = null
|
86
|
+
@visible = false
|
87
|
+
@suggestions = []
|
88
|
+
@suggestion = null
|
89
|
+
@cache = {}
|
90
|
+
@blanks = []
|
91
|
+
|
92
|
+
@prompt.keyup @key
|
93
|
+
@form.submit @hide
|
94
|
+
@
|
95
|
+
|
96
|
+
place: () =>
|
97
|
+
@container.css
|
98
|
+
top: @prompt.position().top + @prompt.outerHeight() - 2
|
99
|
+
left: @prompt.position().left
|
100
|
+
width: @prompt.outerWidth() - 2
|
101
|
+
|
102
|
+
reset: () =>
|
103
|
+
@container.empty()
|
104
|
+
@suggestions = []
|
105
|
+
@suggestion = null
|
106
|
+
|
107
|
+
pend: () =>
|
108
|
+
@place()
|
109
|
+
@reset()
|
110
|
+
@button.addClass "waiting"
|
111
|
+
|
112
|
+
get: (e) =>
|
113
|
+
@pend()
|
114
|
+
query = @prompt.val()
|
115
|
+
if query.length >= @options.threshold and query isnt @previously
|
116
|
+
if @cache[query]
|
117
|
+
@suggest @cache[query]
|
118
|
+
else if @previously_blank(query)
|
119
|
+
@suggest []
|
120
|
+
else
|
121
|
+
@request.abort() if @request
|
122
|
+
@request = $.getJSON(@options.url, "term=" + encodeURIComponent(query), (suggestions) =>
|
123
|
+
@cache[query] = suggestions
|
124
|
+
@blanks.push query if suggestions.length is 0
|
125
|
+
@suggest suggestions
|
126
|
+
)
|
127
|
+
else
|
128
|
+
@hide()
|
129
|
+
|
130
|
+
suggest: (suggestions) =>
|
131
|
+
@button.removeClass "waiting"
|
132
|
+
@show()
|
133
|
+
if suggestions.length > 0
|
134
|
+
$.each suggestions, (i, suggestion) =>
|
135
|
+
link = $("<a href=\"#\">#{suggestion.text}</a>")
|
136
|
+
link.hover () =>
|
137
|
+
@hover(link)
|
138
|
+
link.click (e) =>
|
139
|
+
@select(e, link)
|
140
|
+
$("<li></li>").addClass(suggestion.type).append(link).appendTo @container
|
141
|
+
|
142
|
+
@suggestions = @container.find("a")
|
143
|
+
else
|
144
|
+
@hide()
|
145
|
+
@options.afterSuggest.call @, suggestions if @options.afterSuggest
|
146
|
+
|
147
|
+
select: (e, selection) =>
|
148
|
+
e.preventDefault() if e
|
149
|
+
selection ?= $(@suggestions.get(@suggestion))
|
150
|
+
value = selection.text()
|
151
|
+
if @options.fill_field
|
152
|
+
@prompt.val value
|
153
|
+
else @prompt.val "" if @options.empty_field
|
154
|
+
@form.submit() if @options.submit_form
|
155
|
+
@options.afterSelect.call @, value if @options.afterSelect
|
156
|
+
@hide()
|
157
|
+
|
158
|
+
show: () =>
|
159
|
+
unless @visible
|
160
|
+
@container.fadeIn "slow"
|
161
|
+
@visible = true
|
162
|
+
|
163
|
+
hide: () =>
|
164
|
+
if @visible
|
165
|
+
@container.fadeOut "fast"
|
166
|
+
@visible = false
|
167
|
+
|
168
|
+
key: (e) =>
|
169
|
+
key_code = e.which
|
170
|
+
if @movement(key_code)
|
171
|
+
@show() if @suggestions.length > 0
|
172
|
+
if @visible
|
173
|
+
@movement(key_code).call @, e
|
174
|
+
e.preventDefault()
|
175
|
+
e.stopPropagation()
|
176
|
+
else
|
177
|
+
@get e
|
178
|
+
|
179
|
+
movement: (key_code) =>
|
180
|
+
switch key_code
|
181
|
+
when 27 # escape
|
182
|
+
@hide
|
183
|
+
when 33 # page up
|
184
|
+
@first
|
185
|
+
when 38 # up
|
186
|
+
@previous
|
187
|
+
when 40 # down
|
188
|
+
@next
|
189
|
+
when 33 # page down
|
190
|
+
@last
|
191
|
+
when 9 # tab
|
192
|
+
@next
|
193
|
+
when 13 # enter
|
194
|
+
@select
|
195
|
+
|
196
|
+
next: (e) =>
|
197
|
+
if @suggestion is null or @suggestion >= @suggestions.length - 1
|
198
|
+
@first()
|
199
|
+
else
|
200
|
+
@highlight @suggestion + 1
|
201
|
+
|
202
|
+
previous: (e) =>
|
203
|
+
if @suggestion <= 0
|
204
|
+
@last()
|
205
|
+
else
|
206
|
+
@highlight @suggestion - 1
|
207
|
+
|
208
|
+
first: (e) =>
|
209
|
+
@highlight 0
|
210
|
+
|
211
|
+
last: (e) =>
|
212
|
+
@highlight @suggestions.length - 1
|
213
|
+
|
214
|
+
hover: (link) =>
|
215
|
+
@highlight @suggestions.index(link) # this will be the hovered link
|
216
|
+
|
217
|
+
highlight: (i) =>
|
218
|
+
@unHighlight @suggestion if @suggestion isnt null
|
219
|
+
$(@suggestions.get(i)).addClass "hover"
|
220
|
+
@suggestion = i
|
221
|
+
|
222
|
+
unHighlight: (i) =>
|
223
|
+
$(@suggestions.get(i)).removeClass "hover"
|
224
|
+
@suggestion = null
|
225
|
+
|
226
|
+
previously_blank: (query) =>
|
227
|
+
if @blanks.length > 0
|
228
|
+
blank_re = new RegExp("(" + @blanks.join("|") + ")")
|
229
|
+
return blank_re.test(query)
|
230
|
+
false
|