Our path to winning the (ArcGIS.js <= 100) Responsive Edition App Challenge
Featuring Bootstrap, Backbone.js and much more!
With Jeremy Folds and Nick Volpe
GIS Developers with DTS Agile
My boss's Flex Skills:
My Flex Skills:
Just do it.
Make mistakes
Debug
Make more mistakes
Debug
dojo.require('esri.map', 'esri.tasks.locator', 'esri.geometry.webMercatorUtils');
dojo.addOnLoad(function () {
var AppView = Backbone.View.extend({
el: 'body',
initialize: function() {
_.bindAll.apply(_, [this].concat(_.functions(this)));
var $this = this;
this.model = new (Backbone.Model.extend({}))();
this.model.on('change', this.toggleShare, this);
this.fb = new Firebase('https://luminous-fire-5575.firebaseio.com/users');
this.symbol = new esri.symbol.SimpleMarkerSymbol().setColor(new dojo.Color([0, 255, 0, 0.25]));
this.map = new esri.Map('map', {basemap: 'osm', center: [-98.737039, 38.737039], zoom: 4 });
$('.current-location').on('click',function() { $this.getLocation($this.model) });
$('#search-input').on('typeahead:selected', function (evt, datum, name) {
$this.map.centerAndZoom(new esri.geometry.Point(datum.lon, datum.lat), 15);
$('#search-modal').modal('hide');
});
$('#dev-summit').on('click',function() { $this.map.centerAndZoom([-116.5382, 33.8260], 16)});
this.fb.on('value', function (ss) {
$this.messages = [];
_.each(ss.val(), function (item) { _.each(item.messages, function (item2) {
$this.messages.push(item2) });
});
$this.displayChatMessages() & $this.activateClickListener() & $this.initTypeahead();
});
},
events: {
'keyup #message-input': 'toggleShare', 'keyup #name-input': 'toggleShare',
'click .share-message': 'saveMsg', 'click #add-event-btn': 'enableEventClickHandler'
},
toggleShare: function (model) {
$('#loader').modal('hide');
if ($('#name-input').val() && $('#message-input').val() && (this.model.get('loc'))) {
$('.share-message').removeClass('disabled');
} else { $('.share-message').addClass('disabled') };
},
saveMsg: function (evt) {
var loc = this.model.get('loc');
var exists; var tC = new Date().getTime();
var name = $('#name-input').val(); var text = $('#message-input').val();
if (!name || !text) { $('#alert-modal').modal(); return; }
if (!loc || !loc.lat || !loc.lon) { $('#no-location-modal').modal(); return; };
this.fb.on('value', function (ss) { exists = (ss.val() !== null) });
if(!exists){ this.fb.child(name).set({text: name}) };
this.fb.child(name).child('messages').push({ name: name, text: text,
lat: loc.lat, lon: loc.lon, timeStamp: tC });
$('#share-modal').modal('hide'); $('#message-input').val(''); this.model.set('loc', null);
},getLocation: function (model) {
if (navigator.geolocation) {
$('#loader').modal('show');
navigator.geolocation.getCurrentPosition(function (p) {
model.set('loc', {lat: String(p.coords.latitude), lon: String(p.coords.longitude)});
});
} else { $('#alert-modal').modal(); }
},enableEventClickHandler: function() {
if (this.mch){ dojo.disconnect(this.mch) };
this.mch = dojo.connect(this.map, 'onClick', dojo.hitch(this, this.onMapClick));
$('#share-modal').modal('hide');
},onMapClick: function (evt) {
var x = esri.geometry.xyToLngLat(evt.mapPoint.x, evt.mapPoint.y, true);
this.model.set('loc', { lat: x[1], lon: x[0] });
dojo.disconnect(this.mch) & $('#share-modal').modal('show');
},activateClickListener: function() {
var $this = this;
$('.chat-item').on('click', function(evt) {
var d = evt.currentTarget.dataset;
$this.map.centerAndZoom(new esri.geometry.Point(d.lon, d.lat), 15);
$('#chat-modal').modal('hide');
});
},displayChatMessages: function() {
var $this = this; $('#chat-container').empty();
this.messages.sort(function (a, b) { if (a.timeStamp > b.timeStamp) { return 1; }
if (a.timeStamp < b.timeStamp) { return -1; } return 0;
});
_.each(this.messages, function (msg) {
var tC = new Date().getTime();
tE = Math.floor((tC - msg.timeStamp) / 1000 / 60); //get time elapsed since the previous messages in firebase
tS = (tE > 60) ? Math.floor((tE * 60) / 3600) + ' hours ago' : tE + ' minutes ago';
$('<li class="list-group-item chat-item"></li>').append('<div class="chat-date">' +
msg.name +': '+ tS + '</div><div>'+ msg.text + '</div>')
.attr('data-lat', msg.lat).attr('data-lon',msg.lon).prependTo($('#chat-container'));
if (msg.lat && msg.lon && $this.map.graphics) {
var pt = new esri.geometry.Point(msg.lon, msg.lat);
var g = new esri.Graphic(pt, $this.symbol);
$this.map.graphics.add(g);
};
g.setInfoTemplate(new esri.InfoTemplate().setTitle(msg.name +' '+ tS).setContent(msg.text));
});
},initTypeahead: function () {
$('#search-input').typeahead('destroy');
var bloodhound = new Bloodhound({
datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.text); },
queryTokenizer: Bloodhound.tokenizers.whitespace, local: this.messages });
bloodhound.initialize();
var options = { displayKey: 'text', source: bloodhound.ttAdapter(),
templates: { suggestion: _.template('<strong><%=text%></strong>')}};
$('#search-input').typeahead(null, options);
}});
new AppView();});
Backbone.js (jQuery, underscore.js)
Bootstrap
ESRI JavaScript API (dojo)
Firebase
typeahead.js
GitHub
Sublime Text
Bower
Python SimpleHTTPServer