Skip to content

Commit e278354

Browse files
committed
2 parents 9d4f146 + e2ab902 commit e278354

File tree

19 files changed

+572
-28
lines changed

19 files changed

+572
-28
lines changed

EcodataClientPluginGrailsPlugin.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class EcodataClientPluginGrailsPlugin {
22
// the plugin version
3-
def version = "1.11.4"
3+
def version = "1.12-SNAPSHOT"
44
// the version or versions of Grails the plugin is designed for
55
def grailsVersion = "2.4 > *"
66
// resources that are excluded from plugin packaging
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
(function() {
2+
3+
/* Custom binding for Bootstrap datepicker */
4+
// This binds an element and a model observable to the bootstrap datepicker.
5+
// The element can be an input or container such as span, div, td.
6+
// The datepicker is 2-way bound to the model. An input element will be updated automatically,
7+
// other elements may need an explicit text binding to the formatted model date (see
8+
// clickToPickDate for an example of a simple element).
9+
ko.bindingHandlers.datepicker = {
10+
init: function (element, valueAccessor, allBindingsAccessor) {
11+
// set current date into the element
12+
var $element = $(element),
13+
initialDate = ko.utils.unwrapObservable(valueAccessor()),
14+
initialDateStr = convertToSimpleDate(initialDate);
15+
if ($element.is('input')) {
16+
$element.val(initialDateStr);
17+
} else {
18+
$element.data('date', initialDateStr);
19+
}
20+
21+
var defaults = {format: 'dd-mm-yyyy', autoclose: true};
22+
var options = _.defaults(allBindingsAccessor().datepickerOptions || {}, defaults);
23+
24+
//initialize datepicker with some optional options
25+
$element.datepicker(options);
26+
27+
// if the parent container holds any element with the class 'open-datepicker'
28+
// then add a hook to do so
29+
$element.parent().find('.open-datepicker').click(function () {
30+
if (!$element.prop('disabled')) {
31+
$element.datepicker('show');
32+
}
33+
});
34+
35+
var changeHandler = function (event) {
36+
var value = valueAccessor();
37+
if (ko.isObservable(value)) {
38+
value(event.date);
39+
}
40+
};
41+
42+
//when a user changes the date via the datepicker, update the view model
43+
ko.utils.registerEventHandler(element, "changeDate", changeHandler);
44+
ko.utils.registerEventHandler(element, "hide", changeHandler);
45+
46+
//when a user changes the date via the input, update the view model
47+
ko.utils.registerEventHandler(element, "change", function () {
48+
var value = valueAccessor();
49+
if (ko.isObservable(value)) {
50+
value(stringToDate(element.value));
51+
$(element).trigger('blur'); // This is to trigger revalidation of the date field to remove existing validation errors.
52+
}
53+
});
54+
},
55+
update: function (element, valueAccessor) {
56+
var widget = $(element).data("datepicker");
57+
//when the view model is updated, update the widget
58+
if (widget) {
59+
var date = ko.utils.unwrapObservable(valueAccessor());
60+
widget.date = date;
61+
if (!isNaN(widget.date)) {
62+
widget.setDate(widget.date);
63+
}
64+
}
65+
}
66+
};
67+
68+
// This extends an observable that holds a UTC ISODate. It creates properties that hold:
69+
// a JS Date object - useful with datepicker; and
70+
// a simple formatted date of the form dd-mm-yyyy useful for display.
71+
// The formatted date will include hh:MM if the includeTime argument is true
72+
ko.extenders.simpleDate = function (target, includeTime) {
73+
target.date = ko.computed({
74+
read: function () {
75+
return Date.fromISO(target());
76+
},
77+
78+
write: function (newValue) {
79+
if (newValue) {
80+
var current = target(),
81+
valueToWrite = convertToIsoDate(newValue);
82+
83+
if (valueToWrite !== current) {
84+
target(valueToWrite);
85+
}
86+
} else {
87+
// date has been cleared
88+
target("");
89+
}
90+
}
91+
});
92+
target.formattedDate = ko.computed({
93+
read: function () {
94+
return convertToSimpleDate(target(), includeTime);
95+
},
96+
97+
write: function (newValue) {
98+
if (newValue) {
99+
var current = target(),
100+
valueToWrite = convertToIsoDate(newValue);
101+
102+
if (valueToWrite !== current) {
103+
target(valueToWrite);
104+
}
105+
}
106+
}
107+
});
108+
target.date(target());
109+
target.formattedDate(target());
110+
111+
return target;
112+
};
113+
}());

grails-app/assets/javascripts/preview.js

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
//= require attach-document.js
1616
//= require jquery.fileDownload/jQuery.fileDownload
1717
//= require leaflet-manifest.js
18+
//= require momentjs/2.24.0/moment.min.js
19+
//= require momentjs/2.24.0/locales/en-au.js
20+
//= require momentjs/moment-timezone-with-data.min.js
21+
//= require utils.js
22+
//= require knockout-dates.js
1823
//= require knockout-utils.js
1924
//= require forms.js
2025
//= require geojson2svg/1.2.3/geojson2svg
+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
Handles the display and editing of UTC dates.
3+
4+
Declares a Knockout extender that allows UTC ISODates to be displayed and edited as simple dates in the form
5+
dd-MM-yyyy and with local timezone adjustment. Hours and minutes can optionally be shown and edited.
6+
7+
Declares a custom binding that allows dates to be changed using the Bootstrap datepicker
8+
(https://github.com/eternicode/bootstrap-datepicker).
9+
10+
The date values in the ViewModel are maintained as UTC dates as strings in ISO format (ISO8601 without milliseconds).
11+
12+
The extender adds a 'formattedDate' property to the observable. It is this property that should be bound
13+
to an element, eg
14+
15+
<input data-bind="value: myDate.formattedDate" type=...../> or
16+
<span data-bind="text: myDate.formattedDate" />
17+
18+
The date is defined in the view model like this:
19+
20+
self.myDate = ko.observable("${myDate}").extend({simpleDate: false});
21+
22+
The boolean indicates whether to show the time as well.
23+
24+
The extender also adds a 'date' property to the observable that holds the value as a Javascript date object.
25+
This is used by the datepicker custom binding.
26+
27+
The custom binding listens for changes via the datepicker as well as direct edits to the input field and
28+
updates the model. It also updates the datepicker on change to the model.
29+
30+
*/
31+
32+
(function(){
33+
34+
// creates an ISO8601 date string but without millis - to match the format used by the java thingy for BSON dates
35+
Date.prototype.toISOStringNoMillis = function() {
36+
function pad(n) { return n < 10 ? '0' + n : n }
37+
return this.getUTCFullYear() + '-'
38+
+ pad(this.getUTCMonth() + 1) + '-'
39+
+ pad(this.getUTCDate()) + 'T'
40+
+ pad(this.getUTCHours()) + ':'
41+
+ pad(this.getUTCMinutes()) + ':'
42+
+ pad(this.getUTCSeconds()) + 'Z';
43+
};
44+
45+
// Use native ISO date parsing or shim for old browsers (IE8)
46+
var D= new Date('2011-06-02T09:34:29+02:00');
47+
if(!D || +D!== 1307000069000){
48+
Date.fromISO= function(s){
49+
var day, tz,
50+
rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
51+
p= rx.exec(s) || [];
52+
if(p[1]){
53+
day= p[1].split(/\D/);
54+
for(var i= 0, L= day.length; i<L; i++){
55+
day[i]= parseInt(day[i], 10) || 0;
56+
}
57+
day[1]-= 1;
58+
day= new Date(Date.UTC.apply(Date, day));
59+
if(!day.getDate()) return NaN;
60+
if(p[5]){
61+
tz= (parseInt(p[5], 10)*60);
62+
if(p[6]) tz+= parseInt(p[6], 10);
63+
if(p[4]== '+') tz*= -1;
64+
if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
65+
}
66+
return day;
67+
}
68+
return NaN;
69+
}
70+
}
71+
else{
72+
Date.fromISO= function(s){
73+
return new Date(s);
74+
}
75+
}
76+
})();
77+
78+
function isValidDate(d) {
79+
if ( Object.prototype.toString.call(d) !== "[object Date]" )
80+
return false;
81+
return !isNaN(d.getTime());
82+
}
83+
84+
function convertToSimpleDate(isoDate, includeTime) {
85+
if (!isoDate) { return ''}
86+
if (typeof isoDate === 'object') {
87+
// assume a date object
88+
if (!isValidDate(isoDate)) {
89+
return '';
90+
}
91+
}
92+
// Format the stage labels using Melbourne/Sydney/Canberra time to avoid problems where the date starts
93+
// at midnight and displays as the previous day in other timezones.
94+
var date = moment.tz(isoDate, "Australia/Sydney");
95+
var format = includeTime ? "DD-MM-YYYY HH:mm" : "DD-MM-YYYY";
96+
return date.format(format);
97+
}
98+
99+
/**
100+
* Displays the supplied date as the financial year it falls into. *Note* this routine will subtract
101+
* a day from the supplied date to catch report ranges that start and end on the 1st of July. e.g. 30-06-2017T14:00:00Z - 30-06-2017T14:00:00Z
102+
*
103+
* @param date the date to format - must be a ISO 8601 formatted string.
104+
* @return a String of the form "2016 / 2017"
105+
*/
106+
function isoDateToFinancialYear(date) {
107+
var parsedDate = moment(date).subtract(1, 'days');
108+
var year = parsedDate.year();
109+
// If the month is July - December, the financial year ends with the following year
110+
if (parsedDate.month() >= 6) {
111+
year++;
112+
}
113+
114+
return (year-1) + '/' + year;
115+
}
116+
117+
function convertToIsoDate(date) {
118+
if (typeof date === 'string') {
119+
if (date.length === 20 && date.charAt(19) === 'Z') {
120+
// already an ISO date string
121+
return date;
122+
} else if (date.length > 9){
123+
// assume a short date of the form dd-mm-yyyy
124+
var year = date.substr(6,4),
125+
month = Number(date.substr(3,2))- 1,
126+
day = date.substr(0,2),
127+
hours = date.length > 12 ? date.substr(11,2) : 0,
128+
minutes = date.length > 15 ? date.substr(14,2) : 0;
129+
var dt = new Date(year, month, day, hours, minutes);
130+
if (isValidDate(dt)) {
131+
return dt.toISOStringNoMillis();
132+
}
133+
else {
134+
return '';
135+
}
136+
} else {
137+
return '';
138+
}
139+
} else if (typeof date === 'object') {
140+
// assume a date object
141+
if (isValidDate(date)) {
142+
return date.toISOStringNoMillis();
143+
}
144+
else {
145+
return '';
146+
}
147+
148+
} else {
149+
return '';
150+
}
151+
}
152+
153+
function stringToDate(date) {
154+
if (typeof date === 'string') {
155+
if (date.length === 20 && date.charAt(19) === 'Z') {
156+
// already an ISO date string
157+
return Date.fromISO(date);
158+
} else if (date.length > 9){
159+
// assume a short date of the form dd-mm-yyyy
160+
var year = date.substr(6,4),
161+
month = Number(date.substr(3,2))- 1,
162+
day = date.substr(0,2),
163+
hours = date.length > 12 ? date.substr(11,2) : 0,
164+
minutes = date.length > 15 ? date.substr(14,2) : 0;
165+
return new Date(year, month, day, hours, minutes);
166+
} else {
167+
return undefined;
168+
}
169+
} else if (typeof date === 'object') {
170+
// assume a date object
171+
return date;
172+
} else {
173+
return undefined;
174+
}
175+
}
176+
177+
178+
/* From:
179+
* jQuery File Upload User Interface Plugin 6.8.1
180+
* https://github.com/blueimp/jQuery-File-Upload
181+
*
182+
* Copyright 2010, Sebastian Tschan
183+
* https://blueimp.net
184+
*
185+
* Licensed under the MIT license:
186+
* http://www.opensource.org/licenses/MIT
187+
*/
188+
function formatBytes(bytes) {
189+
if (typeof bytes !== 'number') {
190+
return '';
191+
}
192+
if (bytes >= 1000000000) {
193+
return (bytes / 1000000000).toFixed(2) + ' GB';
194+
}
195+
if (bytes >= 1000000) {
196+
return (bytes / 1000000).toFixed(2) + ' MB';
197+
}
198+
return (bytes / 1000).toFixed(2) + ' KB';
199+
}
200+

grails-app/assets/stylesheets/preview.css

+4
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@
3939
.list {
4040
border-right: 1px solid grey;
4141
height:100%;
42+
}
43+
44+
span.title {
45+
text-overflow: ellipsis;
4246
}

grails-app/conf/BuildConfig.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ clover {
3535
}
3636
}
3737
}
38-
ant.'clover-check'(target: "52%", haltOnFailure: true) { }
38+
ant.'clover-check'(target: "52.5%", haltOnFailure: true) { }
3939

4040
}
4141
}

grails-app/conf/UrlMappings.groovy

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ class UrlMappings {
1010
controller = 'preview'
1111
action = [GET: 'index', POST: 'model']
1212
}
13+
"/preview/imagePreview/$id" {
14+
controller = 'preview'
15+
action = 'imagePreview'
16+
}
1317
"/" {
1418
controller = 'preview'
1519
action = [GET: 'index', POST: 'model']
339 KB
Loading
341 KB
Loading
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"images1": {
3+
"documentId": "d1"
4+
},
5+
"images2": {
6+
"documentId": "d2"
7+
}
8+
}

0 commit comments

Comments
 (0)