aboutsummaryrefslogtreecommitdiffstats
path: root/src/js/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/views')
-rw-r--r--src/js/views/around.js409
-rw-r--r--src/js/views/details.js149
-rw-r--r--src/js/views/details_extra.js108
-rw-r--r--src/js/views/existing.js46
-rw-r--r--src/js/views/fms.js103
-rw-r--r--src/js/views/home.js40
-rw-r--r--src/js/views/locator.js64
-rw-r--r--src/js/views/login.js108
-rw-r--r--src/js/views/offline.js140
-rw-r--r--src/js/views/photo.js87
-rw-r--r--src/js/views/reports.js61
-rw-r--r--src/js/views/save_offline.js31
-rw-r--r--src/js/views/search.js81
-rw-r--r--src/js/views/sent.js36
-rw-r--r--src/js/views/submit.js405
15 files changed, 1868 insertions, 0 deletions
diff --git a/src/js/views/around.js b/src/js/views/around.js
new file mode 100644
index 0000000..c2047d8
--- /dev/null
+++ b/src/js/views/around.js
@@ -0,0 +1,409 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ AroundView: FMS.LocatorView.extend({
+ template: 'around',
+ id: 'around-page',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick #locate_cancel': 'goSearch',
+ 'vclick #login-options': 'goLogin',
+ 'vclick #view-my-reports': 'goReports',
+ 'vclick #search': 'goSearch',
+ 'vclick #relocate': 'centerMapOnPosition',
+ 'vclick #cancel': 'onClickCancel',
+ 'vclick #confirm': 'onClickReport',
+ 'vclick #confirm-map': 'onClickReport',
+ 'vclick #mark-here': 'onClickMark',
+ 'vclick #reposition': 'onClickReposition',
+ 'vclick a.address': 'goAddress',
+ 'submit #postcodeForm': 'search'
+ },
+
+ render: function(){
+ if ( !this.template ) {
+ console.log('no template to render');
+ return;
+ }
+ template = _.template( tpl.get( this.template ) );
+ if ( this.model ) {
+ this.$el.html(template({ model: this.model.toJSON(), user: FMS.currentUser.toJSON() }));
+ } else {
+ this.$el.html(template());
+ }
+ this.afterRender();
+ return this;
+ },
+
+ beforeDisplay: function() {
+ $('a[data-role="button"]').hide();
+ $('#login-options').hide();
+ $('#postcodeForm').hide();
+ $('#cancel').hide();
+ this.fixPageHeight();
+ },
+
+ afterDisplay: function() {
+ if ( FMS.isOffline ) {
+ this.navigate( 'offline' );
+ } else if ( this.model && this.model.get('lat') ) {
+ var modelInfo = { coordinates: { latitude: this.model.get('lat'), longitude: this.model.get('lon') } };
+ this.setMapPosition(modelInfo);
+ this.displayButtons(true);
+ this.setReportPosition({ lat: this.model.get('lat'), lon: this.model.get('lon') }, true);
+ this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
+ FMS.locator.trackPosition();
+ } else if ( FMS.currentPosition ) {
+ var info = { coordinates: FMS.currentPosition };
+ FMS.currentPosition = null;
+ if ( !fixmystreet.map ) {
+ this.setMapPosition(info);
+ }
+ this.displayButtons(false);
+ this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
+ FMS.locator.trackPosition();
+ } else {
+ this.locate();
+ this.displayButtons(false);
+ }
+ },
+
+ setMapPosition: function( info ) {
+ var coords = info.coordinates;
+ fixmystreet.latitude = coords.latitude;
+ fixmystreet.longitude = coords.longitude;
+
+ if ( !fixmystreet.map ) {
+ show_map();
+ } else {
+ FMS.currentPosition = coords;
+ var centre = this.projectCoords( coords );
+ fixmystreet.map.panTo(centre);
+ }
+ },
+
+ gotLocation: function( info ) {
+ $('#relocate').show();
+ this.finishedLocating();
+
+ this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
+
+ this.located = true;
+ this.locateCount = 21;
+
+ this.setMapPosition( info );
+
+ FMS.locator.trackPosition();
+ // FIXME: not sure why I need to do this
+ fixmystreet.select_feature.deactivate();
+ fixmystreet.select_feature.activate();
+ fixmystreet.nav.activate();
+ },
+
+ positionUpdate: function( info ) {
+ $('#relocate').show();
+ FMS.currentPosition = info.coordinates;
+ var centre = this.projectCoords( info.coordinates );
+
+ var point = new OpenLayers.Geometry.Point( centre.lon, centre.lat );
+
+ fixmystreet.location.removeAllFeatures();
+ var x = new OpenLayers.Feature.Vector(
+ point,
+ {},
+ {
+ graphicZIndex: 3000,
+ graphicName: 'circle',
+ 'externalGraphic': 'images/gps-marker.svg',
+ pointRadius: 16
+ }
+ );
+ fixmystreet.location.addFeatures([ x ]);
+ },
+
+ centerMapOnPosition: function(e) {
+ e.preventDefault();
+ if ( !fixmystreet.map ) {
+ return;
+ }
+
+ // if there isn't a currentPosition then something
+ // is up so we probably should not recenter
+ if ( FMS.currentPosition ) {
+ fixmystreet.map.panTo(this.projectCoords( FMS.currentPosition ));
+ }
+ },
+
+ failedLocation: function( details ) {
+ this.finishedLocating();
+ this.locateCount = 21;
+ var msg = '';
+ if ( details.msg ) {
+ msg = details.msg;
+ } else {
+ msg = FMS.strings.location_problem;
+ }
+ if ( !fixmystreet.map ) {
+ $('#relocate').hide();
+ $('#mark-here').hide();
+ }
+ $('#front-howto').html('<p>' + msg + '</msg>');
+ $('#front-howto').show();
+ },
+
+ displayButtons: function(isLocationSet) {
+ if ( fixmystreet.map ) {
+ fixmystreet.nav.activate();
+ fixmystreet.actionafterdrag.activate();
+ }
+ if (isLocationSet) {
+ $('#cancel').addClass('ui-btn-left').show();
+ $('#confirm').addClass('ui-btn-right ui-btn-icon-right').show();
+ $('#confirm-map').show();
+ $('#view-my-reports').hide();
+ $('#login-options').hide();
+ $('#mark-here').hide();
+ $('#postcodeForm').hide();
+ if ( fixmystreet.map ) {
+ fixmystreet.markers.setVisibility(false);
+ fixmystreet.select_feature.deactivate();
+ fixmystreet.bbox_strategy.deactivate();
+ }
+ } else {
+ $('#cancel').hide().removeClass('ui-btn-left');
+ $('#confirm').hide().removeClass('ui-btn-right ui-btn-icon-right');
+ $('#confirm-map').hide();
+ $('#view-my-reports').show();
+ $('#login-options').show();
+ $('#mark-here').show();
+ $('#postcodeForm').show();
+ $('#reposition').hide();
+ if ( fixmystreet.map ) {
+ fixmystreet.bbox_strategy.activate();
+ fixmystreet.report_location.setVisibility(false);
+ fixmystreet.markers.setVisibility(true);
+ fixmystreet.select_feature.deactivate();
+ fixmystreet.select_feature.activate();
+ }
+ }
+ },
+
+ setReportPosition: function(lonlat, convertPosition) {
+ var markers = fms_markers_list( [ [ lonlat.lat, lonlat.lon, 'green', 'location', '', 'location' ] ], convertPosition );
+ fixmystreet.report_location.removeAllFeatures();
+ fixmystreet.report_location.addFeatures( markers );
+ fixmystreet.report_location.setVisibility(true);
+ },
+
+ onClickMark: function(e) {
+ e.preventDefault();
+ this.displayButtons(true);
+ $('#reposition').hide();
+
+ var lonlat = this.getCrossHairPosition();
+ this.setReportPosition(lonlat, true);
+ },
+
+ onClickCancel: function(e) {
+ e.preventDefault();
+ fixmystreet.markers.removeAllFeatures();
+ fixmystreet_activate_drag();
+ this.displayButtons(false);
+ if ( this.model.isPartial() ) {
+ FMS.clearCurrentDraft();
+ } else {
+ // it's not partial but we've created a draft anyway so
+ // delete it
+ if ( this.model.id ) {
+ var del = FMS.removeDraft( this.model.id, true );
+ var that = this;
+ del.done( function() { that.decrementDraftCount(); } );
+ }
+ this.model.set('lat', null);
+ this.model.set('lon', null);
+ }
+ },
+
+ decrementDraftCount: function() {
+ var counter = $('#view-my-reports .draft_count');
+ var count = counter.text();
+ count--;
+ counter.text(count);
+ },
+
+ onClickReposition: function(e) {
+ e.preventDefault();
+ var lonlat = this.getCrossHairPosition();
+ lonlat.transform(
+ new OpenLayers.Projection("EPSG:4326"),
+ fixmystreet.map.getProjectionObject()
+ );
+ fixmystreet.report_location.features[0].move(lonlat);
+ $('#reposition').hide();
+ },
+
+ onClickReport: function(e) {
+ e.preventDefault();
+ var position = this.getMarkerPosition();
+
+ if ( FMS.isOffline ) {
+ this.stopListening(FMS.locator);
+ FMS.locator.stopTracking();
+ // these may be out of the area but lets just save them
+ // for now and they can be checked when we are online.
+ this.model.set('lat', position.lat );
+ this.model.set('lon', position.lon );
+ FMS.saveCurrentDraft();
+ this.navigate( 'offline' );
+ } else {
+ this.listenTo(FMS.locator, 'gps_located', this.goPhoto);
+ this.listenTo(FMS.locator, 'gps_failed', this.noMap );
+ FMS.locator.check_location( { latitude: position.lat, longitude: position.lon } );
+ }
+ },
+
+ search: function(e) {
+ $('#pc').blur();
+ // this is to stop form submission
+ e.preventDefault();
+ $('#front-howto').hide();
+ this.clearValidationErrors();
+ var pc = this.$('#pc').val();
+ this.listenTo(FMS.locator, 'search_located', this.searchSuccess );
+ this.listenTo(FMS.locator, 'search_failed', this.searchFail);
+
+ FMS.locator.lookup(pc);
+ },
+
+ searchSuccess: function( info ) {
+ this.stopListening(FMS.locator);
+ var coords = info.coordinates;
+ if ( fixmystreet.map ) {
+ fixmystreet.map.panTo(this.projectCoords( coords ));
+ } else {
+ this.setMapPosition(info);
+ this.displayButtons(false);
+ }
+ },
+
+ goAddress: function(e) {
+ $('#relocate').show();
+ $('#front-howto').html('').hide();
+ var t = $(e.target);
+ var lat = t.attr('data-lat');
+ var long = t.attr('data-long');
+
+ var coords = { latitude: lat, longitude: long };
+ if ( fixmystreet.map ) {
+ fixmystreet.map.panTo(this.projectCoords( coords ));
+ } else {
+ this.setMapPosition({ coordinates: coords });
+ }
+ },
+
+ searchFail: function( details ) {
+ // this makes sure any onscreen keyboard is dismissed
+ $('#submit').focus();
+ this.stopListening(FMS.locator);
+ if ( details.msg ) {
+ this.validationError( 'pc', details.msg );
+ } else if ( details.locations ) {
+ var multiple = '';
+ for ( var i = 0; i < details.locations.length; i++ ) {
+ var loc = details.locations[i];
+ var li = '<li><a class="address" id="location_' + i + '" data-lat="' + loc.lat + '" data-long="' + loc.long + '">' + loc.address + '</a></li>';
+ multiple = multiple + li;
+ }
+ $('#front-howto').html('<p>Multiple matches found</p><ul data-role="listview" data-inset="true">' + multiple + '</ul>');
+ $('.ui-page').trigger('create');
+ $('#relocate').hide();
+ $('#front-howto').show();
+ } else {
+ this.validationError( 'pc', FMS.strings.location_problem );
+ }
+ },
+
+ pauseMap: function() {
+ this.stopListening(FMS.locator);
+ FMS.locator.stopTracking();
+ if ( fixmystreet.map ) {
+ fixmystreet.nav.deactivate();
+ fixmystreet.actionafterdrag.deactivate();
+ }
+ },
+
+ goPhoto: function(info) {
+ this.pauseMap();
+ this.model.set('lat', info.coordinates.latitude );
+ this.model.set('lon', info.coordinates.longitude );
+ this.model.set('categories', info.details.category );
+ if ( info.details.title_list ) {
+ this.model.set('title_list', info.details.title_list);
+ }
+ FMS.saveCurrentDraft();
+
+ this.navigate( 'photo' );
+ },
+
+ goSearch: function(e) {
+ e.preventDefault();
+ if ( !fixmystreet.map ) {
+ this.$('#mark-here').hide();
+ this.$('#relocate').hide();
+ $('#front-howto').html('<p>' + FMS.strings.locate_dismissed + '</msg>');
+ $('#front-howto').show();
+ }
+ this.finishedLocating();
+ },
+
+ goLogin: function(e) {
+ e.preventDefault();
+ this.pauseMap();
+ this.navigate( 'login' );
+ },
+
+ goReports: function(e) {
+ e.preventDefault();
+ this.pauseMap();
+ this.navigate( 'reports' );
+ },
+
+ getCrossHairPosition: function() {
+ var cross = fixmystreet.map.getControlsByClass(
+ "OpenLayers.Control.Crosshairs");
+
+ var position = cross[0].getMapPosition();
+ position.transform(
+ fixmystreet.map.getProjectionObject(),
+ new OpenLayers.Projection("EPSG:4326")
+ );
+
+ return position;
+ },
+
+ getMarkerPosition: function() {
+ var marker = fixmystreet.report_location.features[0].geometry;
+
+ var position = new OpenLayers.LonLat( marker.x, marker.y );
+ position.transform(
+ fixmystreet.map.getProjectionObject(),
+ new OpenLayers.Projection("EPSG:4326")
+ );
+
+ return position;
+ },
+
+ projectCoords: function( coords ) {
+ var centre = new OpenLayers.LonLat( coords.longitude, coords.latitude );
+ centre.transform(
+ new OpenLayers.Projection("EPSG:4326"),
+ fixmystreet.map.getProjectionObject()
+ );
+
+ return centre;
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/details.js b/src/js/views/details.js
new file mode 100644
index 0000000..90d1f76
--- /dev/null
+++ b/src/js/views/details.js
@@ -0,0 +1,149 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ DetailsView: FMS.FMSView.extend({
+ template: 'details',
+ id: 'details-page',
+ prev: 'photo',
+ next: 'submit-start',
+ bottomMargin: -20,
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext',
+ 'blur textarea': 'updateCurrentReport',
+ 'change select': 'updateSelect',
+ 'blur input': 'updateCurrentReport'
+ },
+
+ afterRender: function() {
+ this.$('#form_category').attr('data-role', 'none');
+
+ if ( this.model.get('category') ) {
+ this.$('#form_category').val( this.model.get('category') );
+ }
+ this.setSelectClass();
+
+ },
+
+ beforeDisplay: function() {
+ this.fixPageHeight();
+ var header = this.$("div[data-role='header']:visible"),
+ detail = this.$('#form_detail'),
+ top = detail.position().top,
+ viewHeight = $(window).height(),
+ contentHeight = viewHeight - header.outerHeight() + 15;
+
+ detail.height( contentHeight - top );
+ },
+
+ onClickButtonPrev: function(e) {
+ e.preventDefault();
+ this.updateCurrentReport();
+ this.navigate( this.prev, true );
+ },
+
+ onClickButtonNext: function(e) {
+ e.preventDefault();
+ this.clearValidationErrors();
+ var valid = 1;
+
+ if ( !$('#form_title').val() ) {
+ valid = 0;
+ this.validationError( 'form_title', FMS.validationStrings.title );
+ }
+
+ if ( !$('#form_detail').val() ) {
+ valid = 0;
+ this.validationError( 'form_detail', FMS.validationStrings.detail );
+ }
+
+ var cat = $('#form_category').val();
+ if ( cat == '-- Pick a category --' ) {
+ valid = 0;
+ this.validationError( 'form_category', FMS.validationStrings.category );
+ }
+
+ if ( valid ) {
+ this.clearValidationErrors();
+ this.updateCurrentReport();
+ if ( FMS.isOffline ) {
+ this.navigate( 'save_offline' );
+ } else {
+ var that = this;
+ $.ajax( {
+ url: CONFIG.FMS_URL + '/report/new/category_extras',
+ type: 'POST',
+ data: {
+ category: this.model.get('category'),
+ latitude: this.model.get('lat'),
+ longitude: this.model.get('lon')
+ },
+ dataType: 'json',
+ timeout: 30000,
+ success: function( data, status ) {
+ if ( data && data.category_extra && data.category_extra.length > 0 ) {
+ that.model.set('category_extras', data.category_extra);
+ that.navigate('details_extra');
+ } else {
+ that.navigate( that.next );
+ }
+ },
+ error: function() {
+ this.navigate( that.next );
+ }
+ } );
+ }
+ }
+ },
+
+ validationError: function(id, error) {
+ var el_id = '#' + id;
+ var el = $(el_id);
+
+ el.addClass('error');
+ if ( el.val() === '' ) {
+ el.attr('orig-placeholder', el.attr('placeholder'));
+ el.attr('placeholder', error);
+ }
+ },
+
+ clearValidationErrors: function() {
+ $('.error').removeClass('error');
+ $('.error').each(function(el) { if ( el.attr('orig-placeholder') ) { el.attr('placeholder', el.attr('orig-placeholder') ); } } );
+ },
+
+ setSelectClass: function() {
+ var cat = this.$('#form_category');
+ if ( cat.val() !== "" && cat.val() !== '-- Pick a category --' ) {
+ cat.removeClass('noselection');
+ } else {
+ cat.addClass('noselection');
+ }
+ },
+
+ updateSelect: function() {
+ this.updateCurrentReport();
+ this.setSelectClass();
+ },
+
+ updateCurrentReport: function() {
+ var category = $('#form_category').val();
+ if ( category === '-- Pick a category --' ) {
+ category = '';
+ }
+ if ( category && $('#form_title').val() && $('#form_detail').val() ) {
+ $('#next').addClass('page_complete_btn');
+ } else {
+ $('#next').removeClass('page_complete_btn');
+ }
+ this.model.set('category', category);
+ this.model.set('title', $('#form_title').val());
+ this.model.set('details', $('#form_detail').val());
+ FMS.saveCurrentDraft();
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/details_extra.js b/src/js/views/details_extra.js
new file mode 100644
index 0000000..7a96e8c
--- /dev/null
+++ b/src/js/views/details_extra.js
@@ -0,0 +1,108 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ DetailsExtraView: FMS.FMSView.extend({
+ template: 'details_extra',
+ id: 'details-extra-page',
+ prev: 'details',
+ next: 'submit-start',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext',
+ 'blur textarea': 'updateCurrentReport',
+ 'change select': 'updateCurrentReport',
+ 'blur input': 'updateCurrentReport'
+ },
+
+ afterRender: function() {
+ console.log(this.model);
+ this.populateFields();
+ },
+
+ onClickButtonPrev: function() {
+ this.updateCurrentReport();
+ this.navigate( this.prev, true );
+ },
+
+ onClickButtonNext: function() {
+ this.clearValidationErrors();
+ var valid = 1;
+ var that = this;
+
+ var isRequired = function(index) {
+ var el = $(this);
+ if ( el.attr('required') && el.val() === '' ) {
+ valid = 0;
+ that.validationError(el.attr('id'), FMS.strings.required);
+ }
+ };
+ // do validation
+ $('input').each(isRequired);
+ $('textarea').each(isRequired);
+ $('select').each(isRequired);
+
+ if ( valid ) {
+ this.clearValidationErrors();
+ this.updateCurrentReport();
+ this.navigate( this.next );
+ }
+ },
+
+ validationError: function(id, error) {
+ var el_id = '#' + id;
+ var el = $(el_id);
+
+ el.addClass('error');
+ if ( el.val() === '' ) {
+ el.attr('orig-placeholder', el.attr('placeholder'));
+ el.attr('placeholder', error);
+ }
+ },
+
+ clearValidationErrors: function() {
+ $('.error').removeClass('error');
+ $('.error').each(function(el) { if ( el.attr('orig-placeholder') ) { el.attr('placeholder', el.attr('orig-placeholder') ); } } );
+ },
+
+ updateSelect: function() {
+ this.updateCurrentReport();
+ },
+
+ updateCurrentReport: function() {
+ var fields = [];
+ var that = this;
+ var update = function(index) {
+ var el = $(this);
+ if ( el.val() !== '' ) {
+ that.model.set(el.attr('name'), el.val());
+ fields.push(el.attr('name'));
+ } else {
+ that.model.set(el.attr('name'), '');
+ }
+
+ };
+
+ $('input').each(update);
+ $('select').each(update);
+ $('textarea').each(update);
+
+ this.model.set('extra_details', fields);
+ FMS.saveCurrentDraft();
+ },
+
+ populateFields: function() {
+ var that = this;
+ var populate = function(index) {
+ console.log(that.$(this).attr('name'));
+ that.$(this).val(that.model.get(that.$(this).attr('name')));
+ };
+ this.$('input').each(populate);
+ this.$('select').each(populate);
+ this.$('textarea').each(populate);
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/existing.js b/src/js/views/existing.js
new file mode 100644
index 0000000..0d16760
--- /dev/null
+++ b/src/js/views/existing.js
@@ -0,0 +1,46 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ ExistingView: FMS.FMSView.extend({
+ template: 'existing',
+ id: 'existing',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick #use_report': 'useReport',
+ 'vclick #save_report': 'saveReport',
+ 'vclick #discard': 'discardReport'
+ },
+
+ setHeight: function(content, height) {
+ content.css( 'min-height', content + 'px');
+ },
+
+ useReport: function(e) {
+ e.preventDefault();
+ FMS.setCurrentDraft(this.model);
+ this.navigate('around');
+ },
+
+ saveReport: function(e) {
+ e.preventDefault();
+ FMS.clearCurrentDraft();
+ this.navigate('around');
+ },
+
+ discardReport: function(e) {
+ e.preventDefault();
+ var reset = FMS.removeDraft(this.model.id, true);
+ var that = this;
+ reset.done( function() { that.onDraftRemove(); } );
+ reset.fail( function() { that.onDraftRemove(); } );
+ },
+
+ onDraftRemove: function() {
+ FMS.clearCurrentDraft();
+ this.navigate( 'around', 'left' );
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/fms.js b/src/js/views/fms.js
new file mode 100644
index 0000000..53cdc66
--- /dev/null
+++ b/src/js/views/fms.js
@@ -0,0 +1,103 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ FMSView: Backbone.View.extend({
+ tag: 'div',
+ bottomMargin: 20,
+ contentSelector: '[data-role="content"]',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext'
+ },
+
+ render: function(){
+ if ( !this.template ) {
+ console.log('no template to render');
+ return;
+ }
+ template = _.template( tpl.get( this.template ) );
+ var args = null;
+ if ( this.options.msg ) {
+ args = { msg: this.options.msg };
+ }
+ if ( this.model ) {
+ if ( args ) {
+ args.model = this.model.toJSON();
+ } else {
+ args = this.model.toJSON();
+ }
+ }
+ this.$el.html(template(args));
+ this.afterRender();
+ return this;
+ },
+
+ fixPageHeight: function() {
+ var header = this.$("div[data-role='header']:visible"),
+ content = this.$(this.contentSelector),
+ top = content.position().top,
+ viewHeight = $(window).height(),
+ contentHeight = viewHeight - header.outerHeight() - this.bottomMargin;
+
+ this.setHeight( content, contentHeight - top );
+ },
+
+ setHeight: function(content, height) {
+ content.height(height);
+ },
+
+ afterRender: function() {},
+
+ beforeDisplay: function() {
+ this.fixPageHeight();
+ },
+
+ afterDisplay: function() {},
+
+ navigate: function( route, reverse ) {
+ if ( reverse ) {
+ FMS.router.reverseTransition();
+ }
+
+ FMS.router.navigate( route, { trigger: true } );
+ },
+
+ onClickButtonPrev: function(e) {
+ e.preventDefault();
+ this.navigate( this.prev, true );
+ },
+
+ onClickButtonNext: function(e) {
+ e.preventDefault();
+ this.navigate( this.next );
+ },
+
+ displayError: function(msg) {
+ alert(msg);
+ },
+
+ validationError: function( id, error ) {
+ var el_id = '#' + id;
+ var el = $(el_id);
+ var err = '<div for="' + id + '" class="form-error">' + error + '</div>';
+ if ( $('div[for='+id+']').length === 0 ) {
+ el.before(err);
+ el.addClass('form-error');
+ }
+ },
+
+ clearValidationErrors: function() {
+ $('div.form-error').remove();
+ $('.form-error').removeClass('form-error');
+ },
+
+ destroy: function() { console.log('destory for ' + this.id); this._destroy(); this.remove(); },
+
+ _destroy: function() {}
+ })
+ });
+ _.extend( FMS.FMSView, Backbone.Events );
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/home.js b/src/js/views/home.js
new file mode 100644
index 0000000..998af1c
--- /dev/null
+++ b/src/js/views/home.js
@@ -0,0 +1,40 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ HomeView: FMS.FMSView.extend({
+ template: 'home',
+ id: 'front-page',
+
+ afterRender: function() {
+ /*
+ if ( !can_geolocate && ( !navigator.network || !navigator.network.connection ) ) {
+ geocheck_count++;
+ window.setTimeout( decide_front_page, 1000 );
+ return;
+ }
+
+ // sometime onDeviceReady does not fire so set this here to be sure
+ can_geolocate = true;
+
+ geocheck_count = 0;
+ */
+
+ $('#locating').show();
+
+ },
+
+ afterDisplay: function() {
+ $('#load-screen').hide();
+ if ( FMS.isOffline ) {
+ this.navigate( 'offline' );
+ } else if ( FMS.currentDraft && (
+ FMS.currentDraft.get('title') || FMS.currentDraft.get('lat') ||
+ FMS.currentDraft.get('details') || FMS.currentDraft.get('file') )
+ ) {
+ this.navigate( 'existing' );
+ } else {
+ this.navigate( 'around' );
+ }
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/locator.js b/src/js/views/locator.js
new file mode 100644
index 0000000..46ebcce
--- /dev/null
+++ b/src/js/views/locator.js
@@ -0,0 +1,64 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ LocatorView: FMS.FMSView.extend({
+ skipLocationCheck: false,
+
+ locate: function() {
+ $('#locating').show();
+ this.listenTo(FMS.locator, 'gps_located', this.gotLocation);
+ this.listenTo(FMS.locator, 'gps_failed', this.failedLocation);
+ this.listenTo(FMS.locator, 'gps_locating', this.locationUpdate);
+
+ FMS.locator.geolocate(CONFIG.ACCURACY, this.skipLocationCheck);
+ this.startLocateProgress();
+ },
+
+ startLocateProgress: function() {
+ this.located = false;
+ this.locateCount = 1;
+ var that = this;
+ window.setTimeout( function() {that.showLocateProgress();}, 1000);
+ },
+
+ locationUpdate: function( accuracy ) {
+ if ( accuracy && accuracy < 500 ) {
+ $('#progress-bar').css( 'background-color', 'orange' );
+ } else if ( accuracy && accuracy < 250 ) {
+ $('#progress-bar').css( 'background-color', 'yellow' );
+ } else {
+ $('#progress-bar').css( 'background-color', 'grey' );
+ }
+
+ $('#accuracy').text(parseInt(accuracy, 10) + 'm');
+ },
+
+ showLocateProgress: function() {
+ if ( !this.located && this.locateCount > 20 ) {
+ FMS.searchMessage = FMS.strings.geolocation_failed;
+ $('#locating').hide();
+ return;
+ }
+ var percent = ( ( 20 - this.locateCount ) / 20 ) * 100;
+ $('#progress-bar').css( 'width', percent + '%' );
+ this.locateCount++;
+ var that = this;
+ window.setTimeout( function() {that.showLocateProgress();}, 1000);
+ },
+
+ finishedLocating: function() {
+ this.stopListening(FMS.locator, 'gps_locating');
+ this.stopListening(FMS.locator, 'gps_located');
+ this.stopListening(FMS.locator, 'gps_failed');
+ $('#locating').hide();
+ },
+
+ failedLocation: function(details) {
+ this.finishedLocating();
+ },
+
+ gotLocation: function(info) {
+ this.finishedLocating();
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/login.js b/src/js/views/login.js
new file mode 100644
index 0000000..a24daa9
--- /dev/null
+++ b/src/js/views/login.js
@@ -0,0 +1,108 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ LoginView: FMS.FMSView.extend({
+ template: 'login',
+ id: 'login',
+ next: 'home',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick #login': 'onClickLogin',
+ 'submit #signinForm': 'onClickLogin',
+ 'vclick #logout': 'onClickLogout',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext'
+ },
+
+ onClickLogin: function(e) {
+ // prevent form submission from onscreen keyboard
+ e.preventDefault();
+ if ( this.validate() ) {
+ var that = this;
+ $.ajax( {
+ url: CONFIG.FMS_URL + '/auth/ajax/sign_in',
+ type: 'POST',
+ data: {
+ email: $('#form_email').val(),
+ password_sign_in: $('#form_password').val(),
+ remember_me: 1
+ },
+ dataType: 'json',
+ timeout: 30000,
+ success: function( data, status ) {
+ if ( data.name ) {
+ that.model.set('password', $('#form_password').val());
+ that.model.set('email', $('#form_email').val());
+ that.model.set('name', data.name);
+ that.model.save();
+ FMS.isLoggedIn = 1;
+ that.$('#password_row').hide();
+ that.$('#success_row').show();
+ $('#logout').focus();
+ } else {
+ $('#login').focus();
+ that.validationError('form_email', FMS.strings.login_details_error);
+ }
+ },
+ error: function() {
+ that.validationError('form_email', FMS.strings.login_error);
+ }
+ } );
+ }
+ },
+
+ onClickLogout: function(e) {
+ e.preventDefault();
+ var that = this;
+ $.ajax( {
+ url: CONFIG.FMS_URL + '/auth/ajax/sign_out',
+ type: 'GET',
+ dataType: 'json',
+ timeout: 30000,
+ success: function( data, status ) {
+ FMS.isLoggedIn = 0;
+ that.model.set('password', '');
+ that.model.save();
+ that.$('#form_email').val('');
+ that.$('#form_password').val('');
+ that.$('#success_row').hide();
+ that.$('#signed_in_row').hide();
+ that.$('#password_row').show();
+ },
+ error: function() {
+ that.validationError('err', FMS.strings.logout_error);
+ }
+ } );
+ },
+
+ validate: function() {
+ this.clearValidationErrors();
+ var isValid = 1;
+
+ if ( !$('#form_password').val() ) {
+ isValid = 0;
+ this.validationError('form_password', FMS.validationStrings.password );
+ }
+
+ var email = $('#form_email').val();
+ if ( !email ) {
+ isValid = 0;
+ this.validationError('form_email', FMS.validationStrings.email.required);
+ // regexp stolen from jquery validate module
+ } else if ( ! /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(email) ) {
+ isValid = 0;
+ this.validationError('form_email', FMS.validationStrings.email.email);
+ }
+
+ if ( !isValid ) {
+ // this makes sure the onscreen keyboard is dismissed
+ $('#login').focus();
+ }
+
+ return isValid;
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/offline.js b/src/js/views/offline.js
new file mode 100644
index 0000000..117c67e
--- /dev/null
+++ b/src/js/views/offline.js
@@ -0,0 +1,140 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ OfflineView: FMS.LocatorView.extend({
+ template: 'offline',
+ id: 'offline',
+ prev: 'home',
+ next: 'reports',
+ skipLocationCheck: true,
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'toggleNextButton',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext',
+ 'vclick #id_photo_button': 'takePhoto',
+ 'vclick #id_existing': 'addPhoto',
+ 'vclick #id_del_photo_button': 'deletePhoto',
+ 'vclick #locate': 'locate',
+ 'vclick #locate_cancel': 'onClickCancel',
+ 'blur input': 'toggleNextButton',
+ 'blur textarea': 'toggleNextButton'
+ },
+
+ draftHasContent: function() {
+ var hasContent = false;
+
+ if ( $('#form_title').val() || $('#form_detail').val() ||
+ this.model.get('lat') || this.model.get('file') ) {
+ hasContent = true;
+ }
+
+ return hasContent;
+ },
+
+ toggleNextButton: function() {
+ if ( this.draftHasContent() ) {
+ $('#offline-next-btn .ui-btn-text').text('Save');
+ } else {
+ $('#offline-next-btn .ui-btn-text').text('Skip');
+ }
+ },
+
+ failedLocation: function(details) {
+ this.finishedLocating();
+ this.locateCount = 21;
+
+ $('#locate_result').html('Could not get position');
+ },
+
+ gotLocation: function(info) {
+ this.finishedLocating();
+
+ this.model.set('lat', info.coordinates.latitude);
+ this.model.set('lon', info.coordinates.longitude);
+
+ $('#locate_result').html('Got position (' + info.coordinates.latitude.toFixed(2) + ', ' + info.coordinates.longitude.toFixed(2) + ')');
+ },
+
+ takePhoto: function() {
+ var that = this;
+ navigator.camera.getPicture( function(imgURI) { that.addPhotoSuccess(imgURI); }, function(error) { that.addPhotoFail(error); }, { saveToPhotoAlbum: true, quality: 49, destinationType: Camera.DestinationType.FILE_URI, sourceType: navigator.camera.PictureSourceType.CAMERA, correctOrientation: true });
+ },
+
+ addPhoto: function() {
+ var that = this;
+ navigator.camera.getPicture( function(imgURI) { that.addPhotoSuccess(imgURI); }, function(error) { that.addPhotoFail(error); }, { saveToPhotoAlbum: false, quality: 49, destinationType: Camera.DestinationType.FILE_URI, sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY, correctOrientation: true });
+ },
+
+ addPhotoSuccess: function(imgURI) {
+ var move = FMS.files.moveURI( imgURI );
+
+ var that = this;
+ move.done( function( file ) {
+ $('#photo').attr('src', file.toURL());
+ that.model.set('file', file.toURL());
+ FMS.saveCurrentDraft();
+
+ $('#photo-next-btn .ui-btn-text').text('Next');
+ $('#display_photo').show();
+ $('#add_photo').hide();
+ });
+
+ move.fail( function() { that.addPhotoFail(); } );
+ },
+
+ addPhotoFail: function() {
+ if ( message != 'no image selected' &&
+ message != 'Selection cancelled.' &&
+ message != 'Camera cancelled.' ) {
+ this.displayError(FMS.strings.photo_failed);
+ }
+ },
+
+ deletePhoto: function() {
+ var that = this;
+ var del = FMS.files.deleteURI( this.model.get('file') );
+
+ del.done( function() {
+ that.model.set('file', '');
+ FMS.saveCurrentDraft();
+ $('#photo').attr('src', '');
+
+ $('#photo-next-btn .ui-btn-text').text('Skip');
+ $('#display_photo').hide();
+ $('#add_photo').show();
+ });
+ },
+
+ onClickCancel: function() {
+ this.finishedLocating();
+ },
+
+ onClickButtonNext: function() {
+ this.updateCurrentReport();
+ if ( !this.draftHasContent() && this.model.id ) {
+ var del = FMS.removeDraft( this.model.id );
+
+ var that = this;
+ del.done( function() { that.draftDeleted(); } );
+ del.fail( function() { that.draftDeleted(); } );
+ } else {
+ FMS.clearCurrentDraft();
+ this.navigate( this.next, 'left' );
+ }
+ },
+
+ draftDeleted: function() {
+ FMS.clearCurrentDraft();
+ this.navigate( this.next, 'left' );
+ },
+
+ updateCurrentReport: function() {
+ this.model.set('title', $('#form_title').val());
+ this.model.set('details', $('#form_detail').val());
+ FMS.saveCurrentDraft();
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/photo.js b/src/js/views/photo.js
new file mode 100644
index 0000000..74f3933
--- /dev/null
+++ b/src/js/views/photo.js
@@ -0,0 +1,87 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ PhotoView: FMS.FMSView.extend({
+ template: 'photo',
+ id: 'photo-page',
+ prev: 'around',
+ next: 'details',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext',
+ 'vclick #id_photo_button': 'takePhoto',
+ 'vclick #id_existing': 'addPhoto',
+ 'vclick #id_del_photo_button': 'deletePhoto'
+ },
+
+ beforeDisplay: function() {
+ this.fixPageHeight();
+ if ( this.model.get('file') ) {
+ $('#id_photo_button').parents('.ui-btn').hide();
+ $('#id_existing').parents('.ui-btn').hide();
+ } else {
+ this.$('#id_del_photo_button').parents('.ui-btn').hide();
+ }
+ },
+
+ takePhoto: function(e) {
+ e.preventDefault();
+ var that = this;
+ navigator.camera.getPicture( function(imgURI) { that.addPhotoSuccess(imgURI); }, function(error) { that.addPhotoFail(error); }, { saveToPhotoAlbum: true, quality: 49, destinationType: Camera.DestinationType.FILE_URI, sourceType: navigator.camera.PictureSourceType.CAMERA, correctOrientation: true });
+ },
+
+ addPhoto: function(e) {
+ e.preventDefault();
+ var that = this;
+ navigator.camera.getPicture( function(imgURI) { that.addPhotoSuccess(imgURI); }, function(error) { that.addPhotoFail(error); }, { saveToPhotoAlbum: false, quality: 49, destinationType: Camera.DestinationType.FILE_URI, sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY, correctOrientation: true });
+ },
+
+ addPhotoSuccess: function(imgURI) {
+ var move = FMS.files.moveURI( imgURI );
+
+ var that = this;
+ move.done( function( file ) {
+ $('#photo').attr('src', file.toURL());
+ that.model.set('file', file.toURL());
+ FMS.saveCurrentDraft();
+
+ $('#photo-next-btn .ui-btn-text').text('Next');
+ $('#id_del_photo_button').parents('.ui-btn').show();
+ $('#id_photo_button').parents('.ui-btn').hide();
+ $('#id_existing').parents('.ui-btn').hide();
+ });
+
+ move.fail( function() { that.addPhotoFail(); } );
+ },
+
+ addPhotoFail: function() {
+ if ( message != 'no image selected' &&
+ message != 'Selection cancelled.' &&
+ message != 'Camera cancelled.' ) {
+ this.displayError(FMS.strings.photo_failed);
+ }
+ },
+
+ deletePhoto: function(e) {
+ e.preventDefault();
+ var that = this;
+ var del = FMS.files.deleteURI( this.model.get('file') );
+
+ del.done( function() {
+ that.model.set('file', '');
+ FMS.saveCurrentDraft();
+ $('#photo').attr('src', 'images/placeholder-photo.png');
+
+ $('#photo-next-btn .ui-btn-text').text('Skip');
+ $('#id_del_photo_button').parents('.ui-btn').hide();
+ $('#id_photo_button').parents('.ui-btn').show();
+ $('#id_existing').parents('.ui-btn').show();
+ });
+
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/reports.js b/src/js/views/reports.js
new file mode 100644
index 0000000..4690fb2
--- /dev/null
+++ b/src/js/views/reports.js
@@ -0,0 +1,61 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ ReportsView: FMS.FMSView.extend({
+ template: 'reports',
+ id: 'reports',
+ next: 'home',
+ contentSelector: '#drafts',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .del_report': 'deleteReport',
+ 'vclick .use_report': 'useReport',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext'
+ },
+
+ deleteReport: function(e) {
+ e.preventDefault();
+ var el = $(e.target);
+ var id = el.parents('li').attr('id');
+ var del = FMS.removeDraft( id, true );
+ var that = this;
+ del.done( function() { that.onRemoveDraft(el); } );
+ del.fail( function() { that.onRemoveDraft(el); } );
+ },
+
+ setHeight: function(content, height) {
+ content.css( 'min-height', content + 'px');
+ },
+
+ useReport: function(e) {
+ e.preventDefault();
+ var el = $(e.target);
+ var id = el.parents('li').attr('id');
+ FMS.currentDraft = FMS.allDrafts.get(id);
+ this.navigate('around');
+ },
+
+ onRemoveDraft: function(el) {
+ el.parents('li').remove();
+ },
+
+ render: function(){
+ if ( !this.template ) {
+ console.log('no template to render');
+ return;
+ }
+ template = _.template( tpl.get( this.template ) );
+ if ( this.model ) {
+ this.$el.html(template({ model: this.model.toJSON(), drafts: FMS.allDrafts }));
+ } else {
+ this.$el.html(template());
+ }
+ this.afterRender();
+ return this;
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/save_offline.js b/src/js/views/save_offline.js
new file mode 100644
index 0000000..e9cedc8
--- /dev/null
+++ b/src/js/views/save_offline.js
@@ -0,0 +1,31 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SaveOfflineView: FMS.FMSView.extend({
+ template: 'save_offline',
+ id: 'save_offline',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pageshow': 'afterDisplay',
+ 'vclick #save_report': 'saveReport',
+ 'vclick #discard': 'discardReport'
+ },
+
+ saveReport: function() {
+ FMS.clearCurrentDraft();
+ this.navigate('reports');
+ },
+
+ discardReport: function() {
+ var reset = FMS.removeDraft(FMS.currentDraft.id, true);
+ var that = this;
+ reset.done( function() { that.onDraftRemove(); } );
+ reset.fail( function() { that.onDraftRemove(); } );
+ },
+
+ onDraftRemove: function() {
+ this.navigate( 'around', 'left' );
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/search.js b/src/js/views/search.js
new file mode 100644
index 0000000..6930e2a
--- /dev/null
+++ b/src/js/views/search.js
@@ -0,0 +1,81 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SearchView: FMS.FMSView.extend({
+ template: 'address_search',
+ id: 'search-page',
+
+ events: {
+ 'vclick a.address': 'goAddress',
+ 'vclick #submit': 'search',
+ 'vclick #locate': 'goLocate',
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'submit #postcodeForm': 'search'
+ },
+
+ afterDisplay: function() {
+ if ( FMS.isOffline ) {
+ this.navigate('offline');
+ }
+ },
+
+ search: function(e) {
+ // this is to stop form submission
+ e.preventDefault();
+ this.clearValidationErrors();
+ var pc = this.$('#pc').val();
+ this.listenTo(FMS.locator, 'search_located', this.searchSuccess );
+ this.listenTo(FMS.locator, 'search_failed', this.searchFail);
+
+ FMS.locator.lookup(pc);
+ },
+
+ searchSuccess: function( info ) {
+ this.stopListening(FMS.locator);
+ var coords = info.coordinates;
+ FMS.currentPosition = coords;
+ this.navigate('around');
+ },
+
+ goAddress: function(e) {
+ var t = $(e.target);
+ var lat = t.attr('data-lat');
+ var long = t.attr('data-long');
+
+ FMS.currentPosition = { latitude: lat, longitude: long };
+ this.navigate('around');
+ },
+
+ searchFail: function( details ) {
+ // this makes sure any onscreen keyboard is dismissed
+ $('#submit').focus();
+ this.stopListening(FMS.locator);
+ if ( details.msg ) {
+ this.validationError( 'pc', details.msg );
+ } else if ( details.locations ) {
+ var multiple = '';
+ for ( var i = 0; i < details.locations.length; i++ ) {
+ var loc = details.locations[i];
+ var li = '<li><a class="address" id="location_' + i + '" data-lat="' + loc.lat + '" data-long="' + loc.long + '">' + loc.address + '</a></li>';
+ multiple = multiple + li;
+ }
+ $('#front-howto').html('<p>Multiple matches found</p><ul data-role="listview" data-inset="true">' + multiple + '</ul>');
+ $('.ui-page').trigger('create');
+ } else {
+ this.validationError( 'pc', FMS.strings.location_problem );
+ }
+ },
+
+ goLocate: function(e) {
+ e.preventDefault();
+ this.navigate( 'around' );
+ },
+
+ _destroy: function() {
+ delete FMS.searchMessage;
+ this.stopListening(FMS.locator);
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/sent.js b/src/js/views/sent.js
new file mode 100644
index 0000000..d146db6
--- /dev/null
+++ b/src/js/views/sent.js
@@ -0,0 +1,36 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SentView: FMS.FMSView.extend({
+ template: 'sent',
+ id: 'sent-page',
+ prev: 'around',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #rate_app': 'onClickRateApp'
+ },
+
+ render: function(){
+ if ( !this.template ) {
+ console.log('no template to render');
+ return;
+ }
+ template = _.template( tpl.get( this.template ) );
+ this.$el.html(template(FMS.createdReport.toJSON()));
+ this.afterRender();
+ return this;
+ },
+
+ onClickRateApp: function(e) {
+ e.preventDefault();
+ var el = $('#rate_app');
+ var href = el.attr('href');
+ window.open(href, '_system');
+ return false;
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
diff --git a/src/js/views/submit.js b/src/js/views/submit.js
new file mode 100644
index 0000000..5ba6e83
--- /dev/null
+++ b/src/js/views/submit.js
@@ -0,0 +1,405 @@
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitView: FMS.FMSView.extend({
+ template: 'submit',
+ id: 'submit-page',
+ prev: 'details',
+ nopassword: 0,
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick .ui-btn-right': 'onClickButtonNext',
+ 'vclick #submit_signed_in': 'onClickSubmit',
+ 'vclick #submit_sign_in': 'onClickSubmit',
+ 'vclick #submit_register': 'onClickSubmit'
+ },
+
+ render: function(){
+ if ( !this.template ) {
+ console.log('no template to render');
+ return;
+ }
+ template = _.template( tpl.get( this.template ) );
+ if ( this.model ) {
+ this.$el.html(template({ model: this.model.toJSON(), user: FMS.currentUser.toJSON(), nopassword: this.nopassword }));
+ } else {
+ this.$el.html(template());
+ }
+ this.afterRender();
+ return this;
+ },
+
+ onClickSubmit: function(e) {
+ // in case we are getting here from a form submission
+ e.preventDefault();
+ this.beforeSubmit();
+
+ if ( this.validate() ) {
+ this.model.set('user', FMS.currentUser);
+ if ( FMS.isOffline ) {
+ this.navigate( 'save_offline' );
+ } else {
+ this.report = new FMS.Report( this.model.toJSON() );
+ this.listenTo( this.report, 'sync', this.onReportSync );
+ this.listenTo( this.report, 'invalid', this.onReportInvalid );
+ this.listenTo( this.report, 'error', this.onReportError );
+ this.report.save();
+ }
+ }
+ },
+
+ onReportSync: function(model, resp, options) {
+ this.stopListening();
+ this.afterSubmit();
+ if ( FMS.currentUser ) {
+ FMS.currentUser.save();
+ }
+ if (resp.report) {
+ this.report.set('site_id', resp.report);
+ this.report.set('site_url', CONFIG.FMS_URL + '/report/' + resp.report);
+ } else {
+ this.report.set('email_confirm', 1);
+ }
+ var reset = FMS.removeDraft( model.id, true);
+ var that = this;
+ reset.done( function() { that.onRemoveDraft(); } );
+ reset.fail( function() { that.onRemoveDraft(); } );
+ },
+
+ onRemoveDraft: function() {
+ FMS.clearCurrentDraft();
+ FMS.createdReport = this.report;
+ this.navigate( 'sent' );
+ },
+
+ onReportInvalid: function(model, err, options) {
+ var errors = err.errors;
+ var errorList = '<ul><li class="plain">' + FMS.strings.invalid_report + '</li>';
+ var validErrors = [ 'password', 'category', 'name' ];
+ for ( var k in errors ) {
+ if ( validErrors.indexOf(k) >= 0 || errors[k].match(/required/) ) {
+ if ( k === 'password' ) {
+ error = FMS.strings.password_problem;
+ } else if ( k !== '') {
+ error = errors[k];
+ }
+ errorList += '<li>' + error + '</li>';
+ }
+ }
+ errorList += '</ul>';
+ $('#errors').html(errorList).show();
+ },
+
+ onReportError: function(model, err, options) {
+ if ( err.errors ) {
+ alert( FMS.strings.sync_error + ': ' + err.errors);
+ } else {
+ alert( FMS.strings.unknown_sync_error );
+ }
+ },
+
+ beforeSubmit: function() {},
+ afterSubmit: function() {},
+
+ _destroy: function() {
+ this.stopListening();
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
+
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitEmailView: FMS.SubmitView.extend({
+ template: 'submit_email',
+ id: 'submit-email-page',
+ prev: 'details',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #set_password': 'onClickSetPassword',
+ 'vclick #have_password': 'onClickPassword',
+ 'vclick #email_confirm': 'onClickConfirm'
+ },
+
+ validate: function() {
+ this.clearValidationErrors();
+ var isValid = 1;
+
+ var email = $('#form_email').val();
+ if ( !email ) {
+ isValid = 0;
+ this.validationError('form_email', FMS.validationStrings.email.required);
+ // regexp stolen from jquery validate module
+ } else if ( ! /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(email) ) {
+ isValid = 0;
+ this.validationError('form_email', FMS.validationStrings.email.email);
+ }
+
+ return isValid;
+ },
+
+ onClickSetPassword: function(e) {
+ e.preventDefault();
+ if ( this.validate() ) {
+ this.model.set('submit_clicked', 'submit_register');
+ FMS.currentUser.set('email', $('#form_email').val());
+ this.navigate( 'submit-set-password' );
+ }
+ },
+
+ onClickPassword: function(e) {
+ e.preventDefault();
+ if ( this.validate() ) {
+ FMS.currentUser.set('email', $('#form_email').val());
+ this.navigate( 'submit-password' );
+ }
+ },
+
+ onClickConfirm: function(e) {
+ e.preventDefault();
+ if ( this.validate() ) {
+ FMS.currentUser.set('email', $('#form_email').val());
+ this.navigate( 'submit-name' );
+ }
+ },
+
+ _destroy: function() {}
+ })
+ });
+})(FMS, Backbone, _, $);
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitNameView: FMS.SubmitView.extend({
+ template: 'submit_name',
+ id: 'submit-name-page',
+ prev: 'submit-email',
+ nopassword: 1,
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #send_confirm': 'onClickSubmit'
+ },
+
+ initialize: function() {
+ console.log('submit name initalize');
+ this.listenTo(this.model, 'sync', this.onReportSync );
+ this.listenTo( this.model, 'error', this.onReportError );
+ },
+
+ validate: function() {
+ this.clearValidationErrors();
+ var isValid = 1;
+
+ var name = $('#form_name').val();
+ if ( !name ) {
+ isValid = 0;
+ this.validationError('form_name', FMS.validationStrings.name.required );
+ } else {
+ var validNamePat = /\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i;
+ if ( name.length < 6 || !name.match( /\S/ ) || name.match( validNamePat ) ) {
+ isValid = 0;
+ this.validationError('form_name', FMS.validationStrings.name.validName);
+ }
+ }
+
+ if ( this.model.get('title_list') && this.model.get('title_list').length > 0 ) {
+ if ( $('#form_title').val() === '' ) {
+ this.validationError('form_title', FMS.strings.required);
+ isValid = 0;
+ }
+ }
+
+ return isValid;
+ },
+
+ beforeSubmit: function() {
+ this.model.set('name', $('#form_name').val());
+ this.model.set('phone', $('#form_phone').val());
+ this.model.set('may_show_name', $('#form_may_show_name').val());
+ if ( this.model.get('title_list') && this.model.get('title_list').length > 0 ) {
+ FMS.currentUser.set('title', $('#form_title').val());
+ }
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitNameSetPasswordView: FMS.SubmitNameView.extend({
+ template: 'submit_name',
+ id: 'submit-name-page-set-password',
+ prev: 'submit-set-password',
+ nopassword: 0
+ })
+ });
+})(FMS, Backbone, _, $);
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitPasswordView: FMS.SubmitView.extend({
+ template: 'submit_password',
+ id: 'submit-password-page',
+ prev: 'submit-email',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #report': 'onClickSubmit',
+ 'vclick #confirm_name': 'onClickSubmit',
+ 'submit #passwordForm': 'onClickSubmit'
+ },
+
+ initialize: function() {
+ this.listenTo(this.model, 'sync', this.onReportSync );
+ this.listenTo( this.model, 'error', this.onReportError );
+ },
+
+ validate: function() {
+ var isValid = 1;
+
+ if ( !$('#form_password').val() ) {
+ isValid = 0;
+ this.validationError('form_password', FMS.validationStrings.password );
+ }
+
+ return isValid;
+ },
+
+ beforeSubmit: function() {
+ $('#report').focus();
+ if ( $('#form_name').val() ) {
+ this.model.set('submit_clicked', 'submit_register');
+ this.model.set('phone', $('#form_phone').val());
+ this.model.set('name', $('#form_name').val());
+ this.model.set('may_show_name', $('#form_may_show_name').val());
+ } else {
+ // if this is set then we are registering a password
+ if ( ! this.model.get('submit_clicked') ) {
+ this.model.set('submit_clicked', 'submit_sign_in');
+ }
+ FMS.currentUser.set('password', $('#form_password').val());
+ }
+ },
+
+ afterSubmit: function() {
+ FMS.isLoggedIn = 1;
+ },
+
+ onReportError: function(model, err, options) {
+ if ( err.check_name ) {
+ $('#form_name').val(err.check_name);
+ $('#password_row').hide();
+ $('#check_name').show();
+ $('#confirm_name').focus();
+ } else {
+ if ( err.errors && err.errors.password ) {
+ this.validationError('form_password', err.errors.password );
+ }
+ $('#report').focus();
+ }
+ }
+
+ })
+ });
+})(FMS, Backbone, _, $);
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitSetPasswordView: FMS.SubmitPasswordView.extend({
+ template: 'submit_set_password',
+ id: 'submit-set-password-page',
+ prev: 'submit-email',
+ next: 'submit-name-set-password',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #continue': 'onClickContinue',
+ 'submit #passwordForm': 'onClickContinue'
+ },
+
+ onClickContinue: function(e) {
+ e.preventDefault();
+ $('#continue').focus();
+ if ( ! this.model.get('submit_clicked') ) {
+ this.model.set('submit_clicked', 'submit_sign_in');
+ }
+ FMS.currentUser.set('password', $('#form_password').val());
+ this.navigate( this.next );
+ }
+ })
+ });
+})(FMS, Backbone, _, $);
+
+(function (FMS, Backbone, _, $) {
+ _.extend( FMS, {
+ SubmitConfirmView: FMS.SubmitView.extend({
+ template: 'submit_confirm',
+ id: 'submit-confirm-page',
+ prev: 'details',
+
+ events: {
+ 'pagehide': 'destroy',
+ 'pagebeforeshow': 'beforeDisplay',
+ 'pageshow': 'afterDisplay',
+ 'vclick .ui-btn-left': 'onClickButtonPrev',
+ 'vclick #report': 'onClickSubmit'
+ },
+
+ validate: function() {
+ this.clearValidationErrors();
+ var isValid = 1;
+
+ var name = $('#form_name').val();
+ if ( !name ) {
+ isValid = 0;
+ this.validationError('form_name', FMS.validationStrings.name.required );
+ } else {
+ var validNamePat = /\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i;
+ if ( name.length < 6 || !name.match( /\S/ ) || name.match( validNamePat ) ) {
+ isValid = 0;
+ this.validationError('form_name', FMS.validationStrings.name.validName);
+ }
+ }
+
+ return isValid;
+ },
+
+ beforeSubmit: function() {
+ this.model.set('name', $('#form_name').val());
+ this.model.set('phone', $('#form_phone').val());
+ this.model.set('may_show_name', $('#form_may_show_name').val());
+ this.model.set('submit_clicked', 'submit_register');
+ },
+
+ onReportError: function(model, err, options) {
+ // TODO: this is a temporary measure which should be replaced by a more
+ // sensible login mechanism
+ if ( err.check_name ) {
+ this.onClickSubmit();
+ } else {
+ if ( err.errors && err.errors.password ) {
+ this.validationError('form_password', err.errors.password );
+ }
+ }
+ }
+ })
+ });
+})(FMS, Backbone, _, $);