foliage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +9 -0
  4. data/app/assets/images/.keep +0 -0
  5. data/app/assets/images/map/marker/icon-2x.png +0 -0
  6. data/app/assets/images/map/marker/icon.png +0 -0
  7. data/app/assets/images/map/marker/icon.svg +67 -0
  8. data/app/assets/images/map/marker/shadow.png +0 -0
  9. data/app/assets/javascripts/core_ext.js.coffee +61 -0
  10. data/app/assets/javascripts/foliage.js.coffee +23 -0
  11. data/app/assets/javascripts/foliage/band.js.coffee +99 -0
  12. data/app/assets/javascripts/foliage/bubbles.js.coffee +77 -0
  13. data/app/assets/javascripts/foliage/categories.js.coffee +70 -0
  14. data/app/assets/javascripts/foliage/choropleth.js.coffee +51 -0
  15. data/app/assets/javascripts/foliage/color.js.coffee +39 -0
  16. data/app/assets/javascripts/foliage/gradient.js.coffee +72 -0
  17. data/app/assets/javascripts/foliage/heatmap.js.coffee +49 -0
  18. data/app/assets/javascripts/foliage/leaf.js.coffee +422 -0
  19. data/app/assets/javascripts/foliage/path.js.coffee +76 -0
  20. data/app/assets/javascripts/foliage/paths.js.coffee +131 -0
  21. data/app/assets/javascripts/foliage/point_group.js.coffee +83 -0
  22. data/app/assets/javascripts/foliage/points.js.coffee +79 -0
  23. data/app/assets/javascripts/foliage/simple.js.coffee +35 -0
  24. data/app/assets/javascripts/leaflet/geographic_util.js.coffee +23 -0
  25. data/app/assets/javascripts/leaflet/ghost_label.js.coffee +100 -0
  26. data/app/assets/javascripts/leaflet/ghost_label_cluster.js.coffee +192 -0
  27. data/app/assets/javascripts/leaflet/layers_scheduler.js.coffee +57 -0
  28. data/app/assets/javascripts/leaflet/reactive_measure.js.coffee +414 -0
  29. data/app/assets/stylesheets/all.scss +16 -0
  30. data/app/assets/stylesheets/application.css +15 -0
  31. data/app/assets/stylesheets/compass/reset.scss +3 -0
  32. data/app/assets/stylesheets/compass/reset/utilities.scss +142 -0
  33. data/app/assets/stylesheets/leaflet.scss +1093 -0
  34. data/app/assets/stylesheets/leaflet/label.scss +40 -0
  35. data/app/assets/stylesheets/leaflet/tooltip.scss +42 -0
  36. data/app/assets/stylesheets/mixins.scss +131 -0
  37. data/app/assets/stylesheets/reset.scss +89 -0
  38. data/app/assets/stylesheets/variables.scss +47 -0
  39. data/app/helpers/foliage_helper.rb +23 -0
  40. data/lib/foliage.rb +9 -0
  41. data/lib/foliage/leaf.rb +235 -0
  42. data/lib/foliage/rails.rb +2 -0
  43. data/lib/foliage/rails/engine.rb +7 -0
  44. data/lib/foliage/rails/integration.rb +8 -0
  45. data/lib/foliage/version.rb +3 -0
  46. data/vendor/assets/javascripts/.keep +0 -0
  47. data/vendor/assets/javascripts/autosize.js +211 -0
  48. data/vendor/assets/javascripts/geographiclib.js +3074 -0
  49. data/vendor/assets/javascripts/leaflet.js.erb +9175 -0
  50. data/vendor/assets/javascripts/leaflet/draw.js +3573 -0
  51. data/vendor/assets/javascripts/leaflet/easy-button.js +366 -0
  52. data/vendor/assets/javascripts/leaflet/fullscreen.js +162 -0
  53. data/vendor/assets/javascripts/leaflet/heatmap.js +142 -0
  54. data/vendor/assets/javascripts/leaflet/label.js +545 -0
  55. data/vendor/assets/javascripts/leaflet/measure.js +6966 -0
  56. data/vendor/assets/javascripts/leaflet/modal.js +364 -0
  57. data/vendor/assets/javascripts/leaflet/providers.js +479 -0
  58. data/vendor/assets/javascripts/rbush.js +621 -0
  59. data/vendor/assets/stylesheets/.keep +0 -0
  60. data/vendor/assets/stylesheets/bootstrap/mixins.scss +55 -0
  61. data/vendor/assets/stylesheets/bootstrap/variables.scss +10 -0
  62. data/vendor/assets/stylesheets/leaflet.scss +479 -0
  63. data/vendor/assets/stylesheets/leaflet/draw.scss +282 -0
  64. data/vendor/assets/stylesheets/leaflet/easy-button.scss +56 -0
  65. data/vendor/assets/stylesheets/leaflet/fullscreen.scss +2 -0
  66. data/vendor/assets/stylesheets/leaflet/measure.scss +168 -0
  67. data/vendor/assets/stylesheets/leaflet/modal.scss +85 -0
  68. metadata +171 -0
@@ -0,0 +1,366 @@
1
+ (function(){
2
+
3
+ // This is for grouping buttons into a bar
4
+ // takes an array of `L.easyButton`s and
5
+ // then the usual `.addTo(map)`
6
+ L.Control.EasyBar = L.Control.extend({
7
+
8
+ options: {
9
+ position: 'topleft', // part of leaflet's defaults
10
+ id: null, // an id to tag the Bar with
11
+ leafletClasses: true // use leaflet classes?
12
+ },
13
+
14
+
15
+ initialize: function(buttons, options){
16
+
17
+ if(options){
18
+ L.Util.setOptions( this, options );
19
+ }
20
+
21
+ this._buildContainer();
22
+ this._buttons = [];
23
+
24
+ for(var i = 0; i < buttons.length; i++){
25
+ buttons[i]._bar = this;
26
+ buttons[i]._container = buttons[i].button;
27
+ this._buttons.push(buttons[i]);
28
+ this.container.appendChild(buttons[i].button);
29
+ }
30
+
31
+ },
32
+
33
+
34
+ _buildContainer: function(){
35
+ this._container = this.container = L.DomUtil.create('div', '');
36
+ this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
37
+ this.options.id && (this.container.id = this.options.id);
38
+ },
39
+
40
+
41
+ enable: function(){
42
+ L.DomUtil.addClass(this.container, 'enabled');
43
+ L.DomUtil.removeClass(this.container, 'disabled');
44
+ this.container.setAttribute('aria-hidden', 'false');
45
+ return this;
46
+ },
47
+
48
+
49
+ disable: function(){
50
+ L.DomUtil.addClass(this.container, 'disabled');
51
+ L.DomUtil.removeClass(this.container, 'enabled');
52
+ this.container.setAttribute('aria-hidden', 'true');
53
+ return this;
54
+ },
55
+
56
+
57
+ onAdd: function () {
58
+ return this.container;
59
+ },
60
+
61
+ addTo: function (map) {
62
+ this._map = map;
63
+
64
+ for(var i = 0; i < this._buttons.length; i++){
65
+ this._buttons[i]._map = map;
66
+ }
67
+
68
+ var container = this._container = this.onAdd(map),
69
+ pos = this.getPosition(),
70
+ corner = map._controlCorners[pos];
71
+
72
+ L.DomUtil.addClass(container, 'leaflet-control');
73
+
74
+ if (pos.indexOf('bottom') !== -1) {
75
+ corner.insertBefore(container, corner.firstChild);
76
+ } else {
77
+ corner.appendChild(container);
78
+ }
79
+
80
+ return this;
81
+ }
82
+
83
+ });
84
+
85
+ L.easyBar = function(){
86
+ var args = [L.Control.EasyBar];
87
+ for(var i = 0; i < arguments.length; i++){
88
+ args.push( arguments[i] );
89
+ }
90
+ return new (Function.prototype.bind.apply(L.Control.EasyBar, args));
91
+ };
92
+
93
+ // L.EasyButton is the actual buttons
94
+ // can be called without being grouped into a bar
95
+ L.Control.EasyButton = L.Control.extend({
96
+
97
+ options: {
98
+ position: 'topleft', // part of leaflet's defaults
99
+
100
+ id: null, // an id to tag the button with
101
+
102
+ type: 'replace', // [(replace|animate)]
103
+ // replace swaps out elements
104
+ // animate changes classes with all elements inserted
105
+
106
+ states: [], // state names look like this
107
+ // {
108
+ // stateName: 'untracked',
109
+ // onClick: function(){ handle_nav_manually(); };
110
+ // title: 'click to make inactive',
111
+ // icon: 'fa-circle', // wrapped with <a>
112
+ // }
113
+
114
+ leafletClasses: true // use leaflet styles for the button
115
+ },
116
+
117
+
118
+
119
+ initialize: function(icon, onClick, title){
120
+
121
+ // clear the states manually
122
+ this.options.states = [];
123
+
124
+ // storage between state functions
125
+ this.storage = {};
126
+
127
+ // is the last item an object?
128
+ if( typeof arguments[arguments.length-1] === 'object' ){
129
+
130
+ // if so, it should be the options
131
+ L.Util.setOptions( this, arguments[arguments.length-1] );
132
+ }
133
+
134
+ // if there aren't any states in options
135
+ // use the early params
136
+ if( this.options.states.length === 0 &&
137
+ typeof icon === 'string' &&
138
+ typeof onClick === 'function'){
139
+
140
+ // turn the options object into a state
141
+ this.options.states.push({
142
+ icon: icon,
143
+ onClick: onClick,
144
+ title: typeof title === 'string' ? title : ''
145
+ });
146
+ }
147
+
148
+ // curate and move user's states into
149
+ // the _states for internal use
150
+ this._states = [];
151
+
152
+ for(var i = 0; i < this.options.states.length; i++){
153
+ this._states.push( new State(this.options.states[i], this) );
154
+ }
155
+
156
+ this._buildButton();
157
+
158
+ this._activateState(this._states[0]);
159
+
160
+ },
161
+
162
+ _buildButton: function(){
163
+
164
+ this.button = L.DomUtil.create('button', '');
165
+
166
+ if (this.options.id ){
167
+ this.button.id = this.options.id;
168
+ }
169
+
170
+ if (this.options.leafletClasses){
171
+ L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part');
172
+ }
173
+
174
+ // don't let double clicks get to the map
175
+ L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
176
+
177
+ // take care of normal clicks
178
+ L.DomEvent.addListener(this.button,'click', function(e){
179
+ L.DomEvent.stop(e);
180
+ this._currentState.onClick(this, this._map ? this._map : null );
181
+ this._map.getContainer().focus();
182
+ }, this);
183
+
184
+ // prep the contents of the control
185
+ if(this.options.type == 'replace'){
186
+ this.button.appendChild(this._currentState.icon);
187
+ } else {
188
+ for(var i=0;i<this._states.length;i++){
189
+ this.button.appendChild(this._states[i].icon);
190
+ }
191
+ }
192
+ },
193
+
194
+
195
+ _currentState: {
196
+ // placeholder content
197
+ stateName: 'unnamed',
198
+ icon: (function(){ return document.createElement('span'); })()
199
+ },
200
+
201
+
202
+
203
+ _states: null, // populated on init
204
+
205
+
206
+
207
+ state: function(newState){
208
+
209
+ // activate by name
210
+ if(typeof newState == 'string'){
211
+
212
+ this._activateStateNamed(newState);
213
+
214
+ // activate by index
215
+ } else if (typeof newState == 'number'){
216
+
217
+ this._activateState(this._states[newState]);
218
+ }
219
+
220
+ return this;
221
+ },
222
+
223
+
224
+ _activateStateNamed: function(stateName){
225
+ for(var i = 0; i < this._states.length; i++){
226
+ if( this._states[i].stateName == stateName ){
227
+ this._activateState( this._states[i] );
228
+ }
229
+ }
230
+ },
231
+
232
+ _activateState: function(newState){
233
+
234
+ if( newState === this._currentState ){
235
+
236
+ // don't touch the dom if it'll just be the same after
237
+ return;
238
+
239
+ } else {
240
+
241
+ // swap out elements... if you're into that kind of thing
242
+ if( this.options.type == 'replace' ){
243
+ this.button.appendChild(newState.icon);
244
+ this.button.removeChild(this._currentState.icon);
245
+ }
246
+
247
+ if( newState.title ){
248
+ this.button.title = newState.title;
249
+ } else {
250
+ this.button.removeAttribute('title');
251
+ }
252
+
253
+ // update classes for animations
254
+ for(var i=0;i<this._states.length;i++){
255
+ L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
256
+ L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
257
+ }
258
+
259
+ // update classes for animations
260
+ L.DomUtil.removeClass(this.button, this._currentState.stateName + '-active');
261
+ L.DomUtil.addClass(this.button, newState.stateName + '-active');
262
+
263
+ // update the record
264
+ this._currentState = newState;
265
+
266
+ }
267
+ },
268
+
269
+
270
+
271
+ enable: function(){
272
+ L.DomUtil.addClass(this.button, 'enabled');
273
+ L.DomUtil.removeClass(this.button, 'disabled');
274
+ this.button.setAttribute('aria-hidden', 'false');
275
+ return this;
276
+ },
277
+
278
+
279
+
280
+ disable: function(){
281
+ L.DomUtil.addClass(this.button, 'disabled');
282
+ L.DomUtil.removeClass(this.button, 'enabled');
283
+ this.button.setAttribute('aria-hidden', 'true');
284
+ return this;
285
+ },
286
+
287
+
288
+ removeFrom: function (map) {
289
+
290
+ this._container.parentNode.removeChild(this._container);
291
+ this._map = null;
292
+
293
+ return this;
294
+ },
295
+
296
+ onAdd: function(){
297
+ var containerObj = L.easyBar([this], {
298
+ position: this.options.position,
299
+ leafletClasses: this.options.leafletClasses
300
+ });
301
+ this._container = containerObj.container;
302
+ return this._container;
303
+ }
304
+
305
+
306
+ });
307
+
308
+ L.easyButton = function(/* args will pass automatically */){
309
+ var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments)
310
+ return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
311
+ };
312
+
313
+ /*************************
314
+ *
315
+ * util functions
316
+ *
317
+ *************************/
318
+
319
+ // constructor for states so only curated
320
+ // states end up getting called
321
+ function State(template, easyButton){
322
+
323
+ this.title = template.title;
324
+ this.stateName = template.stateName ? template.stateName : 'unnamed-state';
325
+
326
+ // build the wrapper
327
+ this.icon = L.DomUtil.create('span', '');
328
+
329
+ L.DomUtil.addClass(this.icon, 'button-state state-' + this.stateName.trim());
330
+ this.icon.innerHTML = buildIcon(template.icon);
331
+ this.onClick = L.Util.bind(template.onClick?template.onClick:function(){}, easyButton);
332
+ }
333
+
334
+ function buildIcon(ambiguousIconString) {
335
+
336
+ var tmpIcon;
337
+
338
+ // does this look like html? (i.e. not a class)
339
+ if( ambiguousIconString.match(/[&;=<>"']/) ){
340
+
341
+ // if so, the user should have put in html
342
+ // so move forward as such
343
+ tmpIcon = ambiguousIconString;
344
+
345
+ // then it wasn't html, so
346
+ // it's a class list, figure out what kind
347
+ } else {
348
+ ambiguousIconString = ambiguousIconString.trim();
349
+ tmpIcon = L.DomUtil.create('span', '');
350
+
351
+ if( ambiguousIconString.indexOf('fa-') === 0 ){
352
+ L.DomUtil.addClass(tmpIcon, 'fa ' + ambiguousIconString)
353
+ } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
354
+ L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
355
+ } else {
356
+ L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
357
+ }
358
+
359
+ // make this a string so that it's easy to set innerHTML below
360
+ tmpIcon = tmpIcon.outerHTML;
361
+ }
362
+
363
+ return tmpIcon;
364
+ }
365
+
366
+ })();
@@ -0,0 +1,162 @@
1
+ (function() {
2
+
3
+ L.Control.FullScreen = L.Control.extend({
4
+ options: {
5
+ position: 'topleft',
6
+ title: 'Full Screen',
7
+ forceSeparateButton: false
8
+ },
9
+
10
+ onAdd: function (map) {
11
+ var className = 'leaflet-control-zoom-fullscreen', container;
12
+
13
+ if (map.zoomControl && !this.options.forceSeparateButton) {
14
+ container = map.zoomControl._container;
15
+ } else {
16
+ container = L.DomUtil.create('div', 'leaflet-bar');
17
+ }
18
+
19
+ this._createButton(this.options.title, className, container, this.toogleFullScreen, map);
20
+
21
+ return container;
22
+ },
23
+
24
+ _createButton: function (title, className, container, fn, context) {
25
+ var link = L.DomUtil.create('a', className, container);
26
+ link.href = '#';
27
+ link.title = title;
28
+
29
+ L.DomEvent
30
+ .addListener(link, 'click', L.DomEvent.stopPropagation)
31
+ .addListener(link, 'click', L.DomEvent.preventDefault)
32
+ .addListener(link, 'click', fn, context);
33
+
34
+ L.DomEvent
35
+ .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
36
+ .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
37
+ .addListener(container, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
38
+
39
+ L.DomEvent
40
+ .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
41
+ .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
42
+ .addListener(document, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
43
+
44
+ return link;
45
+ },
46
+
47
+ toogleFullScreen: function () {
48
+ this._exitFired = false;
49
+ var container = this._container;
50
+ if (this._isFullscreen) {
51
+ if (fullScreenApi.supportsFullScreen) {
52
+ fullScreenApi.cancelFullScreen(container);
53
+ } else {
54
+ L.DomUtil.removeClass(container, 'leaflet-pseudo-fullscreen');
55
+ }
56
+ this.invalidateSize();
57
+ this.fire('exitFullscreen');
58
+ this._exitFired = true;
59
+ this._isFullscreen = false;
60
+ }
61
+ else {
62
+ if (fullScreenApi.supportsFullScreen) {
63
+ fullScreenApi.requestFullScreen(container);
64
+ } else {
65
+ L.DomUtil.addClass(container, 'leaflet-pseudo-fullscreen');
66
+ }
67
+ this.invalidateSize();
68
+ this.fire('enterFullscreen');
69
+ this._isFullscreen = true;
70
+ }
71
+ },
72
+
73
+ _handleEscKey: function () {
74
+ if (!fullScreenApi.isFullScreen(this) && !this._exitFired) {
75
+ this.fire('exitFullscreen');
76
+ this._exitFired = true;
77
+ this._isFullscreen = false;
78
+ }
79
+ }
80
+ });
81
+
82
+ L.Map.addInitHook(function () {
83
+ if (this.options.fullscreenControl) {
84
+ this.fullscreenControl = L.control.fullscreen(this.options.fullscreenControlOptions);
85
+ this.addControl(this.fullscreenControl);
86
+ }
87
+ });
88
+
89
+ L.control.fullscreen = function (options) {
90
+ return new L.Control.FullScreen(options);
91
+ };
92
+
93
+ /*
94
+ Native FullScreen JavaScript API
95
+ -------------
96
+ Assumes Mozilla naming conventions instead of W3C for now
97
+
98
+ source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
99
+
100
+ */
101
+
102
+ var
103
+ fullScreenApi = {
104
+ supportsFullScreen: false,
105
+ isFullScreen: function() { return false; },
106
+ requestFullScreen: function() {},
107
+ cancelFullScreen: function() {},
108
+ fullScreenEventName: '',
109
+ prefix: ''
110
+ },
111
+ browserPrefixes = 'webkit moz o ms khtml'.split(' ');
112
+
113
+ // check for native support
114
+ if (typeof document.exitFullscreen != 'undefined') {
115
+ fullScreenApi.supportsFullScreen = true;
116
+ } else {
117
+ // check for fullscreen support by vendor prefix
118
+ for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
119
+ fullScreenApi.prefix = browserPrefixes[i];
120
+ if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
121
+ fullScreenApi.supportsFullScreen = true;
122
+ break;
123
+ }
124
+ }
125
+ }
126
+
127
+ // update methods to do something useful
128
+ if (fullScreenApi.supportsFullScreen) {
129
+ fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
130
+ fullScreenApi.isFullScreen = function() {
131
+ switch (this.prefix) {
132
+ case '':
133
+ return document.fullScreen;
134
+ case 'webkit':
135
+ return document.webkitIsFullScreen;
136
+ default:
137
+ return document[this.prefix + 'FullScreen'];
138
+ }
139
+ }
140
+ fullScreenApi.requestFullScreen = function(el) {
141
+ return (this.prefix === '') ? el.requestFullscreen() : el[this.prefix + 'RequestFullScreen']();
142
+ }
143
+ fullScreenApi.cancelFullScreen = function(el) {
144
+ return (this.prefix === '') ? document.exitFullscreen() : document[this.prefix + 'CancelFullScreen']();
145
+ }
146
+ }
147
+
148
+ // jQuery plugin
149
+ if (typeof jQuery != 'undefined') {
150
+ jQuery.fn.requestFullScreen = function() {
151
+ return this.each(function() {
152
+ var el = jQuery(this);
153
+ if (fullScreenApi.supportsFullScreen) {
154
+ fullScreenApi.requestFullScreen(el);
155
+ }
156
+ });
157
+ };
158
+ }
159
+
160
+ // export api
161
+ window.fullScreenApi = fullScreenApi;
162
+ })();