/** * @license * Visual Blocks Editor * * Copyright 2015 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Date input field. * @author pkendall64@gmail.com (Paul Kendall) */ 'use strict'; goog.provide('Blockly.FieldDate'); goog.require('Blockly.Events'); goog.require('Blockly.Field'); goog.require('Blockly.utils.dom'); goog.require('goog.date'); goog.require('goog.date.DateTime'); goog.require('goog.events'); goog.require('goog.i18n.DateTimeSymbols'); goog.require('goog.i18n.DateTimeSymbols_he'); goog.require('goog.style'); goog.require('goog.ui.DatePicker'); /** * Class for a date input field. * @param {string=} opt_value The initial value of the field. Should be in * 'YYYY-MM-DD' format. Defaults to the current date. * @param {Function=} opt_validator A function that is called to validate * changes to the field's value. Takes in a date string & returns a * validated date string ('YYYY-MM-DD' format), or null to abort the change. * @extends {Blockly.Field} * @constructor */ Blockly.FieldDate = function(opt_value, opt_validator) { opt_value = this.doClassValidation_(opt_value); if (!opt_value) { opt_value = new goog.date.Date().toIsoString(true); } Blockly.FieldDate.superClass_.constructor.call(this, opt_value, opt_validator); }; goog.inherits(Blockly.FieldDate, Blockly.Field); /** * Construct a FieldDate from a JSON arg object. * @param {!Object} options A JSON object with options (date). * @return {!Blockly.FieldDate} The new field instance. * @package * @nocollapse */ Blockly.FieldDate.fromJson = function(options) { return new Blockly.FieldDate(options['date']); }; /** * Serializable fields are saved by the XML renderer, non-serializable fields * are not. Editable fields should also be serializable. * @type {boolean} * @const */ Blockly.FieldDate.prototype.SERIALIZABLE = true; /** * Mouse cursor style when over the hotspot that initiates the editor. */ Blockly.FieldDate.prototype.CURSOR = 'text'; /** * Border colour for the dropdown div showing the date picker. Must be a CSS * string. * @type {string} * @private */ Blockly.FieldDate.prototype.DROPDOWN_BORDER_COLOUR = 'silver'; /** * Background colour for the dropdown div showing the date picker. Must be a * CSS string. * @type {string} * @private */ Blockly.FieldDate.prototype.DROPDOWN_BACKGROUND_COLOUR = 'white'; /** * Close the colour picker if this input is being deleted. */ Blockly.FieldDate.prototype.dispose = function() { Blockly.WidgetDiv.hideIfOwner(this); Blockly.FieldDate.superClass_.dispose.call(this); }; /** * Ensure that the input value is a valid date. * @param {string=} newValue The input value. * @return {?string} A valid date, or null if invalid. * @protected */ Blockly.FieldDate.prototype.doClassValidation_ = function(newValue) { if (!newValue) { return null; } // Check if the new value is parsable or not. var date = goog.date.Date.fromIsoString(newValue); if (!date || date.toIsoString(true) != newValue) { return null; } return newValue; }; /** * Called when the given value is invalid. If the picker is shown, set * isDirty to true so that it gets reset to the previous selection. * @protected */ Blockly.FieldDate.prototype.doValueInvalid_ = function() { if (this.picker_) { this.isDirty_ = true; } }; /** * Render the field. If the picker is shown make sure it has the current * date selected. * @protected */ Blockly.FieldDate.prototype.render_ = function() { Blockly.FieldDate.superClass_.render_.call(this); if (this.picker_) { this.picker_.setDate(goog.date.Date.fromIsoString(this.getValue())); this.updateEditor_(); } }; /** * Updates the field's colours to match those of the block. * @package */ Blockly.FieldDate.prototype.updateColour = function() { this.todayColour_ = this.sourceBlock_.getColour(); this.selectedColour_ = this.sourceBlock_.getColourShadow(); this.updateEditor_(); }; /** * Updates the picker to show the current date and currently selected date. * @private */ Blockly.FieldDate.prototype.updateEditor_ = function() { if (!this.picker_) { // Nothing to update. return; } // Updating today should come before updating selected, so that if the // current day is selected, it will appear so. if (this.oldTodayElement_) { this.oldTodayElement_.style.backgroundColor = null; this.oldTodayElement_.style.color = null; } var today = this.picker_.getElementByClass('goog-date-picker-today'); this.oldTodayElement_ = today; if (today) { today.style.backgroundColor = this.todayColour_; today.style.color = 'white'; } if (this.oldSelectedElement_ && this.oldSelectedElement_ != today) { this.oldSelectedElement_.style.backgroundColor = null; this.oldSelectedElement_.style.color = null; } var selected = this.picker_.getElementByClass('goog-date-picker-selected'); this.oldSelectedElement_ = selected; if (selected) { selected.style.backgroundColor = this.selectedColour_; selected.style.color = this.todayColour_; } }; /** * Create a date picker under the date field. * @private */ Blockly.FieldDate.prototype.showEditor_ = function() { this.picker_ = this.createWidget_(); this.picker_.render(Blockly.DropDownDiv.getContentDiv()); Blockly.utils.dom.addClass(this.picker_.getElement(), 'blocklyDatePicker'); Blockly.DropDownDiv.showPositionedByField(this); Blockly.DropDownDiv.setColour( this.DROPDOWN_BACKGROUND_COLOUR, this.DROPDOWN_BORDER_COLOUR); this.updateEditor_(); var thisField = this; Blockly.FieldDate.changeEventKey_ = goog.events.listen(this.picker_, goog.ui.DatePicker.Events.CHANGE, function(event) { var date = event.date ? event.date.toIsoString(true) : ''; thisField.setValue(date); }); Blockly.FieldDate.changeEventKey_ = goog.events.listen(this.picker_, goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH, function(_e) { thisField.updateEditor_(); }); }; /** * Create a date picker widget and render it inside the widget div. * @return {!goog.ui.DatePicker} The newly created date picker. * @private */ Blockly.FieldDate.prototype.createWidget_ = function() { // Create the date picker using Closure. Blockly.FieldDate.loadLanguage_(); var picker = new goog.ui.DatePicker(); picker.setAllowNone(false); picker.setShowWeekNum(false); picker.setUseNarrowWeekdayNames(true); picker.setUseSimpleNavigationMenu(true); picker.setDate(goog.date.DateTime.fromIsoString(this.getValue())); return picker; }; /** * Hide the date picker. * @private */ Blockly.FieldDate.widgetDispose_ = function() { if (Blockly.FieldDate.changeEventKey_) { goog.events.unlistenByKey(Blockly.FieldDate.changeEventKey_); } this.picker_ = null; Blockly.Events.setGroup(false); }; /** * Load the best language pack by scanning the Blockly.Msg object for a * language that matches the available languages in Closure. * @private */ Blockly.FieldDate.loadLanguage_ = function() { var reg = /^DateTimeSymbols_(.+)$/; for (var prop in goog.i18n) { var m = prop.match(reg); if (m) { var lang = m[1].toLowerCase().replace('_', '.'); // E.g. 'pt.br' if (goog.getObjectByName(lang, Blockly.Msg)) { goog.i18n.DateTimeSymbols = goog.i18n[prop]; } } } }; /** * CSS for date picker. See css.js for use. */ Blockly.FieldDate.CSS = [ '.blocklyDatePicker,', '.blocklyDatePicker th,', '.blocklyDatePicker td {', ' font: 13px Arial, sans-serif;', ' color: #3c4043;', '}', '.blocklyDatePicker th,', '.blocklyDatePicker td {', ' text-align: center;', ' vertical-align: middle;', '}', '.blocklyDatePicker .goog-date-picker-wday,', '.blocklyDatePicker .goog-date-picker-date {', ' padding: 6px 6px;', '}', '.blocklyDatePicker button {', ' cursor: pointer;', ' padding: 6px 6px;', ' margin: 1px 0;', ' border: 0;', ' color: #3c4043;', ' font-weight: bold;', ' background: transparent;', '}', '.blocklyDatePicker .goog-date-picker-previousMonth,', '.blocklyDatePicker .goog-date-picker-nextMonth {', ' height: 24px;', ' width: 24px;', '}', '.blocklyDatePicker .goog-date-picker-monthyear {', ' font-weight: bold;', '}', '.blocklyDatePicker .goog-date-picker-wday, ', '.blocklyDatePicker .goog-date-picker-other-month {', ' color: #70757a;', ' border-radius: 12px;', '}', '.blocklyDatePicker button,', '.blocklyDatePicker .goog-date-picker-date {', ' cursor: pointer;', ' background-color: rgb(218, 220, 224, 0);', ' border-radius: 12px;', ' transition: background-color,opacity 100ms linear;', '}', '.blocklyDatePicker button:hover,', '.blocklyDatePicker .goog-date-picker-date:hover {', ' background-color: rgb(218, 220, 224, .5);', '}' ]; Blockly.Field.register('field_date', Blockly.FieldDate);