site.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. "use strict";
  2. $(function () {
  3. $('.fieldlist').each(function() {
  4. var $this=$(this);
  5. var lis=$this.children('li');
  6. lis.first().children(':first').after(' <button class="btn btn-mini" type="button"><i class="icon-plus"></i></button>');
  7. lis.first().children('button').click(function() {
  8. clone_fieldlist($this.children('li:last'));
  9. });
  10. lis=lis.slice(1);
  11. lis.each(function() {
  12. append_remove_button($(this));
  13. });
  14. });
  15. $('.selectpicker').selectpicker();
  16. $("[rel=tooltip]").tooltip();
  17. var Geoinput = function(el, options, e) {
  18. this.$element = $(el);
  19. this.init();
  20. };
  21. Geoinput.prototype = {
  22. constructor: Geoinput,
  23. init: function() {
  24. this.$element.hide();
  25. this.$button = this.makeButton();
  26. this.$modal = this.makeModal();
  27. this.$element.after(this.$button);
  28. this.$element.after(this.$modal);
  29. this.$modal.find('textarea').val(this.$element.val());
  30. this.buttonIcon();
  31. var that = this;
  32. this.$button.click(function(e) {
  33. e.preventDefault();
  34. that.$modal.modal();
  35. return false;
  36. });
  37. this.$modal.find('.btn-primary').click(function(e) {
  38. e.preventDefault();
  39. that.$modal.modal('hide');
  40. that.$element.val(that.$modal.find('textarea').val());
  41. that.buttonIcon.call(that);
  42. return false;
  43. });
  44. },
  45. buttonIcon: function() {
  46. if(this.$element.val())
  47. this.$button[0].firstChild.src = '/static/img/map_edit.png';
  48. else
  49. this.$button[0].firstChild.src = '/static/img/map.png';
  50. },
  51. makeButton: function() {
  52. return $('<button/>').addClass("btn btn-default geoinput-button")
  53. .css('padding', '4px 7px')
  54. .attr('title', 'enter geojson')
  55. .html('<img src="/static/img/map.png" alt="map">');
  56. },
  57. makeModal: function() {
  58. return $('<div class="modal hide geoinput-modal">'+
  59. '<div class="modal-header">'+
  60. '<h3>GeoJSON Input</h3>'+
  61. '</div>'+
  62. '<div class="modal-body">'+
  63. '<p>Paste your GeoJSON here:</p>'+
  64. '<textarea style="width: 97%; height: 200px"></textarea>'+
  65. '</div>'+
  66. '<div class="modal-footer">'+
  67. '<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>'+
  68. '<button class="btn btn-primary">Done</button>'+
  69. '</div>'+
  70. '</div>')
  71. }
  72. }
  73. $.fn.geoinput = function(options, event) {
  74. return this.each(function() {
  75. var $this = $(this), data = $this.data('geoinput');
  76. if($this.is('input, textarea')) {
  77. $this.data('geoinput', (data = new Geoinput(this, options, event)));
  78. }
  79. });
  80. };
  81. $('.geoinput').geoinput();
  82. init_map();
  83. });
  84. function layer_from_covered_area(ca) {
  85. return L.geoJson(ca['area'], {
  86. style: {
  87. "color": "#ff7800",
  88. "weight": 5,
  89. "opacity": 0.65
  90. }
  91. });
  92. }
  93. function get_covered_areas(isp_id, cb) {
  94. if('areas' in window.isp_list[isp_id]) {
  95. cb(window.isp_list[isp_id]['areas']);
  96. return;
  97. } else {
  98. window.isp_list[isp_id]['areas']=[];
  99. }
  100. return $.getJSON('/isp/'+isp_id+'/covered_areas.json', function done(data) {
  101. $.each(data, function(k, covered_area) {
  102. if(!covered_area['area'])
  103. return;
  104. covered_area['layer']=layer_from_covered_area(covered_area);
  105. window.isp_list[isp_id]['areas'].push(
  106. covered_area
  107. );
  108. });
  109. cb(window.isp_list[isp_id]['areas']);
  110. });
  111. }
  112. L.Control.Pinpoint = L.Control.extend({
  113. options: {
  114. position: 'topleft'
  115. },
  116. onAdd: function(map) {
  117. this._map = map;
  118. this.select_mode = false;
  119. this._container = L.DomUtil.create('div', 'leaflet-control-pinpoint leaflet-bar');
  120. this._button = L.DomUtil.create('a', 'leaflet-control-pinpoint-button', this._container);
  121. this._button.href = '#';
  122. this._button.innerHTML = '<i class="icon-hand-down"></i>';
  123. this._button.style = 'cursor: pointer';
  124. this._button.title = 'Find ISPs near you';
  125. L.DomEvent
  126. .addListener(this._button, 'click', L.DomEvent.stop)
  127. .addListener(this._button, 'click', L.DomEvent.stopPropagation)
  128. .addListener(this._button, 'click', function() {
  129. if(this.select_mode) {
  130. this._map.removeLayer(this._marker);
  131. this._disableSelect();
  132. } else {
  133. this._enableSelect();
  134. }
  135. }, this);
  136. this._icon = L.icon({
  137. iconUrl: 'static/img/marker_selector.png',
  138. iconSize: [18, 28],
  139. iconAnchor: [9, 26],
  140. });
  141. this._marker = L.marker([0, 0], {icon: this._icon, draggable: true});
  142. this._marker.on('dragend', this.findNearISP, this);
  143. return this._container;
  144. },
  145. _enableSelect: function() {
  146. this._marker.addTo(this._map);
  147. this._map.on('mousemove', this._mouseMove, this);
  148. this._map.on('click', this._setMarker, this);
  149. this._map._container.style.cursor = 'crosshair';
  150. this._marker._icon.style.cursor = 'crosshair';
  151. this.select_mode = true;
  152. },
  153. _disableSelect: function() {
  154. this._map.off('mousemove', this._mouseMove, this);
  155. this._map.off('click', this._setMarker, this);
  156. this._map._container.style.cursor = 'default';
  157. if(!!this._marker._icon)
  158. this._marker._icon.style.cursor = 'default';
  159. this.select_mode = false;
  160. },
  161. _mouseMove: function(e) {
  162. this._marker.setLatLng(e.latlng);
  163. },
  164. _setMarker: function(e) {
  165. this._disableSelect();
  166. this.findNearISP();
  167. },
  168. findNearISP: function() {
  169. var c=this._marker.getLatLng();
  170. var map=this._map;
  171. $.getJSON('/isp/find_near.json?lon='+c.lng+'&lat='+c.lat, function(data) {
  172. var bnds;
  173. if(data[0].length) {
  174. var bnds=new L.LatLngBounds;
  175. var defered=[];
  176. $.each(data[0], function(k, match) {
  177. var isp=window.isp_list[match['isp_id']];
  178. defered.push(get_covered_areas(match['isp_id'], $.noop));
  179. });
  180. $.when.apply(this, defered).done(function() {
  181. $.each(data[0], function(k, match) {
  182. var isp=window.isp_list[match['isp_id']];
  183. var ispc=isp['marker'].getLatLng();
  184. var matching=null;
  185. $.each(isp['areas'], function(j, a) {
  186. if(a['id'] == match['area']['id'])
  187. matching = a;
  188. });
  189. bnds.extend([ispc['lat'], ispc['lng']]);
  190. isp['marker'].openPopup();
  191. if(matching !== null) {
  192. bnds.extend(matching['layer'].getBounds());
  193. matching['layer'].addTo(map);
  194. }
  195. });
  196. bnds.extend(c);
  197. bnds=bnds.pad(0.3);
  198. map.fitBounds(bnds, {paddingTopLeft: [20, 20]});
  199. });
  200. } else {
  201. var r=$.map(data[1], function(match, k) {
  202. var m=window.isp_list[match['isp_id']]['marker'];
  203. var ispc=m.getLatLng();
  204. if(k == 0) {
  205. map.closePopup();
  206. m.openPopup();
  207. }
  208. return [[ispc.lat, ispc.lng]];
  209. });
  210. r.push([c.lat, c.lng])
  211. bnds=new L.LatLngBounds(r);
  212. bnds=bnds.pad(0.3);
  213. map.fitBounds(bnds, {paddingTopLeft: [20, 20]});
  214. }
  215. });
  216. }
  217. })
  218. function init_map() {
  219. var mapquest=L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg', {
  220. attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, '+
  221. 'Tiles courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a>',
  222. subdomains: '1234'
  223. });
  224. var mapquestsat=L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', {
  225. attribution: '&copy; Tiles courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a>, '+
  226. 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency',
  227. subdomains: '1234',
  228. maxZoom: 11
  229. });
  230. var osm=L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  231. attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors',
  232. subdomains: 'ab'
  233. });
  234. var hyb=L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/hyb/{z}/{x}/{y}.jpg', {
  235. subdomains: '1234',
  236. maxZoom: 11
  237. });
  238. if(!$('#map').length)
  239. return;
  240. var map = L.map('map', {
  241. center: new L.LatLng(46.603354, 10),
  242. zoom: 4,
  243. minZoom: 2,
  244. layers: [mapquest],
  245. worldCopyJump: true
  246. });
  247. map.attributionControl.setPrefix('');
  248. map.addControl(new L.Control.Pinpoint);
  249. L.control.layers({'MapQuest': mapquest, 'OSM Mapnik': osm, 'MapQuest Aerial': mapquestsat}).addTo(map);
  250. map.on('baselayerchange', function(a) {
  251. if(a.name == 'MapQuest Aerial') {
  252. map.addLayer(hyb);
  253. hyb.bringToFront();
  254. } else {
  255. map.removeLayer(hyb);
  256. }
  257. });
  258. var icon = L.icon({
  259. iconUrl: 'static/img/marker.png',
  260. iconSize: [14, 20], // size of the icon
  261. iconAnchor: [7, 20], // point of the icon which will correspond to marker's location
  262. popupAnchor: [0, -20] // point from which the popup should open relative to the iconAnchor
  263. });
  264. var icon_ffdn = $.extend(true, {}, icon);
  265. icon_ffdn['options']['iconUrl'] = 'static/img/marker_ffdn.png';
  266. window.isp_list={};
  267. $.getJSON('/isp/map_data.json', function(data) {
  268. $.each(data, function(k, isp) {
  269. window.isp_list[isp.id]=isp;
  270. if(!('coordinates' in isp))
  271. return; // cannot display an ISP without coordinates
  272. var marker = L.marker([isp['coordinates']['latitude'], isp['coordinates']['longitude']],
  273. {'icon': isp.ffdn_member ? icon_ffdn : icon});
  274. marker.bindPopup(isp.popup);
  275. marker.getPopup().on('open', function() {
  276. get_covered_areas(isp.id, function(items) {
  277. $.each(items, function(k, ca) {
  278. ca['layer'].addTo(map);
  279. });
  280. });
  281. });
  282. marker.getPopup().on('close', function() {
  283. $.each(window.isp_list[isp.id]['areas'], function(k, ca) {
  284. map.removeLayer(ca['layer']);
  285. });
  286. });
  287. marker.addTo(map);
  288. window.isp_list[isp.id]['marker']=marker;
  289. });
  290. });
  291. }
  292. function change_input_num(li, new_num, reset) {
  293. li.find('input,select,textarea').each(function() {
  294. var id = $(this).attr('id').replace(/^(.*)-\d{1,4}/, '$1-'+new_num);
  295. $(this).attr({'name': id, 'id': id});
  296. if(!!reset)
  297. $(this).val('').removeAttr('checked');
  298. });
  299. }
  300. function append_remove_button(li) {
  301. li.children(':first').after(' <button class="btn btn-mini" type="button"><i class="icon-minus"></i></button>');
  302. li.children('button').click(function() {
  303. var ul=li.parent();
  304. li.remove();
  305. var i=0;
  306. ul.children('li').each(function() {
  307. change_input_num($(this), i);
  308. i++;
  309. });
  310. });
  311. };
  312. function clone_fieldlist(el) {
  313. var new_element = el.clone(true);
  314. var elem_id = new_element.find(':input')[0].id;
  315. var elem_num = parseInt(elem_id.replace(/^.*-(\d{1,4})/, '$1')) + 1;
  316. new_element.children('button').remove();
  317. new_element.children('.help-inline.error-list').remove();
  318. new_element.find('.bootstrap-select').remove();
  319. new_element.find('.geoinput-button').remove();
  320. new_element.find('.geoinput-modal').remove();
  321. change_input_num(new_element, elem_num, true);
  322. new_element.find('.selectpicker').data('selectpicker', null).selectpicker();
  323. new_element.find('.geoinput').geoinput();
  324. append_remove_button(new_element);
  325. el.after(new_element);
  326. }