voluntary 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/app/assets/javascripts/voluntary/application.js +1 -0
- data/app/assets/javascripts/voluntary/optional_lib/jquery.location_picker.js +405 -0
- data/app/controllers/concerns/voluntary/v1/base_controller.rb +6 -0
- data/app/controllers/stories_controller.rb +1 -1
- data/app/controllers/tasks_controller.rb +23 -32
- data/app/controllers/users_controller.rb +12 -5
- data/app/controllers/workflow/products_controller.rb +2 -0
- data/app/models/story.rb +8 -0
- data/app/models/user.rb +1 -1
- data/app/presenters/resources/user/form_presenter.rb +1 -1
- data/app/views/layouts/application.html.erb +2 -1
- data/app/views/tasks/_form.html.erb +5 -0
- data/app/views/tasks/edit.html.erb +3 -0
- data/app/views/tasks/index.html.erb +5 -0
- data/app/views/tasks/new.html.erb +3 -0
- data/app/views/tasks/show.html.erb +5 -5
- data/app/views/users/_form.html.erb +2 -0
- data/config/locales/general/en.yml +12 -1
- data/config/locales/resources/task/en.yml +6 -0
- data/db/migrate/20150808155719_add_api_key_to_users.rb +5 -1
- data/db/migrate/20151022163015_add_timezone_to_users.rb +5 -0
- data/lib/vendors/simple_navigation/renderer/twitter_sidenav.rb +2 -4
- data/lib/voluntary.rb +2 -0
- data/lib/voluntary/version.rb +1 -1
- metadata +27 -8
- data/app/assets/javascripts/voluntary/lib/moment.js +0 -3195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e733d65d2735dc9749ec9be14d46cdb88c56ca85
|
4
|
+
data.tar.gz: b14d3f3b9dfc746dd1f906208610d47f2b0cdb1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdb14cddd47477319fb349c3fea0ae821e3b070e95db6030fb39b6f5efab15cf512a17b1e249615658beea7c34586bea1e03be65539ada401e88eadf46794dc4
|
7
|
+
data.tar.gz: 1b7d07d75916d31fd56d77f4762c60a8f6d975b517d49d29b71e361aba451cd2b8fd80ea2d217b9c7f5410c2944c0d1023d9fbfcd082358b346e1441ccbbd965
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
## unreleased ##
|
2
2
|
|
3
|
+
## 0.7.0 (October 29, 2015) ##
|
4
|
+
|
5
|
+
* [#94](https://github.com/volontariat/voluntary/issues/94) Customization for classified advertisement product.
|
6
|
+
* [#97](https://github.com/volontariat/voluntary/issues/97) User form does not show errors like country can't be blank.
|
7
|
+
* [#96](https://github.com/volontariat/voluntary/issues/96) Regard user's timezone.
|
8
|
+
* [BUGFIX] Remove obsolete scroll bar from modals.
|
9
|
+
|
3
10
|
## 0.6.0 (October 1, 2015) ##
|
4
11
|
|
5
12
|
* [#93](https://github.com/volontariat/voluntary/issues/93) Core customizations for survey product.
|
@@ -0,0 +1,405 @@
|
|
1
|
+
(function ( $ ) {
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Holds google map object and related utility entities.
|
5
|
+
* @constructor
|
6
|
+
*/
|
7
|
+
function GMapContext(domElement, options) {
|
8
|
+
var _map = new google.maps.Map(domElement, options);
|
9
|
+
var _marker = new google.maps.Marker({
|
10
|
+
position: new google.maps.LatLng(54.19335, -3.92695),
|
11
|
+
map: _map,
|
12
|
+
title: "Drag Me",
|
13
|
+
draggable: options.draggable
|
14
|
+
});
|
15
|
+
return {
|
16
|
+
map: _map,
|
17
|
+
marker: _marker,
|
18
|
+
circle: null,
|
19
|
+
location: _marker.position,
|
20
|
+
radius: options.radius,
|
21
|
+
locationName: options.locationName,
|
22
|
+
addressComponents: {
|
23
|
+
formatted_address: null,
|
24
|
+
addressLine1: null,
|
25
|
+
addressLine2: null,
|
26
|
+
streetName: null,
|
27
|
+
streetNumber: null,
|
28
|
+
city: null,
|
29
|
+
district: null,
|
30
|
+
state: null,
|
31
|
+
stateOrProvince: null
|
32
|
+
},
|
33
|
+
settings: options.settings,
|
34
|
+
domContainer: domElement,
|
35
|
+
geodecoder: new google.maps.Geocoder()
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
// Utility functions for Google Map Manipulations
|
40
|
+
var GmUtility = {
|
41
|
+
/**
|
42
|
+
* Draw a circle over the the map. Returns circle object.
|
43
|
+
* Also writes new circle object in gmapContext.
|
44
|
+
*
|
45
|
+
* @param center - LatLng of the center of the circle
|
46
|
+
* @param radius - radius in meters
|
47
|
+
* @param gmapContext - context
|
48
|
+
* @param options
|
49
|
+
*/
|
50
|
+
drawCircle: function(gmapContext, center, radius, options) {
|
51
|
+
if (gmapContext.circle != null) {
|
52
|
+
gmapContext.circle.setMap(null);
|
53
|
+
}
|
54
|
+
if (radius > 0) {
|
55
|
+
radius *= 1;
|
56
|
+
options = $.extend({
|
57
|
+
strokeColor: "#0000FF",
|
58
|
+
strokeOpacity: 0.35,
|
59
|
+
strokeWeight: 2,
|
60
|
+
fillColor: "#0000FF",
|
61
|
+
fillOpacity: 0.20
|
62
|
+
}, options);
|
63
|
+
options.map = gmapContext.map;
|
64
|
+
options.radius = radius;
|
65
|
+
options.center = center;
|
66
|
+
gmapContext.circle = new google.maps.Circle(options);
|
67
|
+
return gmapContext.circle;
|
68
|
+
}
|
69
|
+
return null;
|
70
|
+
},
|
71
|
+
/**
|
72
|
+
*
|
73
|
+
* @param gMapContext
|
74
|
+
* @param location
|
75
|
+
* @param callback
|
76
|
+
*/
|
77
|
+
setPosition: function(gMapContext, location, callback) {
|
78
|
+
gMapContext.location = location;
|
79
|
+
gMapContext.marker.setPosition(location);
|
80
|
+
gMapContext.map.panTo(location);
|
81
|
+
this.drawCircle(gMapContext, location, gMapContext.radius, {});
|
82
|
+
if (gMapContext.settings.enableReverseGeocode) {
|
83
|
+
gMapContext.geodecoder.geocode({latLng: gMapContext.location}, function(results, status){
|
84
|
+
if (status == google.maps.GeocoderStatus.OK && results.length > 0){
|
85
|
+
gMapContext.locationName = results[0].formatted_address;
|
86
|
+
gMapContext.addressComponents =
|
87
|
+
GmUtility.address_component_from_google_geocode(results[0].address_components);
|
88
|
+
}
|
89
|
+
if (callback) {
|
90
|
+
callback.call(this, gMapContext);
|
91
|
+
}
|
92
|
+
});
|
93
|
+
} else {
|
94
|
+
if (callback) {
|
95
|
+
callback.call(this, gMapContext);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
},
|
100
|
+
locationFromLatLng: function(lnlg) {
|
101
|
+
return {latitude: lnlg.lat(), longitude: lnlg.lng()}
|
102
|
+
},
|
103
|
+
address_component_from_google_geocode: function(address_components) {
|
104
|
+
var result = {};
|
105
|
+
for (var i = address_components.length-1; i>=0; i--) {
|
106
|
+
var component = address_components[i];
|
107
|
+
// Postal code
|
108
|
+
if (component.types.indexOf('postal_code') >= 0) {
|
109
|
+
result.postalCode = component.short_name;
|
110
|
+
}
|
111
|
+
// Street number
|
112
|
+
else if (component.types.indexOf('street_number') >= 0) {
|
113
|
+
result.streetNumber = component.short_name;
|
114
|
+
}
|
115
|
+
// Street name
|
116
|
+
else if (component.types.indexOf('route') >= 0) {
|
117
|
+
result.streetName = component.short_name;
|
118
|
+
}
|
119
|
+
// City
|
120
|
+
else if (component.types.indexOf('locality') >= 0) {
|
121
|
+
result.city = component.short_name;
|
122
|
+
}
|
123
|
+
// District
|
124
|
+
else if (component.types.indexOf('sublocality') >= 0) {
|
125
|
+
result.district = component.short_name;
|
126
|
+
}
|
127
|
+
// State \ Province
|
128
|
+
else if (component.types.indexOf('administrative_area_level_1') >= 0) {
|
129
|
+
result.stateOrProvince = component.short_name;
|
130
|
+
}
|
131
|
+
// State \ Province
|
132
|
+
else if (component.types.indexOf('country') >= 0) {
|
133
|
+
result.country = component.short_name;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
result.addressLine1 = [result.streetNumber, result.streetName].join(' ').trim();
|
137
|
+
result.addressLine2 = '';
|
138
|
+
return result;
|
139
|
+
}
|
140
|
+
};
|
141
|
+
|
142
|
+
function isPluginApplied(domObj) {
|
143
|
+
return getContextForElement(domObj) != undefined;
|
144
|
+
}
|
145
|
+
|
146
|
+
function getContextForElement(domObj) {
|
147
|
+
return $(domObj).data("locationpicker");
|
148
|
+
}
|
149
|
+
|
150
|
+
function updateInputValues(inputBinding, gmapContext){
|
151
|
+
if (!inputBinding) return;
|
152
|
+
var currentLocation = GmUtility.locationFromLatLng(gmapContext.location);
|
153
|
+
if (inputBinding.latitudeInput) {
|
154
|
+
inputBinding.latitudeInput.val(currentLocation.latitude).change();
|
155
|
+
}
|
156
|
+
if (inputBinding.longitudeInput) {
|
157
|
+
inputBinding.longitudeInput.val(currentLocation.longitude).change();
|
158
|
+
}
|
159
|
+
if (inputBinding.radiusInput) {
|
160
|
+
inputBinding.radiusInput.val(gmapContext.radius).change();
|
161
|
+
}
|
162
|
+
if (inputBinding.locationNameInput) {
|
163
|
+
inputBinding.locationNameInput.val(gmapContext.locationName).change();
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
function setupInputListenersInput(inputBinding, gmapContext) {
|
168
|
+
if (inputBinding) {
|
169
|
+
if (inputBinding.radiusInput){
|
170
|
+
inputBinding.radiusInput.on("change", function(e) {
|
171
|
+
if (!e.originalEvent) { return }
|
172
|
+
gmapContext.radius = $(this).val();
|
173
|
+
GmUtility.setPosition(gmapContext, gmapContext.location, function(context){
|
174
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
175
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
176
|
+
});
|
177
|
+
});
|
178
|
+
}
|
179
|
+
if (inputBinding.locationNameInput && gmapContext.settings.enableAutocomplete) {
|
180
|
+
var blur = false;
|
181
|
+
gmapContext.autocomplete = new google.maps.places.Autocomplete(inputBinding.locationNameInput.get(0));
|
182
|
+
google.maps.event.addListener(gmapContext.autocomplete, 'place_changed', function() {
|
183
|
+
blur = false;
|
184
|
+
var place = gmapContext.autocomplete.getPlace();
|
185
|
+
if (!place.geometry) {
|
186
|
+
gmapContext.settings.onlocationnotfound(place.name);
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
GmUtility.setPosition(gmapContext, place.geometry.location, function(context) {
|
190
|
+
updateInputValues(inputBinding, context);
|
191
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
192
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
193
|
+
});
|
194
|
+
});
|
195
|
+
if(gmapContext.settings.enableAutocompleteBlur) {
|
196
|
+
inputBinding.locationNameInput.on("change", function(e) {
|
197
|
+
if (!e.originalEvent) { return }
|
198
|
+
blur = true;
|
199
|
+
});
|
200
|
+
inputBinding.locationNameInput.on("blur", function(e) {
|
201
|
+
if (!e.originalEvent) { return }
|
202
|
+
setTimeout(function() {
|
203
|
+
var address = $(inputBinding.locationNameInput).val();
|
204
|
+
if (address.length > 5 && blur) {
|
205
|
+
blur = false;
|
206
|
+
gmapContext.geodecoder.geocode({'address': address}, function(results, status) {
|
207
|
+
if(status == google.maps.GeocoderStatus.OK && results && results.length) {
|
208
|
+
GmUtility.setPosition(gmapContext, results[0].geometry.location, function(context) {
|
209
|
+
updateInputValues(inputBinding, context);
|
210
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
211
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
212
|
+
});
|
213
|
+
}
|
214
|
+
});
|
215
|
+
}
|
216
|
+
}, 1000);
|
217
|
+
});
|
218
|
+
}
|
219
|
+
}
|
220
|
+
if (inputBinding.latitudeInput) {
|
221
|
+
inputBinding.latitudeInput.on("change", function(e) {
|
222
|
+
if (!e.originalEvent) { return }
|
223
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng($(this).val(), gmapContext.location.lng()), function(context){
|
224
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
225
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
226
|
+
});
|
227
|
+
});
|
228
|
+
}
|
229
|
+
if (inputBinding.longitudeInput) {
|
230
|
+
inputBinding.longitudeInput.on("change", function(e) {
|
231
|
+
if (!e.originalEvent) { return }
|
232
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(gmapContext.location.lat(), $(this).val()), function(context){
|
233
|
+
context.settings.onchanged.apply(gmapContext.domContainer,
|
234
|
+
[GmUtility.locationFromLatLng(context.location), context.radius, false]);
|
235
|
+
});
|
236
|
+
});
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
function autosize(gmapContext) {
|
242
|
+
google.maps.event.trigger(gmapContext.map, 'resize');
|
243
|
+
setTimeout(function() {
|
244
|
+
gmapContext.map.setCenter(gmapContext.marker.position);
|
245
|
+
}, 300);
|
246
|
+
}
|
247
|
+
|
248
|
+
function updateMap(gmapContext, $target, options) {
|
249
|
+
var settings = $.extend({}, $.fn.locationpicker.defaults, options ),
|
250
|
+
latNew = settings.location.latitude,
|
251
|
+
lngNew = settings.location.longitude,
|
252
|
+
radiusNew = settings.radius,
|
253
|
+
latOld = gmapContext.settings.location.latitude,
|
254
|
+
lngOld = gmapContext.settings.location.longitude,
|
255
|
+
radiusOld = gmapContext.settings.radius;
|
256
|
+
|
257
|
+
if (latNew == latOld && lngNew == lngOld && radiusNew == radiusOld)
|
258
|
+
return;
|
259
|
+
|
260
|
+
gmapContext.settings.location.latitude = latNew;
|
261
|
+
gmapContext.settings.location.longitude = lngNew;
|
262
|
+
gmapContext.radius = radiusNew;
|
263
|
+
|
264
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(gmapContext.settings.location.latitude, gmapContext.settings.location.longitude), function(context){
|
265
|
+
setupInputListenersInput(gmapContext.settings.inputBinding, gmapContext);
|
266
|
+
context.settings.oninitialized($target);
|
267
|
+
});
|
268
|
+
}
|
269
|
+
/**
|
270
|
+
* Initializeialization:
|
271
|
+
* $("#myMap").locationpicker(options);
|
272
|
+
* @param options
|
273
|
+
* @param params
|
274
|
+
* @returns {*}
|
275
|
+
*/
|
276
|
+
$.fn.locationpicker = function( options, params ) {
|
277
|
+
if (typeof options == 'string') { // Command provided
|
278
|
+
var _targetDomElement = this.get(0);
|
279
|
+
// Plug-in is not applied - nothing to do.
|
280
|
+
if (!isPluginApplied(_targetDomElement)) return;
|
281
|
+
var gmapContext = getContextForElement(_targetDomElement);
|
282
|
+
switch (options) {
|
283
|
+
case "location":
|
284
|
+
if (params == undefined) { // Getter
|
285
|
+
var location = GmUtility.locationFromLatLng(gmapContext.location);
|
286
|
+
location.radius = gmapContext.radius;
|
287
|
+
location.name = gmapContext.locationName;
|
288
|
+
return location;
|
289
|
+
} else { // Setter
|
290
|
+
if (params.radius) {
|
291
|
+
gmapContext.radius = params.radius;
|
292
|
+
}
|
293
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(params.latitude, params.longitude), function(gmapContext) {
|
294
|
+
updateInputValues(gmapContext.settings.inputBinding, gmapContext);
|
295
|
+
});
|
296
|
+
}
|
297
|
+
break;
|
298
|
+
case "subscribe":
|
299
|
+
/**
|
300
|
+
* Provides interface for subscribing for GoogleMap events.
|
301
|
+
* See Google API documentation for details.
|
302
|
+
* Parameters:
|
303
|
+
* - event: string, name of the event
|
304
|
+
* - callback: function, callback function to be invoked
|
305
|
+
*/
|
306
|
+
if (params == undefined) { // Getter is not available
|
307
|
+
return null;
|
308
|
+
} else {
|
309
|
+
var event = params.event;
|
310
|
+
var callback = params.callback;
|
311
|
+
if (!event || ! callback) {
|
312
|
+
console.error("LocationPicker: Invalid arguments for method \"subscribe\"")
|
313
|
+
return null;
|
314
|
+
}
|
315
|
+
google.maps.event.addListener(gmapContext.map, event, callback);
|
316
|
+
}
|
317
|
+
break;
|
318
|
+
case "map":
|
319
|
+
/**
|
320
|
+
* Returns object which allows access actual google widget and marker paced on it.
|
321
|
+
* Structure: {
|
322
|
+
* map: Instance of the google map widget
|
323
|
+
* marker: marker placed on map
|
324
|
+
* }
|
325
|
+
*/
|
326
|
+
if (params == undefined) { // Getter
|
327
|
+
var locationObj = GmUtility.locationFromLatLng(gmapContext.location);
|
328
|
+
locationObj.formattedAddress = gmapContext.locationName;
|
329
|
+
locationObj.addressComponents = gmapContext.addressComponents;
|
330
|
+
return {
|
331
|
+
map: gmapContext.map,
|
332
|
+
marker: gmapContext.marker,
|
333
|
+
location: locationObj
|
334
|
+
}
|
335
|
+
} else { // Setter is not available
|
336
|
+
return null;
|
337
|
+
}
|
338
|
+
case "autosize":
|
339
|
+
autosize(gmapContext);
|
340
|
+
return this;
|
341
|
+
}
|
342
|
+
return null;
|
343
|
+
}
|
344
|
+
return this.each(function() {
|
345
|
+
var $target = $(this);
|
346
|
+
// If plug-in hasn't been applied before - initialize, otherwise - skip
|
347
|
+
if (isPluginApplied(this)){
|
348
|
+
updateMap(getContextForElement(this), $(this), options);
|
349
|
+
return;
|
350
|
+
}
|
351
|
+
// Plug-in initialization is required
|
352
|
+
// Defaults
|
353
|
+
var settings = $.extend({}, $.fn.locationpicker.defaults, options );
|
354
|
+
// Initialize
|
355
|
+
var gmapContext = new GMapContext(this, {
|
356
|
+
zoom: settings.zoom,
|
357
|
+
center: new google.maps.LatLng(settings.location.latitude, settings.location.longitude),
|
358
|
+
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
359
|
+
mapTypeControl: false,
|
360
|
+
disableDoubleClickZoom: false,
|
361
|
+
scrollwheel: settings.scrollwheel,
|
362
|
+
streetViewControl: false,
|
363
|
+
radius: settings.radius,
|
364
|
+
locationName: settings.locationName,
|
365
|
+
settings: settings,
|
366
|
+
draggable: settings.draggable
|
367
|
+
});
|
368
|
+
$target.data("locationpicker", gmapContext);
|
369
|
+
// Subscribe GMap events
|
370
|
+
google.maps.event.addListener(gmapContext.marker, "dragend", function(event) {
|
371
|
+
GmUtility.setPosition(gmapContext, gmapContext.marker.position, function(context){
|
372
|
+
var currentLocation = GmUtility.locationFromLatLng(gmapContext.location);
|
373
|
+
context.settings.onchanged.apply(gmapContext.domContainer, [currentLocation, context.radius, true]);
|
374
|
+
updateInputValues(gmapContext.settings.inputBinding, gmapContext);
|
375
|
+
});
|
376
|
+
});
|
377
|
+
GmUtility.setPosition(gmapContext, new google.maps.LatLng(settings.location.latitude, settings.location.longitude), function(context){
|
378
|
+
updateInputValues(settings.inputBinding, gmapContext);
|
379
|
+
// Set input bindings if needed
|
380
|
+
setupInputListenersInput(settings.inputBinding, gmapContext);
|
381
|
+
context.settings.oninitialized($target);
|
382
|
+
});
|
383
|
+
});
|
384
|
+
};
|
385
|
+
$.fn.locationpicker.defaults = {
|
386
|
+
location: {latitude: 40.7324319, longitude: -73.82480777777776},
|
387
|
+
locationName: "",
|
388
|
+
radius: 500,
|
389
|
+
zoom: 15,
|
390
|
+
scrollwheel: true,
|
391
|
+
inputBinding: {
|
392
|
+
latitudeInput: null,
|
393
|
+
longitudeInput: null,
|
394
|
+
radiusInput: null,
|
395
|
+
locationNameInput: null
|
396
|
+
},
|
397
|
+
enableAutocomplete: false,
|
398
|
+
enableAutocompleteBlur: false,
|
399
|
+
enableReverseGeocode: true,
|
400
|
+
draggable: true,
|
401
|
+
onchanged: function(currentLocation, radius, isMarkerDropped) {},
|
402
|
+
onlocationnotfound: function(locationName) {},
|
403
|
+
oninitialized: function (component) {}
|
404
|
+
}
|
405
|
+
}( jQuery ));
|
@@ -10,6 +10,8 @@ module Voluntary
|
|
10
10
|
|
11
11
|
helper_method :parent, :application_navigation, :navigation_product_path, :navigation_product_name, :voluntary_application_stylesheets
|
12
12
|
helper_method :voluntary_application_javascripts
|
13
|
+
|
14
|
+
before_filter :set_timezone
|
13
15
|
end
|
14
16
|
|
15
17
|
def voluntary_application_stylesheets
|
@@ -77,6 +79,10 @@ module Voluntary
|
|
77
79
|
|
78
80
|
private
|
79
81
|
|
82
|
+
def set_timezone
|
83
|
+
Time.zone = current_user.try(:timezone).present? ? current_user.timezone : 'UTC'
|
84
|
+
end
|
85
|
+
|
80
86
|
def current_namespace
|
81
87
|
controller_name_segments = params[:controller].split('/')
|
82
88
|
controller_name_segments.pop
|