foliage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ })();