diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-12-13 14:12:55 -0500 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-12-13 21:41:38 -0500 |
commit | e7bacd45e1b44090781067159b63fe14b46b8611 (patch) | |
tree | d291a561cd58d4e42536b76d9ec91d26d72a87a5 | |
parent | 85a578479406865502a3eb504577c1dfe64f34f5 (diff) |
Use HTML5 type=datetime-local inputs for ChartCoordinator.
-rw-r--r-- | docs/examples/assets/css/jquerytools.dateinput.skin1.css | 157 | ||||
-rw-r--r-- | docs/examples/index.html | 26 | ||||
-rw-r--r-- | jarmon/external.doc.js | 6 | ||||
-rw-r--r-- | jarmon/jarmon.js | 301 | ||||
-rw-r--r-- | jarmonbuild/commands.py | 1 |
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'), |