aboutsummaryrefslogtreecommitdiffstats
path: root/www
diff options
context:
space:
mode:
Diffstat (limited to 'www')
-rw-r--r--www/cobrands/cuidomiciudad/css/style.css6
-rw-r--r--www/cobrands/cuidomiciudad/css/style.scss6
-rw-r--r--www/cobrands/makemyisland/css/style.css6
-rw-r--r--www/cobrands/makemyisland/css/style.scss6
-rw-r--r--www/css/fms.css106
-rw-r--r--www/images/hide-pins-link.pngbin0 -> 734 bytes
-rw-r--r--www/images/hide-pins-link.svg1
-rw-r--r--www/index.html2
-rw-r--r--www/js/app.js84
-rw-r--r--www/js/config.js-example45
-rw-r--r--www/js/files.js14
-rw-r--r--www/js/map-OpenLayers.js10
-rw-r--r--www/js/models/draft.js26
-rw-r--r--www/js/models/report.js122
-rw-r--r--www/js/router.js2
-rw-r--r--www/js/strings.en.js5
-rw-r--r--www/js/strings.es.js5
-rw-r--r--www/js/views/around.js31
-rw-r--r--www/js/views/details.js35
-rw-r--r--www/js/views/details_extra.js8
-rw-r--r--www/js/views/fms.js30
-rw-r--r--www/js/views/home.js5
-rw-r--r--www/js/views/login.js28
-rw-r--r--www/js/views/offline.js93
-rw-r--r--www/js/views/photo.js67
-rw-r--r--www/js/views/submit.js110
-rw-r--r--www/templates/en/around.html1
-rw-r--r--www/templates/en/details_extra.html6
-rw-r--r--www/templates/en/existing.html4
-rw-r--r--www/templates/en/help.html2
-rw-r--r--www/templates/en/login.html16
-rw-r--r--www/templates/en/offline.html35
-rw-r--r--www/templates/en/photo.html64
-rw-r--r--www/templates/en/reports.html2
-rw-r--r--www/templates/en/submit_confirm.html2
-rw-r--r--www/templates/en/submit_set_password.html2
-rw-r--r--www/templates/es/around.html1
-rw-r--r--www/templates/es/details_extra.html6
-rw-r--r--www/templates/es/existing.html4
-rw-r--r--www/templates/es/login.html16
-rw-r--r--www/templates/es/offline.html35
-rw-r--r--www/templates/es/photo.html64
-rw-r--r--www/templates/es/reports.html2
-rw-r--r--www/templates/es/submit_confirm.html2
-rw-r--r--www/templates/es/submit_set_password.html2
45 files changed, 765 insertions, 354 deletions
diff --git a/www/cobrands/cuidomiciudad/css/style.css b/www/cobrands/cuidomiciudad/css/style.css
index b50f822..db3112f 100644
--- a/www/cobrands/cuidomiciudad/css/style.css
+++ b/www/cobrands/cuidomiciudad/css/style.css
@@ -1,11 +1,11 @@
/* line 8, style.scss */
-.ios7 .ui-bar-a,
-body.ios7 {
+.ios .ui-bar-a,
+body.ios {
background-color: #005cb9;
}
/* line 13, style.scss */
-.ios7 .ui-bar-a {
+.ios .ui-bar-a {
border-color: #005cb9;
}
diff --git a/www/cobrands/cuidomiciudad/css/style.scss b/www/cobrands/cuidomiciudad/css/style.scss
index 3976a23..71cb081 100644
--- a/www/cobrands/cuidomiciudad/css/style.scss
+++ b/www/cobrands/cuidomiciudad/css/style.scss
@@ -5,12 +5,12 @@ $color-cmc-yellow: #eed11c;
$color-cmc-green: #00ad68;
$text-color: #eee;
-.ios7 .ui-bar-a,
-body.ios7 {
+.ios .ui-bar-a,
+body.ios {
background-color: $color-cmc-blue;
}
-.ios7 .ui-bar-a {
+.ios .ui-bar-a {
border-color: $color-cmc-blue;
}
diff --git a/www/cobrands/makemyisland/css/style.css b/www/cobrands/makemyisland/css/style.css
index e0ee20c..106d6e0 100644
--- a/www/cobrands/makemyisland/css/style.css
+++ b/www/cobrands/makemyisland/css/style.css
@@ -1,8 +1,8 @@
-.ios7 .ui-bar-a,
-body.ios7 {
+.ios .ui-bar-a,
+body.ios {
background-color: #9fd9ea; }
-.ios7 .ui-bar-a {
+.ios .ui-bar-a {
border-color: #9fd9ea; }
#load-screen,
diff --git a/www/cobrands/makemyisland/css/style.scss b/www/cobrands/makemyisland/css/style.scss
index ea8c43c..195d7f1 100644
--- a/www/cobrands/makemyisland/css/style.scss
+++ b/www/cobrands/makemyisland/css/style.scss
@@ -3,12 +3,12 @@
$yellow: rgb(249, 255, 198);
$blue: rgb(159, 217, 234);
-.ios7 .ui-bar-a,
-body.ios7 {
+.ios .ui-bar-a,
+body.ios {
background-color: $blue;
}
-.ios7 .ui-bar-a {
+.ios .ui-bar-a {
border-color: $blue;
}
diff --git a/www/css/fms.css b/www/css/fms.css
index aa93acc..d800619 100644
--- a/www/css/fms.css
+++ b/www/css/fms.css
@@ -160,38 +160,37 @@
background-color: rgba(0,0,0,0);
}
-/* ios 7 tweaks to deal with different status bar behaviour */
- .ios7 .ui-mobile-viewport {
+/* ios tweaks to deal with different status bar behaviour */
+ .ios .ui-mobile-viewport {
background-color: none;
background-image: none;
}
- .ios7 .ui-bar-a,
- body.ios7 {
+ .ios .ui-bar-a,
+ body.ios {
background-color: #F6BE41;
background-image: none;
}
- .ios7 .ui-bar-a {
+ .ios .ui-bar-a {
border-color: #F6BE41;
}
- .ios7 .ui-header-fixed .ui-btn-left,
- .ios7 .ui-header-fixed .ui-btn-right {
+ .ios .ui-header-fixed .ui-btn-left,
+ .ios .ui-header-fixed .ui-btn-right {
top: 4px;
}
- .ios7 #map_box {
- top: 20px;
+ .ios #map_box {
background-color: #534741;
background-image: url('../images/mapbg.png');
}
- .ios7 #around-page #locating {
- bottom: -22px;
+ .ios #around-page #locating {
+ bottom: -2px;
}
- .ios7 #map_box .olLayerDiv div {
+ .ios #map_box .olLayerDiv div {
display: block !important;
}
@@ -210,7 +209,7 @@
#drafts,
div[data-role='content'] {
position: relative;
- margin: 20px 10px;
+ margin: 10px 10px 20px 10px;
background-color: rgba(255, 255, 255, 0.9);
}
@@ -401,6 +400,10 @@
text-align: center;
}
+ #offline textarea {
+ min-height: 0;
+ }
+
/* home screen */
#front-page div[data-role='content'] {
@@ -424,6 +427,9 @@
-webkit-filter: blur(3px);
opacity: 0.9;
}
+ #map_box.background-map .olControlAttribution{
+ display: none;
+ }
#map{
opacity: 0.85;
@@ -592,19 +598,31 @@
}
- #relocate {
- display: block;
+ #relocate, #hidepins {
+ display: inline-block;
height: 30px;
width: 30px;
padding: 5px;
margin: 0px;
background-color: rgba(248,248,248,0.9);
+ }
+
+ #relocate {
background-image: url(../images/location@x2.png);
background-repeat: no-repeat;
background-position: center center;
background-size: 50%;
}
+ #hidepins {
+ background-image: url(../images/hide-pins-link.png);
+ background-image: url(../images/hide-pins-link.svg);
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-size: 80%;
+ margin-left: .3em;
+ }
+
#pc {
padding-left: 0.2em;
border-left: 1px solid #F6BE41;
@@ -726,11 +744,29 @@
div.photo {
position: relative;
display: inline-block;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: top center;
+ width: 100%;
+ margin: 0 0 1em 0;
+ -webkit-box-flex: 1;
+ flex: 1;
+ }
+
+ div.photo:nth-child(3) {
+ margin-bottom: 0;
}
div.photo-wrapper {
margin: auto;
text-align: center;
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-align: center;
+ align-items: center;
}
img.small {
@@ -744,9 +780,9 @@
width: 270px;
}
- #id_del_photo_button.photo-corner-button {
+ .del_photo_button.photo-corner-button {
position: absolute;
- bottom: -15px;
+ top: -15px;
right: 0px;
-webkit-border-radius: 50%;
border-radius: 50%;
@@ -758,6 +794,12 @@
font-size: 30px;
}
+ /* On the offline page the UI is ordered differently, so we want
+ to stop the button overlapping the 'add another photo' butto' */
+ #offline .del_photo_button.photo-corner-button {
+ top: 0;
+ }
+
@media only screen and (max-width: 800px) and (min-width: 600px) {
img.placeholder {
height: 398px;
@@ -814,6 +856,11 @@
}
#category_meta {
margin: 0px 15px 15px 15px;
+ padding: 1em 0;
+ }
+
+ #category_meta h2 {
+ line-height: 1em;
}
#category_meta h4 {
@@ -931,3 +978,28 @@
top: 5px;
right: 5px;
}
+
+ .ui-content.hidden {
+ visibility: hidden;
+ }
+
+/* iPhone X has an 'unsafe' area occupied by the home
+indicator at the bottom of screen, which we need to stop UI controls
+clashing with. */
+body.iphone-x {
+ /* iOS 11–11.1 use constant(), 11.2 uses env() - assign whatever one is valid
+ to a CSS variable as this makes things simpler (e.g. with calc()) */
+ --safe-area-inset-bottom: constant(safe-area-inset-bottom);
+ --safe-area-inset-bottom: env(safe-area-inset-bottom);
+}
+
+/* The OpenLayers attribution control */
+body.iphone-x .olControlAttribution {
+ padding-bottom: var(--safe-area-inset-bottom);
+}
+
+/* The "new report here" button */
+body.iphone-x .map-bottom-btn {
+ /* Can't use env() inside calc(), so assign value to a CSS variable instead */
+ bottom: calc(20px + var(--safe-area-inset-bottom));
+}
diff --git a/www/images/hide-pins-link.png b/www/images/hide-pins-link.png
new file mode 100644
index 0000000..d2b48ac
--- /dev/null
+++ b/www/images/hide-pins-link.png
Binary files differ
diff --git a/www/images/hide-pins-link.svg b/www/images/hide-pins-link.svg
new file mode 100644
index 0000000..1c8ed76
--- /dev/null
+++ b/www/images/hide-pins-link.svg
@@ -0,0 +1 @@
+<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="evenodd"><path d="M25.4 21.1l-3.7-3.6A4 4 0 0 0 18 12a4 4 0 0 0-1.5.3L13 8.6a9 9 0 0 1 12.5 12.5zm-4.5 4l-3 6-3.4-6.8a9 9 0 0 1-5.2-10.7L21 25.2zM7.4 6L30 28.6 28.6 30 6 7.4z"/></g></svg> \ No newline at end of file
diff --git a/www/index.html b/www/index.html
index aae1e71..b60ab56 100644
--- a/www/index.html
+++ b/www/index.html
@@ -8,7 +8,7 @@
-->
<head>
<title></title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta charset="utf-8">
<script type="text/javascript" src="jslib/jquery-1.9.1.min.js"></script>
diff --git a/www/js/app.js b/www/js/app.js
index 282e921..8b7432c 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -100,6 +100,11 @@ var tpl = {
printDebug: function(msg) {
if ( CONFIG.DEBUG ) {
console.log(msg);
+
+ // Some messages get logged before we've had a chance to
+ // attach the debugger, so keep them all for later reference.
+ FMS.debug_messages = FMS.debug_messages || [];
+ FMS.debug_messages.push(msg);
}
},
@@ -113,21 +118,28 @@ var tpl = {
},
checkLoggedInStatus: function() {
+ var p = $.Deferred();
+
if ( FMS.isOffline ) {
+ p.resolve();
} else {
$.ajax( {
url: CONFIG.FMS_URL + '/auth/ajax/check_auth',
type: 'GET',
dataType: 'json',
timeout: 30000,
- success: function( data, status ) {
- FMS.isLoggedIn = 1;
- },
- error: function() {
- FMS.isLoggedIn = 0;
- }
- } );
+ })
+ .done(function() {
+ FMS.isLoggedIn = 1;
+ p.resolve();
+ })
+ .fail(function() {
+ FMS.isLoggedIn = 0;
+ p.resolve();
+ })
}
+
+ return p;
},
saveCurrentDraft: function(force) {
@@ -151,12 +163,12 @@ var tpl = {
removeDraft: function(draftID, removePhoto) {
var draft = FMS.allDrafts.get(draftID);
- var uri = draft.get('file');
+ var files = draft.get('files');
FMS.allDrafts.remove(draft);
draft.destroy();
- if ( removePhoto && uri ) {
- return FMS.files.deleteURI( uri );
+ if ( removePhoto && files.length ) {
+ return FMS.files.deleteURIs( files );
}
var p = $.Deferred();
p.resolve();
@@ -266,6 +278,13 @@ var tpl = {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(false);
}
$('#load-screen').height( $(window).height() );
+
+ // Rough-and-ready iPhone X detection so CSS can stop things
+ // obscuring the home indicator at the bottom of the screen.
+ if (window.screen.width == 375 && window.screen.height == 812) {
+ $("body").addClass("iphone-x");
+ }
+
FMS.initialized = 1;
if ( navigator && navigator.splashscreen ) {
navigator.splashscreen.hide();
@@ -283,11 +302,7 @@ var tpl = {
if ( typeof device !== 'undefined' && device.platform === 'iOS' ) {
var model = parseInt(device.model.replace('iPhone',''), 10);
FMS.iPhoneModel = model;
-
- // fix overlap of status bar in ios7
- if (parseFloat(window.device.version) >= 7.0) {
- $('body').addClass('ios7');
- }
+ $('body').addClass('ios');
}
_.extend(FMS, {
@@ -306,9 +321,6 @@ var tpl = {
}
FMS.windowHeight = $(window).height();
- if ( $('body').hasClass('ios7') ) {
- FMS.windowHeight -= 20;
- }
if ( localStorage.usedBefore ) {
FMS.usedBefore = 1;
@@ -327,23 +339,37 @@ var tpl = {
$(document).on('ajaxStart', function() { $.mobile.loading('show'); } );
$(document).on('ajaxStop', function() { $.mobile.loading('hide'); } );
- $('#display-help').on('vclick', function(e) { FMS.helpShow(e); } );
+ $('#display-help').on('vclick', function(e) {
+ // Avoid a problem with input cursors being visible through
+ // the help layer on Web View, by unfocusing the element
+ if (device.platform === 'iOS') {
+ $('input').blur();
+ }
+
+ FMS.helpShow(e);
+ });
+
$('#dismiss').on('vclick', function(e) { FMS.helpHide(e); } );
FMS.allDrafts.comparator = function(a,b) { var a_date = a.get('created'), b_date = b.get('created'); return a_date === b_date ? 0 : a_date < b_date ? 1 : -1; };
FMS.allDrafts.fetch();
FMS.checkOnlineStatus();
FMS.loadCurrentDraft();
- FMS.checkLoggedInStatus();
- FMS.setupHelp();
-
- Backbone.history.start();
- if ( navigator && navigator.splashscreen ) {
- navigator.splashscreen.hide();
- } else {
- $('#load-screen').hide();
- }
- $('#display-help').show();
+ FMS.checkLoggedInStatus().done(function() {
+ if (!CONFIG.HELP_DISABLED) {
+ FMS.setupHelp();
+ }
+
+ Backbone.history.start();
+ if ( navigator && navigator.splashscreen ) {
+ navigator.splashscreen.hide();
+ } else {
+ $('#load-screen').hide();
+ }
+ if (!CONFIG.HELP_DISABLED) {
+ $('#display-help').show();
+ }
+ });
});
}
});
diff --git a/www/js/config.js-example b/www/js/config.js-example
index 2fceda7..1f1eef1 100644
--- a/www/js/config.js-example
+++ b/www/js/config.js-example
@@ -43,7 +43,50 @@ var CONFIG = {
image_svg: 'images/pin.svg',
background_svg: 'images/pin_shadow.svg'
}
- }
+ },
+
+ // Set this to true if you want to disable the help button on the right hand
+ // side of the screen. NB you'll also need to hide #display-help and #help
+ // elements in your CSS.
+ HELP_DISABLED: false,
+
+ // Set this to true if the user must provide at least one photo when making
+ // a report. If this is true the 'skip' button on the photo page is removed
+ // and 'next' doesn't appear until at least one photo is attached.
+ PHOTO_REQUIRED: false,
+
+ // The maximum number of photos the user can attach to a report.
+ MAX_PHOTOS: 3,
+
+ // If this is true then the user must login as the first step after
+ // installing the app, and before making any reports.
+ LOGIN_REQUIRED: false,
+
+ // The ratio of the data bounds to the viewport bounds (in each dimension).
+ // See http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/Strategy/BBOX-js.html
+ MAP_LOADING_RATIO: 2,
+
+ // If the user is logged in and this setting is true, the 'Your details'
+ // page is skipped and the report is sent immediately after the report
+ // details have been entered.
+ SKIP_CONFIRM_REPORT: false,
+
+ // You can optionally enforce a minimum password length if the user is
+ // registering an account when submitting a report. This should match the
+ // same minimum length required by your FixMyStreet server.
+ // Set this to 0 if you wish to disable this check. NB: If the check is
+ // active on the server the user's password may still be rejected if it's
+ // too short.
+ PASSWORD_MIN_LENGTH: 6,
+
+ // FMS provides a mechanism for rejecting passwords that are too common.
+ // Set this flag to true if the password should be checked against the
+ // server when a user registers an account via the app.
+ // NB: If this flag is false here but the check is active on the FMS server,
+ // common passwords will still be rejected at the point the report is sent
+ // from the app to the server - which may be a large POST if the report has
+ // photos attached.
+ PASSWORD_CHECK_COMMON: true
};
diff --git a/www/js/files.js b/www/js/files.js
index eea38c3..0725323 100644
--- a/www/js/files.js
+++ b/www/js/files.js
@@ -57,6 +57,16 @@
},
+ deleteURIs: function(uris) {
+ console.log("deleteURIs", uris);
+ var deferred = $.Deferred();
+ deferred.resolve();
+ uris.forEach(function(uri) {
+ deferred = deferred.then(FMS.files.deleteURI(uri));
+ });
+ return deferred;
+ },
+
// Delete a file from the filesystem
deleteFile: function (path) {
@@ -124,7 +134,7 @@
function moveFile (src, dest, newName) {
- FMS.printDebug( 'moveing file ' + src.fullPath + ' to ' + dest.fullPath );
+ FMS.printDebug( 'moving file ' + src.fullPath + ' to ' + dest.fullPath );
var move = $.Deferred();
@@ -166,7 +176,7 @@
var file = $.Deferred();
- window.resolveLocalFileSystemURI( uri, file.resolve, file.reject);
+ window.resolveLocalFileSystemURL( uri, file.resolve, file.reject);
return file.promise();
}
diff --git a/www/js/map-OpenLayers.js b/www/js/map-OpenLayers.js
index d458641..1de220d 100644
--- a/www/js/map-OpenLayers.js
+++ b/www/js/map-OpenLayers.js
@@ -128,7 +128,7 @@ function fixmystreet_onload() {
fixmystreet.map.addLayer(fixmystreet.report_location);
if (fixmystreet.page == 'around') {
- fixmystreet.bbox_strategy = new OpenLayers.Strategy.BBOX({ ratio: 1 });
+ fixmystreet.bbox_strategy = new OpenLayers.Strategy.BBOX({ ratio: CONFIG.MAP_LOADING_RATIO });
pin_layer_options.strategies = [ fixmystreet.bbox_strategy ];
pin_layer_options.protocol = new OpenLayers.Protocol.HTTP({
url: CONFIG.FMS_URL + '/ajax',
@@ -202,13 +202,9 @@ OpenLayers.Map.prototype.getCurrentSize = function() {
function show_map(event) {
if (typeof fixmystreet !== 'undefined' && fixmystreet.page == 'around') {
// Immediately go full screen map if on around page
- var mapTop = 0;
- if ( $('body').hasClass('ios7') ) {
- mapTop = 20;
- }
$('#map_box').css({
position: 'fixed',
- top: mapTop, left: 0, right: 0, bottom: 0,
+ top: 0, left: 0, right: 0, bottom: 0,
height: FMS.windowHeight,
margin: 0
});
@@ -408,7 +404,7 @@ OpenLayers.Control.ActionAfterDrag = OpenLayers.Class(OpenLayers.Control, {
if ( $('#confirm-map').css('display') == 'block' ) {
$('#reposition').show();
} else {
- $('#relocate').show();
+ $('#relocate, #hidepins').removeClass('nodisplay');
$('#front-howto').hide();
}
}
diff --git a/www/js/models/draft.js b/www/js/models/draft.js
index 56d8111..e517f1b 100644
--- a/www/js/models/draft.js
+++ b/www/js/models/draft.js
@@ -3,17 +3,19 @@
Draft: Backbone.Model.extend({
localStorage: new Backbone.LocalStorage(CONFIG.NAMESPACE + '-drafts'),
- defaults: {
- lat: 0,
- lon: 0,
- title: '',
- details: '',
- may_show_name: '',
- category: '',
- phone: '',
- pc: '',
- file: '',
- created: moment.utc()
+ defaults: function() {
+ return {
+ lat: 0,
+ lon: 0,
+ title: '',
+ details: '',
+ may_show_name: '',
+ category: '',
+ phone: '',
+ pc: '',
+ files: [],
+ created: moment.utc()
+ };
},
description: function() {
@@ -33,7 +35,7 @@
this.get('title') ||
this.get('details') ||
this.get('category') ||
- this.get('file')
+ this.get('files').length
) {
return true;
}
diff --git a/www/js/models/report.js b/www/js/models/report.js
index 1a72e73..6926980 100644
--- a/www/js/models/report.js
+++ b/www/js/models/report.js
@@ -3,16 +3,18 @@
Report: Backbone.Model.extend({
urlRoot: CONFIG.FMS_URL + '/report/ajax',
- defaults: {
- lat: 0,
- lon: 0,
- title: '',
- details: '',
- may_show_name: '',
- category: '',
- phone: '',
- pc: '',
- file: ''
+ defaults: function() {
+ return {
+ lat: 0,
+ lon: 0,
+ title: '',
+ details: '',
+ may_show_name: '',
+ category: '',
+ phone: '',
+ pc: '',
+ files: []
+ };
},
sync: function(method, model, options) {
@@ -51,6 +53,74 @@
return false;
},
+ _readFileAsBase64String: function(file, success, error) {
+ return this._readFileAsBinaryString(file, function(data) {
+ var b64 = btoa(data);
+ success(b64);
+ }, error);
+ },
+
+ _readFileAsBinaryString: function(file, success, error) {
+ var reader = new FileReader();
+ reader.onloadend = function() {
+ success(this.result);
+ };
+ reader.onerror = error;
+ return reader.readAsBinaryString(file);
+ },
+
+ _getParamName: function(field, encoding, length) {
+ // The FileTransfer plugin technically only supports a single
+ // file in each upload. However, we can force other files to
+ // be added with a little workaround.
+ // FileTransfer allows extra parameters to be sent with the
+ // HTTP POST request, each of which is its own part of the
+ // multipart-encoded request.
+ // For a part to be treated as a file by the backend we need
+ // to provide a 'filename' value in the Content-Disposition
+ // header. The FileTransfer code doesn't escape the names of
+ // extra POST parameters[0][1], so we can take advantage of this
+ // and essentially inject our own header lines and filename
+ // value with a carefully-crafted HTTP POST field name that's
+ // passed to FileTransfer.upload.
+ // FIXME: This is basically a hack, and needs a better
+ // solution at some point.
+ // [0]: https://github.com/apache/cordova-plugin-file-transfer/blob/49c21f951f51381d887646b38823222ed11c60c1/src/ios/CDVFileTransfer.m#L208
+ // [1]: https://github.com/apache/cordova-plugin-file-transfer/blob/49c21f951f51381d887646b38823222ed11c60c1/src/android/FileTransfer.java#L369
+ var name = field + '"; filename="' + field + '.jpg"\r\n';
+ name += "Content-Type: image/jpeg\r\n";
+ name += "Content-Transfer-Encoding: " + encoding + "\r\n";
+ name += "Content-Length: " + length + "\r\n";
+ name += 'X-Ignore-This-Header: "'; // to close the open quotes
+ return name;
+ },
+
+ _addExtraPhotos: function(files, options, success, error) {
+ var photos = [];
+ for (var i = 0; i < files.length; i++) {
+ var uri = files[i];
+ photos.push({field: "photo"+(i+2), uri: uri});
+ }
+ this._addNextExtraPhoto(photos, options, success, error);
+ },
+
+ _addNextExtraPhoto: function(photos, options, success, error) {
+ var photo = photos.shift();
+ if (photo === undefined) {
+ success();
+ return;
+ }
+ var self = this;
+ resolveLocalFileSystemURL(photo.uri, function(fileentry) {
+ fileentry.file(function(file) {
+ self._readFileAsBase64String(file, function(data) {
+ options.params[self._getParamName(photo.field, "base64", data.length)] = data;
+ self._addNextExtraPhoto(photos, options, success, error);
+ }, error);
+ }, error);
+ }, error);
+ },
+
post: function(model,options) {
var params = {
@@ -86,7 +156,7 @@
}
var that = this;
- if ( model.get('file') && model.get('file') !== '' ) {
+ if ( model.get('files') && model.get('files').length > 0 ) {
var fileUploadSuccess = function(r) {
FMS.uploading = false;
$.mobile.loading('hide');
@@ -122,7 +192,8 @@
}
};
- fileURI = model.get('file');
+ var files = model.get('files').slice();
+ fileURI = files.shift();
var fileOptions = new FileUploadOptions();
fileOptions.fileKey="photo";
@@ -169,14 +240,25 @@
uploadPcnt++;
}
};
- $.mobile.loading('show', {
- text: FMS.strings.photo_loading,
- textVisible: true,
- html: '<span class="ui-icon ui-icon-loading"></span><h1>' + FMS.strings.photo_loading + '</h1><span id="progress"></span>'
- });
- window.setTimeout( checkUpload, 15000 );
- FMS.uploading = true;
- ft.upload(fileURI, CONFIG.FMS_URL + "/report/new/mobile", fileUploadSuccess, fileUploadFail, fileOptions);
+
+ // If file2 or file3 have been set on this model we need to
+ // add the photos to the file upload request manually
+ // as FileTransfer only supports a single file upload.
+ that._addExtraPhotos(
+ files,
+ fileOptions,
+ function() {
+ $.mobile.loading('show', {
+ text: FMS.strings.photo_loading,
+ textVisible: true,
+ html: '<span class="ui-icon ui-icon-loading"></span><h1>' + FMS.strings.photo_loading + '</h1><span id="progress"></span>'
+ });
+ window.setTimeout( checkUpload, 15000 );
+ FMS.uploading = true;
+ ft.upload(fileURI, CONFIG.FMS_URL + "/report/new/mobile", fileUploadSuccess, fileUploadFail, fileOptions);
+ },
+ fileUploadFail
+ );
};
setupChecker();
} else {
diff --git a/www/js/router.js b/www/js/router.js
index c691abb..2a23708 100644
--- a/www/js/router.js
+++ b/www/js/router.js
@@ -159,7 +159,7 @@
// any transitions as they just add visual distraction to no end
// likewise displaying the offline page
var options = { changeHash: false };
- if ( !this.currentView || this.currentView.id == 'front-page' || view.id == 'offline' ) {
+ if ( !this.currentView || this.currentView.id == 'front-page' || view.id == 'offline' || view.id === this.currentView.id) {
options.transition = 'none';
}
if ( this.reverse ) {
diff --git a/www/js/strings.en.js b/www/js/strings.en.js
index 1dabd30..bf67b14 100644
--- a/www/js/strings.en.js
+++ b/www/js/strings.en.js
@@ -17,7 +17,10 @@
required: 'Please enter your email',
email: 'Please enter a valid email'
},
- password: 'Please enter a password'
+ password: {
+ required: 'Please enter a password',
+ short: 'Please enter a password at least %d characters long'
+ }
},
strings: {
next: 'Next',
diff --git a/www/js/strings.es.js b/www/js/strings.es.js
index 9c8b950..5b2481b 100644
--- a/www/js/strings.es.js
+++ b/www/js/strings.es.js
@@ -17,7 +17,10 @@
required: 'Por favor, introduzca su email',
email: 'Por favor, introduzca un email válido'
},
- password: 'Por favor, introduzca la contraseña'
+ password: {
+ required: 'Por favor, introduzca la contraseña',
+ short: 'Please enter a password at least %d characters long'
+ }
},
strings: {
next: 'Siguiente',
diff --git a/www/js/views/around.js b/www/js/views/around.js
index f0b8692..4afc5bb 100644
--- a/www/js/views/around.js
+++ b/www/js/views/around.js
@@ -15,6 +15,7 @@
'vclick .ui-input-clear': 'clearSearchErrors',
'blur #pc': 'clearSearchErrors',
'vclick #relocate': 'centerMapOnPosition',
+ 'vclick #hidepins': 'toggleMarkersVisibility',
'vclick #cancel': 'onClickCancel',
'vclick #confirm-map': 'onClickReport',
'vclick #mark-here': 'onClickMark',
@@ -45,7 +46,7 @@
$('#view-my-reports').hide();
$('#login-options').hide();
$('#postcodeForm').hide();
- $('#relocate').hide();
+ $('#relocate, #hidepins').addClass("nodisplay");
$('#cancel').hide();
$('#map_box').removeClass('background-map');
this.fixPageHeight();
@@ -101,7 +102,7 @@
},
gotLocation: function( info ) {
- $('#relocate').show();
+ $('#relocate, #hidepins').removeClass("nodisplay");
this.finishedLocating();
this.listenTo(FMS.locator, 'gps_current_position', this.positionUpdate);
@@ -121,7 +122,7 @@
positionUpdate: function( info ) {
if ( $('#front-howto').is(':hidden') ) {
- $('#relocate').show();
+ $('#relocate, #hidepins').removeClass("nodisplay");
}
FMS.currentPosition = info.coordinates;
var centre = this.projectCoords( info.coordinates );
@@ -182,7 +183,7 @@
msg = FMS.strings.location_problem;
}
if ( !fixmystreet.map ) {
- $('#relocate').hide();
+ $('#relocate, #hidepins').addClass("nodisplay");
$('#mark-here').hide();
// if we are going to display the help then we don't want to focus on
// the search box as it will show through the help
@@ -197,7 +198,7 @@
},
displayHelpIfFirstTime: function() {
- if ( !FMS.usedBefore ) {
+ if ( !FMS.usedBefore && !CONFIG.HELP_DISABLED ) {
FMS.helpShow();
}
},
@@ -267,7 +268,6 @@
onClickCancel: function(e) {
e.preventDefault();
- fixmystreet.markers.removeAllFeatures();
fixmystreet_activate_drag();
// force pins to be refetched and displayed
fixmystreet.bbox_strategy.update({force: true});
@@ -352,7 +352,7 @@
},
goAddress: function(e) {
- $('#relocate').show();
+ $('#relocate, #hidepins').removeClass("nodisplay");
$('#front-howto').html('').hide();
var t = $(e.target);
var lat = t.attr('data-lat');
@@ -371,7 +371,7 @@
$('#pc').attr('placeholder', msg).addClass('error');;
} else {
$('#front-howto').html(msg);
- $('#relocate').hide();
+ $('#relocate, #hidepins').addClass("nodisplay");
$('#front-howto').show();
}
},
@@ -380,7 +380,7 @@
$('#pc').attr('placeholder', this.origPcPlaceholder).removeClass('error');;
if ( fixmystreet.map ) {
$('#front-howto').hide();
- $('#relocate').show();
+ $('#relocate, #hidepins').removeClass("nodisplay");
}
},
@@ -400,7 +400,7 @@
}
$('#front-howto').html('<p>' + FMS.strings.multiple_matches + '</p><ul data-role="listview" data-inset="true">' + multiple + '</ul>');
$('.ui-page').trigger('create');
- $('#relocate').hide();
+ $('#relocate, #hidepins').addClass("nodisplay");
$('#front-howto').show();
} else {
this.searchError( FMS.strings.location_problem );
@@ -410,9 +410,7 @@
pauseMap: function() {
this.stopListening(FMS.locator);
FMS.locator.stopTracking();
- if ( FMS.iPhoneModel > 3 ) {
- $('#map_box').addClass('background-map');
- }
+ $('#map_box').addClass('background-map');
$('#map_box').off('touchend');
if ( fixmystreet.map ) {
fixmystreet.nav.deactivate();
@@ -442,7 +440,7 @@
e.preventDefault();
if ( !fixmystreet.map ) {
this.$('#mark-here').hide();
- this.$('#relocate').hide();
+ this.$('#relocate, #hidepins').addClass("nodisplay");
$('#front-howto').html('<p>' + FMS.strings.locate_dismissed + '</p>');
$('#front-howto').show();
}
@@ -497,6 +495,11 @@
);
return centre;
+ },
+
+ toggleMarkersVisibility: function(e) {
+ e.preventDefault();
+ fixmystreet.markers.setVisibility(!fixmystreet.markers.getVisibility());
}
})
});
diff --git a/www/js/views/details.js b/www/js/views/details.js
index 9f03d58..84b6ac2 100644
--- a/www/js/views/details.js
+++ b/www/js/views/details.js
@@ -18,6 +18,16 @@
'blur input': 'updateCurrentReport'
},
+ initialize: function() {
+ var that = this;
+ window.addEventListener('native.keyboardshow', function(e) {
+ that.fixDetailTextAreaHeight(e.keyboardHeight);
+ });
+ window.addEventListener('native.keyboardhide', function(e) {
+ that.fixDetailTextAreaHeight();
+ });
+ },
+
afterRender: function() {
this.$('#form_category').attr('data-role', 'none');
@@ -28,13 +38,18 @@
},
- beforeDisplay: function() {
- this.fixPageHeight();
+ beforeDisplay: function(extra) {
+ this.fixDetailTextAreaHeight();
+ },
+
+ fixDetailTextAreaHeight: function(extra) {
+ extra = extra || 0;
+ this.fixPageHeight(extra);
var header = this.$("div[data-role='header']:visible"),
detail = this.$('#form_detail'),
top = detail.position().top,
viewHeight = $(window).height(),
- contentHeight = viewHeight - header.outerHeight() + 15;
+ contentHeight = viewHeight - header.outerHeight() + 15 - extra;
detail.height( contentHeight - top );
},
@@ -87,8 +102,18 @@
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');
+ // Some categories have only hidden fields - in that case we
+ // don't want to navigate to the details_extra view.
+ var all_hidden = data.category_extra_json.reduce(function(accumulator, field) {
+ return accumulator && (field.automated === "hidden_field");
+ }, true);
+
+ if (all_hidden) {
+ that.navigate( that.next );
+ } else {
+ that.model.set('category_extras', data.category_extra);
+ that.navigate('details_extra');
+ }
} else {
that.navigate( that.next );
}
diff --git a/www/js/views/details_extra.js b/www/js/views/details_extra.js
index 160ff11..e5f63ba 100644
--- a/www/js/views/details_extra.js
+++ b/www/js/views/details_extra.js
@@ -19,15 +19,19 @@
afterRender: function() {
this.populateFields();
+ this.enableScrolling();
},
- onClickButtonPrev: function() {
+ onClickButtonPrev: function(e) {
+ e.preventDefault();
+ this.disableScrolling();
this.model.set('hasExtras', 0);
this.updateCurrentReport();
this.navigate( this.prev, true );
},
onClickButtonNext: function() {
+ this.disableScrolling();
this.clearValidationErrors();
var valid = 1;
var that = this;
@@ -49,6 +53,8 @@
this.clearValidationErrors();
this.updateCurrentReport();
this.navigate( this.next );
+ } else {
+ this.enableScrolling();
}
},
diff --git a/www/js/views/fms.js b/www/js/views/fms.js
index 6ff569b..ae1174f 100644
--- a/www/js/views/fms.js
+++ b/www/js/views/fms.js
@@ -2,7 +2,7 @@
_.extend( FMS, {
FMSView: Backbone.View.extend({
tag: 'div',
- bottomMargin: 20,
+ bottomMargin: 0,
contentSelector: '[data-role="content"]',
events: {
@@ -45,12 +45,21 @@
return this;
},
- fixPageHeight: function() {
+ fixPageHeight: function(extra) {
+ extra = extra || 0;
var header = this.$("div[data-role='header']:visible"),
content = this.$(this.contentSelector),
top = content.position().top,
viewHeight = $(window).height(),
- contentHeight = FMS.windowHeight - header.outerHeight() - this.bottomMargin;
+ contentHeight = FMS.windowHeight - header.outerHeight() - this.bottomMargin - extra;
+
+ if ($("body").hasClass("iphone-x")) {
+ var body = $("body").get(0);
+ var inset = window.getComputedStyle(body).getPropertyValue("--safe-area-inset-bottom");
+ // We want the pixel value, not the CSS string
+ inset = parseInt(inset.replace(/[^\d]*/g, ''));
+ contentHeight -= inset;
+ }
this.setHeight( content, contentHeight - top );
},
@@ -107,7 +116,20 @@
$('.form-error').removeClass('form-error');
},
- destroy: function() { FMS.printDebug('destory for ' + this.id); this._destroy(); this.remove(); },
+ disableScrolling: function() {
+ if ( typeof cordova !== 'undefined' ) {
+ cordova.plugins.Keyboard.disableScroll(true);
+ $('body').scrollTop(0);
+ }
+ },
+
+ enableScrolling: function() {
+ if ( typeof cordova !== 'undefined' ) {
+ cordova.plugins.Keyboard.disableScroll(false);
+ }
+ },
+
+ destroy: function() { FMS.printDebug('destroy for ' + this.id); this._destroy(); this.remove(); },
_destroy: function() {}
})
diff --git a/www/js/views/home.js b/www/js/views/home.js
index 998af1c..d82b874 100644
--- a/www/js/views/home.js
+++ b/www/js/views/home.js
@@ -24,11 +24,14 @@
afterDisplay: function() {
$('#load-screen').hide();
+
if ( FMS.isOffline ) {
this.navigate( 'offline' );
+ } else if ( !FMS.isLoggedIn && CONFIG.LOGIN_REQUIRED ) {
+ this.navigate( 'login' );
} else if ( FMS.currentDraft && (
FMS.currentDraft.get('title') || FMS.currentDraft.get('lat') ||
- FMS.currentDraft.get('details') || FMS.currentDraft.get('file') )
+ FMS.currentDraft.get('details') || FMS.currentDraft.get('files').length > 0 )
) {
this.navigate( 'existing' );
} else {
diff --git a/www/js/views/login.js b/www/js/views/login.js
index c0f16ba..9225c18 100644
--- a/www/js/views/login.js
+++ b/www/js/views/login.js
@@ -40,8 +40,7 @@
that.model.set('name', data.name);
that.model.save();
FMS.isLoggedIn = 1;
- that.$('#password_row').hide();
- that.$('#success_row').show();
+ that.rerender();
} else {
that.validationError('signinForm', FMS.strings.login_details_error);
}
@@ -65,11 +64,7 @@
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();
+ that.rerender();
},
error: function() {
that.validationError('err', FMS.strings.logout_error);
@@ -83,7 +78,7 @@
if ( !$('#form_password').val() ) {
isValid = 0;
- this.validationError('form_password', FMS.validationStrings.password );
+ this.validationError('form_password', FMS.validationStrings.password.required);
}
var email = $('#form_email').val();
@@ -102,6 +97,23 @@
}
return isValid;
+ },
+
+ beforeDisplay: function() {
+ this.fixPageHeight();
+ if ( !FMS.isLoggedIn && CONFIG.LOGIN_REQUIRED ) {
+ this.$("#reports-next-btn").hide();
+ }
+ },
+
+ rerender: function() {
+ // Simply calling this.render() breaks the DOM in a weird and
+ // interesting way - somehow the main view element is duplicated
+ // instead of replaced and none of the event handlers are
+ // hooked up so you end up with a blank screen.
+ // This is a convenience wrapper around the correct router call
+ // which works around the problem.
+ FMS.router.login();
}
})
});
diff --git a/www/js/views/offline.js b/www/js/views/offline.js
index ac007d1..3ce94a9 100644
--- a/www/js/views/offline.js
+++ b/www/js/views/offline.js
@@ -9,17 +9,18 @@
events: {
'pagehide': 'destroy',
- 'pagebeforeshow': 'beforeShow',
+ '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',
+ 'vclick .del_photo_button': 'deletePhoto',
'vclick #locate': 'onClickLocate',
'vclick #locate_cancel': 'onClickCancel',
'blur input': 'toggleNextButton',
- 'blur textarea': 'toggleNextButton'
+ 'blur textarea': 'blurTextArea',
+ 'focus textarea': 'focusTextArea'
},
_back: function() {
@@ -30,7 +31,7 @@
var hasContent = false;
if ( $('#form_title').val() || $('#form_detail').val() ||
- this.model.get('lat') || this.model.get('file') ) {
+ this.model.get('lat') || this.model.get('files').length > 0 ) {
hasContent = true;
}
@@ -39,11 +40,22 @@
afterDisplay: function() {
$('body')[0].scrollTop = 0;
- $('div[data-role="content"]').show();
- },
- beforeShow: function() {
- $('div[data-role="content"]').hide();
+ // The height of the photos container needs to be adjusted
+ // depending on the number of photos - if the max number of
+ // photos have already been added then the 'add photo' UI isn't
+ // shown so we should use all the vertical space for the
+ // thumbnails.
+ var wrapperHeight = $(".ui-content").height();
+ wrapperHeight -= $(".ui-content .notopmargin").outerHeight(true);
+ wrapperHeight -= $(".ui-content #locate_result").outerHeight(true);
+ wrapperHeight -= $(".ui-content .inputcard").outerHeight(true);
+ wrapperHeight -= $(".ui-content #add_photo").outerHeight(true);
+ $(".photo-wrapper").height(wrapperHeight);
+ },
+
+ beforeDisplay: function() {
+ this.fixPageHeight();
this.toggleNextButton();
},
@@ -55,6 +67,15 @@
}
},
+ focusTextArea: function() {
+ $("textarea#form_detail").get(0).rows = 7;
+ },
+
+ blurTextArea: function(e) {
+ $("textarea#form_detail").get(0).rows = 2;
+ this.toggleNextButton();
+ },
+
failedLocation: function(details) {
this.finishedLocating();
this.locateCount = 21;
@@ -73,32 +94,47 @@
takePhoto: function() {
var that = this;
+ $.mobile.loading('show');
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;
+ $.mobile.loading('show');
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 move;
+ // on iOS the photos go into a temp folder in the apps own filespace so we
+ // can move them, and indeed have to as the tmp space is cleaned out by the OS
+ // so draft reports might have their images removed. on android you access the
+ // images where they are stored on the filesystem so if you move, and then delete
+ // them, you are moving and deleting the only copy of them which is likely to be
+ // surprising and unwelcome so we copy them instead.
+ var fileName = CONFIG.NAMESPACE + '_' + this.model.cid + '_' + moment().unix() + '.jpg';
+ if ( FMS.isAndroid ) {
+ move = FMS.files.copyURI( imgURI, fileName );
+ } else {
+ move = FMS.files.moveURI( imgURI, fileName );
+ }
var that = this;
move.done( function( file ) {
- $('#photo').attr('src', file.toURL());
- that.model.set('file', file.toURL());
- FMS.saveCurrentDraft();
+ var files = that.model.get('files');
+ files.push(file.toURL());
+ that.model.set('files', files);
+ that.updateCurrentReport();
- $('#photo-next-btn .ui-btn-text').text(FMS.strings.next);
- $('#display_photo').show();
- $('#add_photo').hide();
+ $.mobile.loading('hide');
+ that.rerender();
});
move.fail( function() { that.addPhotoFail(); } );
},
addPhotoFail: function(message) {
+ $.mobile.loading('hide');
if ( message != 'no image selected' &&
message != 'Selection cancelled.' &&
message != 'Camera cancelled.' ) {
@@ -106,18 +142,22 @@
}
},
- deletePhoto: function() {
- var that = this;
- var del = FMS.files.deleteURI( this.model.get('file') );
+ deletePhoto: function(e) {
+ e.preventDefault();
+ $.mobile.loading('show');
+ var files = this.model.get('files');
+ var index = parseInt($(e.target).data('fileIndex'));
+ var deleted_file = files.splice(index, 1)[0];
+ var del = FMS.files.deleteURI( deleted_file );
+
+ var that = this;
del.done( function() {
- that.model.set('file', '');
- FMS.saveCurrentDraft();
- $('#photo').attr('src', '');
+ that.model.set('files', files);
+ that.updateCurrentReport();
- $('#photo-next-btn .ui-btn-text').text(FMS.strings.skip);
- $('#display_photo').hide();
- $('#add_photo').show();
+ $.mobile.loading('hide');
+ that.rerender();
});
},
@@ -154,7 +194,12 @@
this.model.set('title', $('#form_title').val());
this.model.set('details', $('#form_detail').val());
FMS.saveCurrentDraft();
+ },
+
+ rerender: function() {
+ FMS.router.offline();
}
+
})
});
})(FMS, Backbone, _, $);
diff --git a/www/js/views/photo.js b/www/js/views/photo.js
index 485b74c..0b0c5e9 100644
--- a/www/js/views/photo.js
+++ b/www/js/views/photo.js
@@ -14,17 +14,23 @@
'vclick .ui-btn-right': 'onClickButtonNext',
'vclick #id_photo_button': 'takePhoto',
'vclick #id_existing': 'addPhoto',
- 'vclick #id_del_photo_button': 'deletePhoto'
+ 'vclick .del_photo_button': 'deletePhoto'
},
beforeDisplay: function() {
this.fixPageHeight();
- this.$('#id_del_photo_button').hide();
- if ( this.model.get('file') ) {
- $('#id_photo_button').parents('.ui-btn').hide();
- $('#id_existing').parents('.ui-btn').hide();
- window.setTimeout( function() { $('#id_del_photo_button').show(); }, 250 );
- }
+ },
+
+ afterDisplay: function() {
+ // The height of the photos container needs to be adjusted
+ // depending on the number of photos - if the max number of
+ // photos have already been added then the 'add photo' UI isn't
+ // shown so we should use all the vertical space for the
+ // thumbnails.
+ var wrapperHeight = $(".ui-content").height();
+ wrapperHeight -= $(".ui-content h2").outerHeight(true);
+ wrapperHeight -= $(".ui-content .bottom-btn").outerHeight(true)
+ $(".photo-wrapper").height(wrapperHeight);
},
getOptions: function(isFromAlbum) {
@@ -52,7 +58,7 @@
takePhoto: function(e) {
e.preventDefault();
$.mobile.loading('show');
- $('#photo').hide();
+ $('.photo-wrapper .photo img').hide();
var that = this;
var options = this.getOptions();
@@ -63,7 +69,7 @@
addPhoto: function(e) {
e.preventDefault();
$.mobile.loading('show');
- $('#photo').hide();
+ $('.photo-wrapper .photo img').hide();
var that = this;
var options = this.getOptions(true);
navigator.camera.getPicture( function(imgURI) { that.addPhotoSuccess(imgURI); }, function(error) { that.addPhotoFail(error); }, options);
@@ -86,25 +92,19 @@
var that = this;
move.done( function( file ) {
- $('#nophoto_title').hide();
- $('#photo_title').html(FMS.strings.photo_added).show();
- $('#photo').attr('src', file.toURL()).addClass('small').removeClass('placeholder');
- that.model.set('file', file.toURL());
+ var files = that.model.get('files');
+ files.push(file.toURL());
+ that.model.set('files', files);
FMS.saveCurrentDraft();
-
- $('#photo-next-btn .ui-btn-text').text(FMS.strings.next);
- $('#id_photo_button').parents('.ui-btn').hide();
- $('#id_existing').parents('.ui-btn').hide();
- $('#photo').show();
- window.setTimeout(function() { $('#id_del_photo_button').show() }, 500);
- window.setTimeout(function() { $.mobile.loading('hide') }, 100);
+ $.mobile.loading('hide');
+ that.rerender();
});
move.fail( function() { that.addPhotoFail(); } );
},
addPhotoFail: function(message) {
- $('#photo').show();
+ $('.photo-wrapper .photo img').show();
$.mobile.loading('hide');
if ( message != 'no image selected' &&
message != 'Selection cancelled.' &&
@@ -115,22 +115,25 @@
deletePhoto: function(e) {
e.preventDefault();
- var that = this;
- var del = FMS.files.deleteURI( this.model.get('file') );
+ var files = this.model.get('files');
+ var index = parseInt($(e.target).data('fileIndex'));
+ var deleted_file = files.splice(index, 1)[0];
+ var del = FMS.files.deleteURI( deleted_file );
+
+ var that = this;
del.done( function() {
- $('#photo_title').hide();
- $('#nophoto_title').show();
- $('#id_del_photo_button').hide();
- that.model.set('file', '');
+ that.model.set('files', files);
FMS.saveCurrentDraft(true);
- $('#photo').attr('src', 'images/placeholder-photo.png').addClass('placeholder').removeClass('small');
-
- $('#photo-next-btn .ui-btn-text').text(FMS.strings.skip);
- $('#id_photo_button').parents('.ui-btn').show();
- $('#id_existing').parents('.ui-btn').show();
+ that.rerender();
});
+ },
+ rerender: function() {
+ // Simply calling this.render() breaks the DOM in a weird and
+ // interesting way, so this is a convenience wrapper around
+ // the correct router call.
+ FMS.router.photo();
}
})
});
diff --git a/www/js/views/submit.js b/www/js/views/submit.js
index 6a7c946..4a92fe3 100644
--- a/www/js/views/submit.js
+++ b/www/js/views/submit.js
@@ -86,7 +86,7 @@
if ( !this._handleInvalid( model, err, options ) ) {
var errors = err.errors;
var errorList = '<ul><li class="plain">' + FMS.strings.invalid_report + '</li>';
- var validErrors = [ 'password', 'category', 'name' ];
+ var validErrors = [ 'password', 'password_register', 'category', 'name' ];
for ( var k in errors ) {
if ( validErrors.indexOf(k) >= 0 || errors[k].match(/required/) ) {
if ( k === 'password' ) {
@@ -128,6 +128,22 @@
}
},
+ validateUserTitle: function() {
+ if ( this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
+ if ( $('#form_title').val() === '' ) {
+ this.validationError('form_title', FMS.strings.required);
+ return false;
+ }
+ }
+ return true;
+ },
+
+ setUserTitle: function() {
+ if ( this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
+ FMS.currentUser.set('title', $('#form_title').val());
+ }
+ },
+
beforeSubmit: function() {},
afterSubmit: function() {},
@@ -145,7 +161,8 @@
(function (FMS, Backbone, _, $) {
_.extend( FMS, {
SubmitInitialPageView: FMS.SubmitView.extend({
- onClickButtonPrev: function() {
+ onClickButtonPrev: function(e) {
+ e.preventDefault();
if ( this.model.get('hasExtras') == 1 ) {
this.navigate( 'details_extra', true );
} else {
@@ -264,11 +281,8 @@
}
}
- if ( this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
- if ( $('#form_title').val() === '' ) {
- this.validationError('form_title', FMS.strings.required);
- isValid = 0;
- }
+ if (!this.validateUserTitle()) {
+ isValid = 0;
}
return isValid;
@@ -282,10 +296,7 @@
this.model.set('may_show_name', $('#form_may_show_name').is(':checked'));
FMS.currentUser.set('name', $('#form_name').val());
FMS.currentUser.set('may_show_name', $('#form_may_show_name').is(':checked'));
-
- if ( this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
- FMS.currentUser.set('title', $('#form_title').val());
- }
+ this.setUserTitle();
if ( FMS.currentUser ) {
FMS.currentUser.save();
@@ -329,20 +340,21 @@
},
validate: function() {
+ this.clearValidationErrors();
var isValid = 1;
if ( !$('#form_password').val() ) {
isValid = 0;
- this.validationError('form_password', FMS.validationStrings.password );
+ this.validationError('form_password', FMS.validationStrings.password.required );
+ } else if ( CONFIG.PASSWORD_MIN_LENGTH && $('#form_password').val().length < CONFIG.PASSWORD_MIN_LENGTH ) {
+ isValid = 0;
+ var msg = FMS.validationStrings.password.short.replace('%d', CONFIG.PASSWORD_MIN_LENGTH);
+ this.validationError('form_password', msg);
}
- if ( $('#form_name').val() && this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
- if ( $('#form_title').val() === '' ) {
- this.validationError('form_title', FMS.strings.required);
- isValid = 0;
- }
+ if ($('#form_name').val() && !this.validateUserTitle()) {
+ isValid = 0;
}
-
return isValid;
},
@@ -357,9 +369,7 @@
this.model.set('may_show_name', $('#form_may_show_name').is(':checked'));
FMS.currentUser.set('name', $('#form_name').val());
FMS.currentUser.set('may_show_name', $('#form_may_show_name').is(':checked'));
- if ( this.model.get('titles_list') && this.model.get('titles_list').length > 0 ) {
- FMS.currentUser.set('title', $('#form_title').val());
- }
+ this.setUserTitle();
FMS.currentUser.save();
} else {
// if this is set then we are registering a password
@@ -408,14 +418,43 @@
onClickContinue: function(e) {
e.preventDefault();
- if ( this.validate() ) {
- $('#continue').focus();
- if ( ! this.model.get('submit_clicked') ) {
- this.model.set('submit_clicked', 'submit_sign_in');
+ if (this.validate()) {
+ // The password may be long enough, but is it going to be
+ // accepted by the server? Check before proceeding.
+ if (CONFIG.PASSWORD_CHECK_COMMON) {
+ var that = this;
+ $.post(
+ CONFIG.FMS_URL + "/auth/common_password",
+ { password_register: $('#form_password').val() },
+ null,
+ 'json'
+ )
+ .done(function(result) {
+ if (result === true) {
+ that.savePasswordAndContinue();
+ } else {
+ that.validationError('form_password', result);
+ }
+ })
+ .fail(function() {
+ // If this failed for whatever reason (e.g. network
+ // error etc), don't worry about it as it'll be
+ // resubmitted with the report.
+ that.savePasswordAndContinue();
+ });
+ } else {
+ this.savePasswordAndContinue();
}
- FMS.currentUser.set('password', $('#form_password').val());
- this.navigate( this.next );
}
+ },
+
+ savePasswordAndContinue: function() {
+ $('#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 );
}
})
});
@@ -462,18 +501,33 @@
this.model.set('submit_clicked', 'submit_register');
FMS.currentUser.set('name', $('#form_name').val());
FMS.currentUser.set('may_show_name', $('#form_may_show_name').is(':checked'));
+ this.setUserTitle();
},
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();
+ this.doSubmit();
} else {
if ( err.errors && err.errors.password ) {
this.validationError('form_password', err.errors.password );
}
}
+ },
+
+ afterRender: function() {
+ console.log("SubmitConfirmView.afterRender");
+ if (CONFIG.SKIP_CONFIRM_REPORT) {
+ var that = this;
+ setTimeout(function() {
+ // This needs to be in a setTimeout call otherwise
+ // the app gets stuck on an empty "Your Details" page.
+ // This is something to do with the way Backbone routes
+ // between views, I believe.
+ that.doSubmit();
+ }, 10);
+ }
}
})
});
diff --git a/www/templates/en/around.html b/www/templates/en/around.html
index 7c64fe5..8328a95 100644
--- a/www/templates/en/around.html
+++ b/www/templates/en/around.html
@@ -33,6 +33,7 @@
<% } %>
</form>
<a data-role="none" id="relocate" class="nodisplay"></a>
+ <a data-role="none" id="hidepins" class="nodisplay"></a>
<div class="nodisplay" id="front-howto"></div>
</div>
diff --git a/www/templates/en/details_extra.html b/www/templates/en/details_extra.html
index 339cc9d..c4390b4 100644
--- a/www/templates/en/details_extra.html
+++ b/www/templates/en/details_extra.html
@@ -1,7 +1,11 @@
<div data-role="header" data-position="fixed">
<a data-rel="back" data-icon="arrow-l" class="ui-btn-left">Back</a>
<h1>Further Details</h1>
- <a id="next" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Next</a>
+ <% if (CONFIG.SKIP_CONFIRM_REPORT) { %>
+ <a id="next" class="ui-btn-right">Submit</a>
+ <% } else { %>
+ <a id="next" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Next</a>
+ <% } %>
</div>
<div data-role="content" data-enhance="false">
<%= category_extras %>
diff --git a/www/templates/en/existing.html b/www/templates/en/existing.html
index 5e27785..c0a90ec 100644
--- a/www/templates/en/existing.html
+++ b/www/templates/en/existing.html
@@ -17,8 +17,8 @@
<p><% print( moment( created ).fromNow() ) %></p>
</div>
- <% if ( file ) { %>
- <div class="photo" style="background-image: url(<%= file %>)"></div>
+ <% if ( files.length > 0 ) { %>
+ <div class="photo" style="background-image: url(<%= files[0] %>)"></div>
<% } %>
<% if ( details ) { %>
diff --git a/www/templates/en/help.html b/www/templates/en/help.html
index 3722544..2917c67 100644
--- a/www/templates/en/help.html
+++ b/www/templates/en/help.html
@@ -40,7 +40,7 @@
<dt>Is it free?</dt>
<dd>The site and app are free to use, yes. <% print( CONFIG.APP_NAME ) %> is run
by a registered charity, though, so if you want to make a contribution, <a
- onclick="FMS.openExternal(event); return false;" href="https://secure.mysociety.org/donate/">please do</a>.
+ onclick="FMS.openExternal(event); return false;" href="https://www.mysociety.org/donate/">please do</a>.
For more information see the <a onclick="FMS.openExternal(event); return false;" href="http://www.fixmystreet.com/faq/">website</a>.
</dd>
diff --git a/www/templates/en/login.html b/www/templates/en/login.html
index 13944e8..bbe6267 100644
--- a/www/templates/en/login.html
+++ b/www/templates/en/login.html
@@ -12,13 +12,9 @@
<input type="button" id="logout" name="logout" value="Sign Out" data-theme="a">
</div>
</div>
- <div id="password_row" class="nodisplay">
- <p class="notopmargin">
- Signed out!
- </p>
<% } else { %>
<div id="password_row">
- <% } %>
+ <p class="notopmargin">You are not signed in.</p>
<form name="signinForm" class="inputcard" id="signinForm">
<div>
<input data-role="none" type="email" value="" name="email" id="form_email" placeholder="Your Email" required>
@@ -31,13 +27,5 @@
</div>
</form>
</div>
- <div id="success_row" class="nodisplay">
- <p class="notopmargin">
- Signed in!
- </p>
- <div id="err" class="nodisplay"></div>
- <div class="bottom-btn">
- <input type="button" id="logout" name="logout" value="Sign Out" data-theme="a">
- </div>
- </div>
+ <% } %>
</div>
diff --git a/www/templates/en/offline.html b/www/templates/en/offline.html
index 10e87d9..4fde64f 100644
--- a/www/templates/en/offline.html
+++ b/www/templates/en/offline.html
@@ -1,7 +1,7 @@
<div data-role="header" data-position="fixed">
<a id="offline-prev-btn" class="ui-btn-left">Try Again</a>
<h1>Offline</h1>
- <a id="offline-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Skip</a>
+ <a id="offline-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right"><%= files.length == 0 ? "Skip" : "Next" %></a>
</div>
<div id="locating" class="nodisplay">
<div class="radar"></div>
@@ -42,30 +42,25 @@
</div>
<div>
- <textarea data-role="none" rows="7" cols="26" name="detail" id="form_detail" placeholder="Please fill in details of the problem." required><%= details %></textarea>
+ <textarea data-role="none" rows="2" cols="26" name="detail" id="form_detail" placeholder="Please fill in details of the problem." required><%= details %></textarea>
</div>
</div>
- <% if ( file != '' ) { %>
- <div id="add_photo" style="display: none">
- <% } else { %>
- <div id="add_photo">
+ <% if ( files.length < CONFIG.MAX_PHOTOS ) { %>
+ <div id="add_photo">
+ <input data-icon="fms-photo-new" value="Take a new photo" type="button" name="photo_button" id="id_photo_button" data-role="button" data-theme="a">
+ <input data-icon="fms-photo-existing" value="Add an existing photo" type="button" name="existing" id="id_existing" data-role="button" data-theme="c">
+ </div>
<% } %>
- <label>Add a Photo <em>(optional)</em></label>
- <input data-icon="fms-photo-new" value="Take a new photo" type="button" name="photo_button" id="id_photo_button" data-role="button" data-theme="a">
- <input data-icon="fms-photo-existing" value="Add an existing photo" type="button" name="existing" id="id_existing" data-role="button" data-theme="c">
- </div>
- <% if ( file == '' ) { %>
- <div id="display_photo" style="display: none">
- <% } else { %>
- <div id="display_photo">
- <% } %>
- <label>Your Photo</label>
- <div class="photo">
- <img class="small" id="photo" src="<%= file %>" />
+ <% if ( files.length > 0 ) { %>
+ <div class="photo-wrapper">
+ <% _.each(files, function(file, index) { %>
+ <div class="photo" style="background-image: url('<%= file %>');">
+ <a data-role="none" class="photo-corner-button del_photo_button" data-file-index="<%= index %>">X</a>
+ </div>
+ <% }); %>
</div>
- <input value="Remove Photo" type="button" name="del_photo_button" id="id_del_photo_button" data-role="button" data-theme="a">
- </div>
+ <% } %>
</div>
</div>
diff --git a/www/templates/en/photo.html b/www/templates/en/photo.html
index 8dac456..3221192 100644
--- a/www/templates/en/photo.html
+++ b/www/templates/en/photo.html
@@ -1,37 +1,49 @@
<div data-role="header">
<a data-rel="back" data-icon="arrow-l" class="ui-btn-left">Back</a>
- <h1>Add Photo</h1>
- <% if ( file == '' ) { %>
- <a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Skip</a>
+ <h1>Add Photos</h1>
+ <% if ( files.length == 0 ) { %>
+ <% if ( !CONFIG.PHOTO_REQUIRED ) { %>
+ <a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Skip</a>
+ <% } %>
<% } else { %>
<a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Next</a>
<% } %>
</div>
+
<div data-role="content">
- <% var photoClass = 'nodisplay', nophotoClass = 'nodisplay';
- if ( file != '' ) {
- photoClass = '';
- } else {
- nophotoClass = '';
- } %>
- <h2 id="nophoto_title" class="<% print( nophotoClass ); %>">
- Add a Photo <em>(optional)</em>
- </h2>
- <h2 id="photo_title" class="<% print( photoClass ); %>">
- Photo for report
- </h2>
- <div class="photo-wrapper">
- <div class="photo">
- <% if ( file == '' ) { %>
- <img class="placeholder" src="images/placeholder-photo.png" alt="" id="photo">
+ <% if ( files.length == 0 ) { %>
+ <h2 id="nophoto_title">
+ Add a Photo
+ <% if ( !CONFIG.PHOTO_REQUIRED ) { %>
+ <em>(optional)</em>
+ <% } else if ( files.length == 0) { %>
+ <em>(required)</em>
+ <%} %>
+ </h2>
<% } else { %>
- <img class="small" id="photo" src="<%= file %>" />
+ <h2 id="photo_title">
+ Photos for report
+ </h2>
<% } %>
- <a data-role="none" id="id_del_photo_button" class="photo-corner-button">X</a>
- </div>
- </div>
- <div class="bottom-btn">
- <input data-icon="fms-photo-existing" type="button" id="id_existing" data-theme="c" value="Add an existing photo" >
- <input data-icon="fms-photo-new" id="id_photo_button" type="button" data-theme="a" value="Take a new photo">
+
+ <div class="photo-wrapper">
+ <% if ( files.length == 0 ) { %>
+ <div class="photo">
+ <img class="placeholder" src="images/placeholder-photo.png" alt="">
+ </div>
+ <% } else { %>
+ <% _.each(files, function(file, index) { %>
+ <div class="photo" style="background-image: url('<%= file %>');">
+ <a data-role="none" class="photo-corner-button del_photo_button" data-file-index="<%= index %>">X</a>
+ </div>
+ <% });%>
+ <% } %>
</div>
+
+ <% if ( files.length < CONFIG.MAX_PHOTOS ) { %>
+ <div class="bottom-btn">
+ <input data-icon="fms-photo-existing" type="button" id="id_existing" data-theme="c" value="Add an existing photo" >
+ <input data-icon="fms-photo-new" id="id_photo_button" type="button" data-theme="a" value="Take a new photo">
+ </div>
+ <% } %>
</div>
diff --git a/www/templates/en/reports.html b/www/templates/en/reports.html
index 047fa76..e04f5f6 100644
--- a/www/templates/en/reports.html
+++ b/www/templates/en/reports.html
@@ -15,7 +15,7 @@
if ( draft.get('lat') ) {
classes.push('with_location');
}
- if ( draft.get('file') ) {
+ if ( draft.get('files').length > 0 ) {
classes.push('with_photo');
}
var draftClass = '';
diff --git a/www/templates/en/submit_confirm.html b/www/templates/en/submit_confirm.html
index 5d2e6a0..f41e71c 100644
--- a/www/templates/en/submit_confirm.html
+++ b/www/templates/en/submit_confirm.html
@@ -3,7 +3,7 @@
<h1>Your details</h1>
</div>
-<div data-role="content">
+<div data-role="content"<% if (CONFIG.SKIP_CONFIRM_REPORT) { %> class="hidden"<% } %>>
<p class="notopmargin">
You are logged in as <%= user.email %>.
</p>
diff --git a/www/templates/en/submit_set_password.html b/www/templates/en/submit_set_password.html
index 1a24eae..75baf7a 100644
--- a/www/templates/en/submit_set_password.html
+++ b/www/templates/en/submit_set_password.html
@@ -12,7 +12,7 @@
</p>
<div class="inputcard">
<div>
- <input data-role="none" type="password" value="" name="password" id="form_password" placeholder="Your password" required>
+ <input data-role="none" type="password" value="<%= FMS.currentUser.get('password') %>" name="password" id="form_password" placeholder="Your password" required>
</div>
</div>
<div class="noborder">
diff --git a/www/templates/es/around.html b/www/templates/es/around.html
index 8686933..5f47ed4 100644
--- a/www/templates/es/around.html
+++ b/www/templates/es/around.html
@@ -33,6 +33,7 @@
<% } %>
</form>
<a data-role="none" id="relocate" class="nodisplay"></a>
+ <a data-role="none" id="hidepins" class="nodisplay"></a>
<div class="nodisplay" id="front-howto"></div>
</div>
diff --git a/www/templates/es/details_extra.html b/www/templates/es/details_extra.html
index 2d782c8..0f302fe 100644
--- a/www/templates/es/details_extra.html
+++ b/www/templates/es/details_extra.html
@@ -1,7 +1,11 @@
<div data-role="header" data-position="fixed">
<a data-rel="back" data-icon="arrow-l" class="ui-btn-left">Atrás</a>
<h1>Detalles adicionales</h1>
- <a id="next" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Siguiente</a>
+ <% if (CONFIG.SKIP_CONFIRM_REPORT) { %>
+ <a id="next" class="ui-btn-right">Enviar</a>
+ <% } else { %>
+ <a id="next" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Siguiente</a>
+ <% } %>
</div>
<div data-role="content" data-enhance="false">
<%= category_extras %>
diff --git a/www/templates/es/existing.html b/www/templates/es/existing.html
index b45b3d8..baecd89 100644
--- a/www/templates/es/existing.html
+++ b/www/templates/es/existing.html
@@ -17,8 +17,8 @@
<p><% print( moment( created ).fromNow() ) %></p>
</div>
- <% if ( file ) { %>
- <div class="photo" style="background-image: url(<%= file %>)"></div>
+ <% if ( files.length > 0 ) { %>
+ <div class="photo" style="background-image: url(<%= files[0] %>)"></div>
<% } %>
<% if ( details ) { %>
diff --git a/www/templates/es/login.html b/www/templates/es/login.html
index 4dff7a6..3fcaa99 100644
--- a/www/templates/es/login.html
+++ b/www/templates/es/login.html
@@ -12,13 +12,9 @@
<input type="button" id="logout" name="logout" value="Cerrar sesión" data-theme="a">
</div>
</div>
- <div id="password_row" class="nodisplay">
- <p class="notopmargin">
- ¡Sesión cerrada!
- </p>
<% } else { %>
<div id="password_row">
- <% } %>
+ <p class="notopmargin">You are not signed in.</p>
<form name="signinForm" class="inputcard" id="signinForm">
<div>
<input data-role="none" type="email" value="" name="email" id="form_email" placeholder="Su email" required>
@@ -31,13 +27,5 @@
</div>
</form>
</div>
- <div id="success_row" class="nodisplay">
- <p class="notopmargin">
- ¡Sesión abierta!
- </p>
- <div id="err" class="nodisplay"></div>
- <div class="bottom-btn">
- <input type="button" id="logout" name="logout" value="Cerrar sesión" data-theme="a">
- </div>
- </div>
+ <% } %>
</div>
diff --git a/www/templates/es/offline.html b/www/templates/es/offline.html
index 1051f57..35190df 100644
--- a/www/templates/es/offline.html
+++ b/www/templates/es/offline.html
@@ -1,7 +1,7 @@
<div data-role="header" data-position="fixed">
<a id="offline-prev-btn" class="ui-btn-left">Inténtelo nuevamente</a>
<h1>Sin conexión</h1>
- <a id="offline-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Saltar</a>
+ <a id="offline-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right"><%= files.length == 0 ? "Saltar" : "Siguiente" %></a>
</div>
<div id="locating" class="nodisplay">
<div class="radar"></div>
@@ -42,30 +42,25 @@
</div>
<div>
- <textarea data-role="none" rows="7" cols="26" name="detail" id="form_detail" placeholder="Please fill in details of the problem." required><%= details %></textarea>
+ <textarea data-role="none" rows="2" cols="26" name="detail" id="form_detail" placeholder="Please fill in details of the problem." required><%= details %></textarea>
</div>
</div>
- <% if ( file != '' ) { %>
- <div id="add_photo" style="display: none">
- <% } else { %>
- <div id="add_photo">
+ <% if ( files.length < CONFIG.MAX_PHOTOS ) { %>
+ <div id="add_photo">
+ <input data-icon="fms-photo-new" value="Tomar una nueva foto" type="button" name="photo_button" id="id_photo_button" data-role="button" data-theme="a">
+ <input data-icon="fms-photo-existing" value="Añadir una foto existente" type="button" name="existing" id="id_existing" data-role="button" data-theme="c">
+ </div>
<% } %>
- <label>Añadir una foto <em>(opcional)</em></label>
- <input data-icon="fms-photo-new" value="Tomar una nueva foto" type="button" name="photo_button" id="id_photo_button" data-role="button" data-theme="a">
- <input data-icon="fms-photo-existing" value="Añadir una foto existente" type="button" name="existing" id="id_existing" data-role="button" data-theme="c">
- </div>
- <% if ( file == '' ) { %>
- <div id="display_photo" style="display: none">
- <% } else { %>
- <div id="display_photo">
- <% } %>
- <label>Su foto</label>
- <div class="photo">
- <img class="small" id="photo" src="<%= file %>" />
+ <% if ( files.length > 0 ) { %>
+ <div class="photo-wrapper">
+ <% _.each(files, function(file, index) { %>
+ <div class="photo" style="background-image: url('<%= file %>');">
+ <a data-role="none" class="photo-corner-button del_photo_button" data-file-index="<%= index %>">X</a>
+ </div>
+ <% }); %>
</div>
- <input value="Eliminar foto" type="button" name="del_photo_button" id="id_del_photo_button" data-role="button" data-theme="a">
- </div>
+ <% } %>
</div>
</div>
diff --git a/www/templates/es/photo.html b/www/templates/es/photo.html
index 62f36dc..75de686 100644
--- a/www/templates/es/photo.html
+++ b/www/templates/es/photo.html
@@ -1,37 +1,49 @@
<div data-role="header">
<a data-rel="back" data-icon="arrow-l" class="ui-btn-left">Atrás</a>
- <h1>Añadir foto</h1>
- <% if ( file == '' ) { %>
- <a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Saltar</a>
+ <h1>Add Photos</h1>
+ <% if ( files.length == 0 ) { %>
+ <% if ( !CONFIG.PHOTO_REQUIRED ) { %>
+ <a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Saltar</a>
+ <% } %>
<% } else { %>
<a id="photo-next-btn" data-icon="arrow-r" data-iconpos="right" class="ui-btn-right">Siguiente</a>
<% } %>
</div>
+
<div data-role="content">
- <% var photoClass = 'nodisplay', nophotoClass = 'nodisplay';
- if ( file != '' ) {
- photoClass = '';
- } else {
- nophotoClass = '';
- } %>
- <h2 id="nophoto_title" class="<% print( nophotoClass ); %>">
- Añadir una foto <em>(opcional)</em>
- </h2>
- <h2 id="photo_title" class="<% print( photoClass ); %>">
- Foto para el informe
- </h2>
- <div class="photo-wrapper">
- <div class="photo">
- <% if ( file == '' ) { %>
- <img class="placeholder" src="images/placeholder-photo.png" alt="" id="photo">
+ <% if ( files.length == 0 ) { %>
+ <h2 id="nophoto_title">
+ Añadir una foto
+ <% if ( !CONFIG.PHOTO_REQUIRED ) { %>
+ <em>(opcional)</em>
+ <% } else if ( files.length == 0) { %>
+ <em>(required)</em>
+ <%} %>
+ </h2>
<% } else { %>
- <img class="small" id="photo" src="<%= file %>" />
+ <h2 id="photo_title">
+ Photos for report
+ </h2>
<% } %>
- <a data-role="none" id="id_del_photo_button" class="photo-corner-button">X</a>
- </div>
- </div>
- <div class="bottom-btn">
- <input data-icon="fms-photo-existing" type="button" id="id_existing" data-theme="c" value="Añadir una foto existente" >
- <input data-icon="fms-photo-new" id="id_photo_button" type="button" data-theme="a" value="Tomar una nueva foto">
+
+ <div class="photo-wrapper">
+ <% if ( files.length == 0 ) { %>
+ <div class="photo">
+ <img class="placeholder" src="images/placeholder-photo.png" alt="">
+ </div>
+ <% } else { %>
+ <% _.each(files, function(file, index) { %>
+ <div class="photo" style="background-image: url('<%= file %>');">
+ <a data-role="none" class="photo-corner-button del_photo_button" data-file-index="<%= index %>">X</a>
+ </div>
+ <% });%>
+ <% } %>
</div>
+
+ <% if ( files.length < CONFIG.MAX_PHOTOS ) { %>
+ <div class="bottom-btn">
+ <input data-icon="fms-photo-existing" type="button" id="id_existing" data-theme="c" value="Añadir una foto existente" >
+ <input data-icon="fms-photo-new" id="id_photo_button" type="button" data-theme="a" value="Tomar una nueva foto">
+ </div>
+ <% } %>
</div>
diff --git a/www/templates/es/reports.html b/www/templates/es/reports.html
index cbd2acf..05772a5 100644
--- a/www/templates/es/reports.html
+++ b/www/templates/es/reports.html
@@ -15,7 +15,7 @@
if ( draft.get('lat') ) {
classes.push('with_location');
}
- if ( draft.get('file') ) {
+ if ( draft.get('files').length > 0 ) {
classes.push('with_photo');
}
var draftClass = '';
diff --git a/www/templates/es/submit_confirm.html b/www/templates/es/submit_confirm.html
index a9dd90a..224195b 100644
--- a/www/templates/es/submit_confirm.html
+++ b/www/templates/es/submit_confirm.html
@@ -3,7 +3,7 @@
<h1>Sus detalles</h1>
</div>
-<div data-role="content">
+<div data-role="content"<% if (CONFIG.SKIP_CONFIRM_REPORT) { %> class="hidden"<% } %>>
<p class="notopmargin">
Estas registrado como <%= user.email %>.
</p>
diff --git a/www/templates/es/submit_set_password.html b/www/templates/es/submit_set_password.html
index a9d9b46..39403cb 100644
--- a/www/templates/es/submit_set_password.html
+++ b/www/templates/es/submit_set_password.html
@@ -12,7 +12,7 @@
</p>
<div class="inputcard">
<div>
- <input data-role="none" type="password" value="" name="password" id="form_password" placeholder="Su contraseña" required>
+ <input data-role="none" type="password" value="<%= FMS.currentUser.get('password') %>" name="password" id="form_password" placeholder="Su contraseña" required>
</div>
</div>
<div class="noborder">