summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-12-13 14:12:55 -0500
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-12-13 21:41:38 -0500
commite7bacd45e1b44090781067159b63fe14b46b8611 (patch)
treed291a561cd58d4e42536b76d9ec91d26d72a87a5
parent85a578479406865502a3eb504577c1dfe64f34f5 (diff)
Use HTML5 type=datetime-local inputs for ChartCoordinator.
-rw-r--r--docs/examples/assets/css/jquerytools.dateinput.skin1.css157
-rw-r--r--docs/examples/index.html26
-rw-r--r--jarmon/external.doc.js6
-rw-r--r--jarmon/jarmon.js301
-rw-r--r--jarmonbuild/commands.py1
5 files changed, 143 insertions, 348 deletions
diff --git a/docs/examples/assets/css/jquerytools.dateinput.skin1.css b/docs/examples/assets/css/jquerytools.dateinput.skin1.css
deleted file mode 100644
index 2f77f6e..0000000
--- a/docs/examples/assets/css/jquerytools.dateinput.skin1.css
+++ /dev/null
@@ -1,157 +0,0 @@
-/* Skin for jQuery Tools dateinput.
- *
- * Based on <https://github.com/jquerytools/jquerytools.github.com/blob/master/media/css/dateinput/skin1.css>.
- *
- * Documentation on dateinput skinning: <http://jquerytools.github.io/documentation/dateinput/index.html#skinning>
- */
-
-/* the input field */
-.date {
- border:1px solid #ccc;
- font-size:18px;
- padding:4px;
- text-align:center;
- width:194px;
-
- box-shadow:0 0 10px #eee inset;
- -moz-box-shadow:0 0 10px #eee inset;
- -webkit-box-shadow:0 0 10px #eee inset;
-}
-
-/* calendar root element */
-#calroot {
- margin-top:-1px;
- width:198px;
- padding:2px;
- background-color:#fff;
- font-size:11px;
- border:1px solid #ccc;
-
- border-radius:5px;
- -moz-border-radius:5px;
- -webkit-border-radius:5px;
-
- box-shadow: 0 0 15px #666;
- -moz-box-shadow: 0 0 15px #666;
- -webkit-box-shadow: 0 0 15px #666;
-}
-
-/* head. contains title, prev/next month controls and possible month/year selectors */
-#calhead {
- padding:2px 0;
- height:22px;
-}
-
-#caltitle {
- font-size:14px;
- color:#0150D1;
- float:left;
- text-align:center;
- width:155px;
- line-height:20px;
- text-shadow:0 1px 0 #ddd;
-}
-
-#calnext, #calprev {
- display:block;
- width:20px;
- height:20px;
- background:transparent url(../icons/prev.gif) no-repeat scroll center center;
- float:left;
- cursor:pointer;
-}
-
-#calnext {
- background-image:url(../icons/next.gif);
- float:right;
-}
-
-#calprev.caldisabled, #calnext.caldisabled {
- visibility:hidden;
-}
-
-/* year/month selector */
-#caltitle select {
- font-size:10px;
-}
-
-/* names of the days */
-#caldays {
- height:14px;
- border-bottom:1px solid #ddd;
-}
-
-#caldays span {
- display:block;
- float:left;
- width:28px;
- text-align:center;
-}
-
-/* container for weeks */
-#calweeks {
- background-color:#fff;
- margin-top:4px;
-}
-
-/* single week */
-.calweek {
- clear:left;
- height:22px;
-}
-
-/* single day */
-.calweek a {
- display:block;
- float:left;
- width:27px;
- height:20px;
- text-decoration:none;
- font-size:11px;
- margin-left:1px;
- text-align:center;
- line-height:20px;
- color:#666;
- border-radius:3px;
- -moz-border-radius:3px;
- -webkit-border-radius:3px;
-}
-
-/* different states */
-.calweek a:hover, .calfocus {
- background-color:#ddd;
-}
-
-/* sunday */
-a.calsun {
- color:red;
-}
-
-/* offmonth day */
-a.caloff {
- color:#ccc;
-}
-
-a.caloff:hover {
- background-color:rgb(245, 245, 250);
-}
-
-
-/* unselecteble day */
-a.caldisabled {
- background-color:#efefef !important;
- color:#ccc !important;
- cursor:default;
-}
-
-/* current day */
-#calcurrent {
- background-color:#498CE2;
- color:#fff;
-}
-
-/* today */
-#caltoday {
- background-color:#333;
- color:#fff;
-}
diff --git a/docs/examples/index.html b/docs/examples/index.html
index b86fe91..44fae85 100644
--- a/docs/examples/index.html
+++ b/docs/examples/index.html
@@ -9,7 +9,6 @@
<link rel="stylesheet" type="text/css" href="assets/css/style.css" />
<link rel="stylesheet" type="text/css" href="assets/css/jquerytools.tabs.tabs-no-images.css" />
- <link rel="stylesheet" type="text/css" href="assets/css/jquerytools.dateinput.skin1.css" />
<script type="text/javascript" src="assets/js/dependencies.js"></script>
<script type="text/javascript" src="../../jarmon/jarmon.js"></script>
@@ -20,26 +19,13 @@
<body>
<div class="chartRangeControl">
<form>
- <div>
- <span class="timerange_control custom">
- <img src="assets/icons/calendar.png" width="16" height="16" alt="calendar" class="from_custom"
- title="Click to choose a custom start date" />
- <input name="from_custom" type="text" readonly="readonly"
- title="Time range start" />
- <img src="assets/icons/calendar.png" width="16" height="16" alt="calendar" class="to_custom"
- title="Click to choose a custom end date" />
- <input name="to_custom" type="text" readonly="readonly"
- title="Time range end" />
+ <div class="range-inputs">
+ <span class="custom">
+ <input name="from" type="datetime-local" step="1" />
+ <input name="to" type="datetime-local" step="1" />
</span>
- <span class="timerange_control standard">
- <select name="from_standard"
- title="Time range shortcuts - click to select an alternative time range" >
- </select>
- </span>
- <input name="from" type="hidden" />
- <input name="to" type="hidden" />
- <select name="tzoffset"
- title="Timezone offset - click to choose a custom timezone offset" ></select>
+ <select name="shortcuts" title="Time range shortcuts - click to select an alternative time range" ></select>
+ <select name="tzoffset" title="Timezone offset - click to choose a custom timezone offset" ></select>
<input name="action" value="Update" type="button"
title="Graph update - click to update all graphs" />
</div>
diff --git a/jarmon/external.doc.js b/jarmon/external.doc.js
index 119d1fb..10d9ea7 100644
--- a/jarmon/external.doc.js
+++ b/jarmon/external.doc.js
@@ -17,12 +17,6 @@
*/
/**
- * {@link http://jquerytools.github.io/ jQuery Tools} / dateinput
- * @module jQueryTools/dateinput
- * @see http://jquerytools.github.io/documentation/dateinput/index.html
- */
-
-/**
* {@link http://jquerytools.github.io/ jQuery Tools} / tabs
* @module jQueryTools/tabs
* @see http://jquerytools.github.io/documentation/tabs/index.html
diff --git a/jarmon/jarmon.js b/jarmon/jarmon.js
index cef338b..f3f6e33 100644
--- a/jarmon/jarmon.js
+++ b/jarmon/jarmon.js
@@ -13,7 +13,6 @@
* - javascriptRRD/binaryXHR 0.6/1.1: http://javascriptrrd.sourceforge.net/docs/javascriptrrd_v1.1.1/doc/lib/binaryXHR_js.html
* - jQuery 1.6.3: http://jquery.com/
* - Flot 1.7: http://www.flotcharts.org/
- * - jQueryTools/dateinput: http://jquerytools.github.io/documentation/dateinput/index.html
* - jQueryTools/tabs: http://jquerytools.github.io/documentation/tabs/index.html
* - jQueryTools/toolbox/history: http://jquerytools.github.io/documentation/toolbox/history.html
*/
@@ -1306,9 +1305,35 @@ jarmon.timeRangeShortcuts = [
* Presents the user with a form and a timeline with which they can choose a
* time range and co-ordinates the refreshing of a series of charts.
*
+ * The `ui` parameter should be a jQuery for a DOM node containing elements
+ * matching these selectors:
+ *
+ * - `select[name="shortcuts"]` : An empty `<select>` to be populated with
+ * {@linkcode jarmon.timeRangeShortcuts}
+ * - `[name="from"]` ; An input to select the start datetime
+ * - `[name="to"]` : An input to select the end datetime
+ * - `[name="tzoffset"]` : An input to select the timezone
+ * - `[name="action"]` : A button to update the charts
+ * - `.range-preview` : Placeholder for a Flot chart
+ *
+ * The `[name="to"]` and `[name="from"]` inputs should behave somewhat like
+ * WHATWG HTML (revision 2016-12-12) `type=datetime-local` inputs with `step=1`.
+ * Because `type=datetime-local` is not currently in a W3C spec, and not all
+ * common browsers implement it, and polyfills vary in quality; Jarmon avoids
+ * interacting with it too deeply, placing only a few requirements placed on the
+ * implementation:
+ *
+ * - The value MUST be settable via `el.value = string` where `string` is of the
+ * format `%Y-%m-%dT%H:%M:%S`.
+ * - The value MUST be gettable via `Date.parse(el.value+'Z')`.
+ * - The user interface SHOULD prohibit the user from selecting a value that is
+ * before than the `min` attribute, which is in the format
+ * `%Y-%m-%dT%H:%M:%S`.
+ * - The user interface SHOULD prohibit the user from selecting a value that is
+ * after than the `max` attribute, which is in the format `%Y-%m-%dT%H:%M:%S`.
+ *
* @constructor
* @requires jQuery
- * @requires jQueryTools/dateinput
* @requires Flot
*
* @param ui {module:jQuery.jQuery} A one element jQuery containing an input form and
@@ -1336,97 +1361,98 @@ jarmon.ChartCoordinator = function(ui, charts) {
}
};
- var options = this.ui.find('select[name="from_standard"]');
- var default_index = 0; // Select the first shortcut by default
- for(var i=0; i<jarmon.timeRangeShortcuts.length; i++) {
- options.append($('<option />').text(jarmon.timeRangeShortcuts[i][0]));
- if (jarmon.timeRangeShortcuts[i][2] === true) {
- default_index = i;
+ this.inputs = {
+ shortcuts: this.ui.find('select[name="shortcuts"]'),
+ from: this.ui.find('[name="from"]'),
+ to: this.ui.find('[name="to"]'),
+ tzoffset: this.ui.find('[name="tzoffset"]'),
+ action: this.ui.find('[name="action"]')
+ };
+
+ // DOM manipulation ////////////////////////////////////////////////////////
+
+ // (This is put in a closure to avoid leaking variables and making it hard
+ // for me to reason about the code.)
+ (function(options, tzoffsetEl) {
+ // Set up shortcuts
+ var option;
+ for(var i=0; i<jarmon.timeRangeShortcuts.length; i++) {
+ option = $('<option />').text(jarmon.timeRangeShortcuts[i][0])
+ if (jarmon.timeRangeShortcuts[i][2] === true) {
+ option[0].setAttribute('selected', 'selected');
+ }
+ options.append(option);
}
- }
+ // Append a custom option for when the user selects an area of the graph
+ options.append($('<option />').text('custom'));
+
+ // Populate a list of tzoffset options if the element is present in the
+ // template as a select list
+ if (tzoffsetEl.is('select')) {
+ var label, val;
+ for(i=-12; i<=12; i++) {
+ label = 'UTC';
+ val = i;
+ if(val >= 0) {
+ label += ' + ';
+ } else {
+ label += ' - ';
+ }
+ val = Math.abs(val).toString();
+ if(val.length === 1) {
+ label += '0';
+ }
+ label += val + '00';
+ tzoffsetEl.append(
+ $('<option />').attr('value', i*60*60*1000).text(label));
+ }
+ }
+
+ // Default timezone offset based on localtime
+ var tzoffset = -1 * (new Date()).getTimezoneOffset() * 60 * 1000;
+ tzoffsetEl.val(tzoffset);
+ })(this.inputs.shortcuts, this.inputs.tzoffset);
- // Append a custom option for when the user selects an area of the graph
- options.append($('<option />').text('custom'));
- // Select whichever timeRangeShortcut is the default
- options.val(jarmon.timeRangeShortcuts[default_index][0]);
+ // Event bindings //////////////////////////////////////////////////////////
- options.bind('change', function(e) {
+ this.inputs.shortcuts.bind('change', function(e) {
// No point in updating if the user chose custom.
if($(this).val() !== 'custom') {
self.update();
}
});
- // Update the time ranges and redraw charts when the custom datetime inputs
- // are changed
- this.ui.find('[name="from_custom"]').bind('change',
+ this.inputs.from.bind('change',
function(e) {
- self.ui.find('[name="from_standard"]').val('custom');
- var tzoffset = parseInt(self.ui.find('[name="tzoffset"]').val(), 10);
- self.setTimeRange(
- new Date(this.value + ' UTC').getTime() - tzoffset, null);
+ self.inputs.shortcuts.val('custom');
self.update();
}
);
- this.ui.find('[name="to_custom"]').bind('change',
+ this.inputs.to.bind('change',
function(e) {
- self.ui.find('[name="from_standard"]').val('custom');
- var tzoffset = parseInt(self.ui.find('[name="tzoffset"]').val(), 10);
- self.setTimeRange(
- null, new Date(this.value + ' UTC').getTime() - tzoffset);
+ self.inputs.shortcuts.val('custom');
self.update();
}
);
- // Populate a list of tzoffset options if the element is present in the
- // template as a select list
- var tzoffsetEl = this.ui.find('[name="tzoffset"]');
- if(tzoffsetEl.is('select')) {
- var label, val;
- for(i=-12; i<=12; i++) {
- label = 'UTC';
- val = i;
- if(val >= 0) {
- label += ' + ';
- } else {
- label += ' - ';
- }
- val = Math.abs(val).toString();
- if(val.length === 1) {
- label += '0';
- }
- label += val + '00';
- tzoffsetEl.append(
- $('<option />').attr('value', i*60*60*1000).text(label));
- }
-
- tzoffsetEl.bind('change', function(e) {
- self.update();
- });
- }
-
- // Default timezone offset based on localtime
- var tzoffset = -1 * new Date().getTimezoneOffset() * 60 * 1000;
- tzoffsetEl.val(tzoffset);
+ this.inputs.tzoffset.bind('change', function(e) {
+ self.update();
+ });
- // Update the time ranges and redraw charts when the form is submitted
- this.ui.find('[name="action"]').bind('click', function(e) {
+ this.inputs.action.bind('click', function(e) {
self.update();
return false;
});
// When a selection is made on the range timeline, or any of my charts
// redraw all the charts.
- $(document).bind(
- 'plotselected',
- {self: this},
+ $(document).bind('plotselected', {},
function(e, ranges) {
- var self = e.data.self;
var eventSourceIsMine = false;
// plotselected event may be from my range selector chart or
- if( self.ui.has(e.target) ) {
+ if (self.ui.has(e.target) ) {
eventSourceIsMine = true;
} else {
// ...it may come from one of the charts under my supervision
@@ -1439,97 +1465,12 @@ jarmon.ChartCoordinator = function(ui, charts) {
}
if(eventSourceIsMine) {
- // Update the prepared time range select box to value "custom"
- self.ui.find('[name="from_standard"]').val('custom');
-
- // Update all my charts
self.setTimeRange(ranges.xaxis.from, ranges.xaxis.to);
- self.update();
}
}
);
-
- // Add dhtml calendars to the date input fields
- this.ui.find(".timerange_control img")
- .dateinput({
- 'format': 'dd mmm yyyy 00:00:00',
- 'max': +1,
- 'css': {'input': 'jquerytools_date'}})
- .bind('onBeforeShow', function(e) {
- var classes = $(this).attr('class').split(' ');
- var currentDate, input_selector;
- for(var i=0; i<=classes.length; i++) {
- input_selector = '[name="' + classes[i] + '"]';
- // Look for a neighboring input element whose name matches the
- // class name of this calendar
- // Parse the value as a date if the returned date.getTime
- // returns NaN we know it's an invalid date
- // XXX: is there a better way to check for valid date?
- currentDate = new Date($(this).siblings(input_selector).val());
- if(! isNaN(currentDate.getTime()) ) {
- $(this).data(
- 'dateinput')._initial_val = currentDate.getTime();
- $(this).data('dateinput').setValue(currentDate);
- break;
- }
- }
- })
- .bind(
- 'onHide',
- {self: this},
- function(e) {
- var self = e.data.self;
- // Called after a calendar date has been chosen by the user.
- // Use the sibling selector that we generated above
- // before opening the calendar
- var oldStamp = $(this).data('dateinput')._initial_val;
- var newDate = $(this).data('dateinput').getValue();
- // Only update the form field if the date has changed.
- if(oldStamp !== newDate.getTime()) {
- // Update the prepared time range select box to
- // value "custom"
- self.ui.find('[name="from_standard"]').val('custom');
- var from = null;
- var to = null;
- if($(this).hasClass('from_custom')){
- from = newDate.getTime();
- } else {
- to = newDate.getTime();
- }
- self.setTimeRange(from, to);
- self.update();
- }
- });
-
- // Avoid overlaps between the calendars
- // XXX: This is a bit of hack, what if there's more than one set of calendar
- // controls on a page?
- this.ui.find(".timerange_control img.from_custom").bind(
- 'onBeforeShow',
- {self: this},
- function(e) {
- var self = e.data.self;
- var otherVal = new Date(
- self.ui.find('.timerange_control [name="to_custom"]').val());
-
- $(this).data('dateinput').setMax(otherVal);
- }
- );
- this.ui.find(".timerange_control img.to_custom").bind(
- 'onBeforeShow',
- {self: this},
- function(e) {
- var self = e.data.self;
- var otherVal = new Date(
- self.ui.find('.timerange_control [name="from_custom"]').val());
-
- $(this).data('dateinput').setMin(otherVal);
- }
- );
-
};
-
/**
* Grab the start and end time from the ui form, highlight the range on the
* range timeline and set the time range of all the charts and redraw.
@@ -1539,27 +1480,25 @@ jarmon.ChartCoordinator = function(ui, charts) {
**/
jarmon.ChartCoordinator.prototype.update = function() {
var self = this;
- var selection = this.ui.find('[name="from_standard"]').val();
+ var shortcut = this.inputs.shortcuts.val();
- var now = new Date().getTime();
+ var now = new Date();
for(var i=0; i<jarmon.timeRangeShortcuts.length; i++) {
- if(jarmon.timeRangeShortcuts[i][0] === selection) {
- var range = jarmon.timeRangeShortcuts[i][1](now);
- this.setTimeRange(range[0], range[1]);
+ if(jarmon.timeRangeShortcuts[i][0] === shortcut) {
+ var range = jarmon.timeRangeShortcuts[i][1](now.getTime());
+ this.inputs.from.val(this.unix2datetimelocal(range[0]))
+ this.inputs.to.val(this.unix2datetimelocal(range[1]));
break;
}
}
- var startTime = parseInt(this.ui.find('[name="from"]').val(), 10);
- var endTime = parseInt(this.ui.find('[name="to"]').val(), 10);
- var tzoffset = parseInt(this.ui.find('[name="tzoffset"]').val(), 10);
+ var startTime = this.datetimelocal2unix(this.inputs.from.val());
+ var endTime = this.datetimelocal2unix(this.inputs.to.val());
+ var tzoffset = parseInt(this.inputs.tzoffset.val(), 10);
- this.ui.find('[name="from_custom"]').val(
- new Date(startTime + tzoffset)
- .toUTCString().split(' ').slice(1,5).join(' '));
- this.ui.find('[name="to_custom"]').val(
- new Date(endTime + tzoffset)
- .toUTCString().split(' ').slice(1,5).join(' '));
+ this.inputs.from[0].setAttribute('max', this.inputs.to.val());
+ this.inputs.to[0].setAttribute( 'min', this.inputs.from.val());
+ this.inputs.to[0].setAttribute( 'max', now.toISOString().slice(0, -1));
this.rangePreviewOptions.xaxis.tzoffset = tzoffset;
@@ -1629,6 +1568,38 @@ jarmon.ChartCoordinator.prototype.update = function() {
};
/**
+ * Convert a UNIX millisecond timestamp into a [datetime-local string][]; using
+ * the `[name="tzoffoffset"]` input for the timezone.
+ *
+ * [datetime-local string]: https://html.spec.whatwg.org/multipage/infrastructure.html#concept-datetime-local
+ *
+ * @method
+ * @private
+ * @param unix {number}
+ * @return {string}
+ */
+jarmon.ChartCoordinator.prototype.unix2datetimelocal = function(unix) {
+ var tz = parseInt(this.inputs.tzoffset.val(), 10);
+ return (new Date(unix + tz)).toISOString().slice(0, -1);
+}
+
+/**
+ * Convert a [datetime-local string][] into a UNIX millisecond timestamp; using
+ * the `[name="tzoffoffset"]` input for the timezone.
+ *
+ * [datetime-local string]: https://html.spec.whatwg.org/multipage/infrastructure.html#concept-datetime-local
+ *
+ * @method
+ * @private
+ * @param datetimelocal {string}
+ * @return {number}
+ */
+jarmon.ChartCoordinator.prototype.datetimelocal2unix = function(datetimelocal) {
+ var tz = parseInt(this.inputs.tzoffset.val(), 10);
+ return Date.parse(datetimelocal+'Z') - tz;
+};
+
+/**
* Set the start and end time fields in the form and trigger an update
*
* @method
@@ -1637,11 +1608,13 @@ jarmon.ChartCoordinator.prototype.update = function() {
**/
jarmon.ChartCoordinator.prototype.setTimeRange = function(from, to) {
if(typeof(from) !== 'undefined' && from !== null) {
- this.ui.find('[name="from"]').val(from);
+ this.inputs.from.val(this.unix2datetimelocal(from))
}
if(typeof(to) !== 'undefined' && to !== null) {
- this.ui.find('[name="to"]').val(to);
+ this.inputs.to.val(this.unix2datetimelocal(to));
}
+ this.inputs.shortcuts.val('custom');
+ this.update();
};
/**
diff --git a/jarmonbuild/commands.py b/jarmonbuild/commands.py
index 749a84c..617976f 100644
--- a/jarmonbuild/commands.py
+++ b/jarmonbuild/commands.py
@@ -209,7 +209,6 @@ class BuildJavascriptDependenciesCommand(BuildCommand):
('code_url', 'https://raw.githubusercontent.com/flot/flot/v0.7.0/jquery.flot.selection.js'),
('code_url', 'https://lukeshu.com/git/2git/javascriptrrd/plain/src/lib/rrdFile.js?id=v1.1.1'),
('code_url', 'https://lukeshu.com/git/2git/javascriptrrd/plain/src/lib/binaryXHR.js?id=v1.1.1'),
- ('code_url', 'https://raw.githubusercontent.com/jquerytools/jquerytools/8ac4636a01d3860f1c4726ba722190a531bf1068/src/dateinput/dateinput.js'),
('code_url', 'https://raw.githubusercontent.com/jquerytools/jquerytools/8ac4636a01d3860f1c4726ba722190a531bf1068/src/tabs/tabs.js'),
('code_url', 'https://raw.githubusercontent.com/jquerytools/jquerytools/8ac4636a01d3860f1c4726ba722190a531bf1068/src/toolbox/toolbox.history.js'),
('compilation_level', 'SIMPLE_OPTIMIZATIONS'),