diff options
Diffstat (limited to 'apps/system/js/value_selector')
-rw-r--r-- | apps/system/js/value_selector/date_picker.js | 568 | ||||
-rw-r--r-- | apps/system/js/value_selector/input_parser.js | 160 | ||||
-rw-r--r-- | apps/system/js/value_selector/spin_date_picker.js | 341 | ||||
-rw-r--r-- | apps/system/js/value_selector/value_picker.js | 222 | ||||
-rw-r--r-- | apps/system/js/value_selector/value_selector.js | 526 |
5 files changed, 0 insertions, 1817 deletions
diff --git a/apps/system/js/value_selector/date_picker.js b/apps/system/js/value_selector/date_picker.js deleted file mode 100644 index 9cdd484..0000000 --- a/apps/system/js/value_selector/date_picker.js +++ /dev/null @@ -1,568 +0,0 @@ -/** - * DatePicker is a html/js "widget" which will display - * all the days of a given month and allow selection of - * one specific day. It also implements controls to travel - * between months and jump into arbitrary time. - * - * The DatePicker itself contains no UI for the controls. - * - * Example usage: - * - * // the container will have elements for the month - * // added and removed from it. - * var picker = new DatePicker(container); - * - * // EVENTS: - * - * // called when the user clicks a day in the calendar. - * picker.onvaluechange = function(date) {} - * - * // called when the month of the calendar changes. - * // NOTE: at this time this can only happen programmatically - * // so there is only for control flow. - * picker.onmonthchange = function(date) {} - * - * // display a given year/month/date on the calendar the month - * // is zero based just like the JS date constructor. - * picker.display(2012, 0, 2); - * - * // move to the next month. - * picker.next(); - * - * // move to the previous month - * picker.previous(); - * - */ -var DatePicker = (function() { - 'use strict'; - - const SELECTED = 'selected'; - - var Calc = { - - NEXT_MONTH: 'next-month', - - OTHER_MONTH: 'other-month', - - PRESENT: 'present', - - FUTURE: 'future', - - PAST: 'past', - - get today() { - return new Date(); - }, - - daysInWeek: function() { - //XXX: We need to localize this... - return 7; - }, - - /** - * Checks is given date is today. - * - * @param {Date} date compare. - * @return {Boolean} true when today. - */ - isToday: function(date) { - return Calc.isSameDate(date, Calc.today); - }, - - /** - * Checks if two date objects occur - * on the same date (in the same month, year, day). - * Disregards time. - * - * @param {Date} first date. - * @param {Date} second date. - * @return {Boolean} true when they are the same date. - */ - isSameDate: function(first, second) { - return first.getMonth() == second.getMonth() && - first.getDate() == second.getDate() && - first.getFullYear() == second.getFullYear(); - }, - - /** - * Returns an identifier for a specific - * date in time for a given date - * - * @param {Date} date to get id for. - * @return {String} identifier. - */ - getDayId: function(date) { - return [ - date.getFullYear(), - date.getMonth(), - date.getDate() - ].join('-'); - }, - - /** - * Returns a date object from - * a string id for a date. - * - * @param {String} id identifier for date. - * @return {Date} date output. - */ - dateFromId: function(id) { - var parts = id.split('-'); - return new Date(parts[0], parts[1], parts[2]); - }, - - createDay: function(date, day, month, year) { - return new Date( - typeof year !== 'undefined' ? year : date.getFullYear(), - typeof month !== 'undefined' ? month : date.getMonth(), - typeof day !== 'undefined' ? day : date.getDate() - ); - }, - - /** - * Finds localized week start date of given date. - * - * @param {Date} date any day the week. - * @return {Date} first date in the week of given date. - */ - getWeekStartDate: function(date) { - var currentDay = date.getDay(); - var startDay = date.getDate() - currentDay; - - return Calc.createDay(date, startDay); - }, - - getWeekEndDate: function(date) { - // TODO: There are localization problems - // with this approach as we assume a 7 day week. - var start = Calc.getWeekStartDate(date); - start.setDate(start.getDate() + 7); - start.setMilliseconds(-1); - - return start; - }, - - /** - * Returns an array of dates objects. - * Inclusive. First and last are - * the given instances. - * - * @param {Date} start starting day. - * @param {Date} end ending day. - * @param {Boolean} includeTime include times start/end ? - */ - daysBetween: function(start, end, includeTime) { - if (!(start instanceof Date)) { - throw new Error('start date must be an instanceof Date'); - } - - if (!(end instanceof Date)) { - throw new Error('end date must be an instanceof Date'); - } - - if (start > end) { - var tmp = end; - end = start; - start = tmp; - tmp = null; - } - - var list = []; - var last = start.getDate(); - var cur; - - // for infinite loop protection. - var max = 500; - var macInc = 0; - - while (macInc++ < max) { - var next = new Date( - start.getFullYear(), - start.getMonth(), - ++last - ); - - if (next > end) { - throw new Error( - 'sanity fails next is greater then end' - ); - } - - if (!Calc.isSameDate(next, end)) { - list.push(next); - continue; - } - - break; - } - - if (includeTime) { - list.unshift(start); - list.push(end); - } else { - list.unshift(this.createDay(start)); - list.push(this.createDay(end)); - } - - return list; - }, - - /** - * Checks if date is in the past - * - * @param {Date} date to check. - * @return {Boolean} true when date is in the past. - */ - isPast: function(date) { - return (date.valueOf() < Calc.today.valueOf()); - }, - - /** - * Checks if date is in the future - * - * @param {Date} date to check. - * @return {Boolean} true when date is in the future. - */ - isFuture: function(date) { - return !Calc.isPast(date); - }, - - /** - * Based on the input date - * will return one of the following states - * - * past, present, future - * - * @param {Date} day for compare. - * @param {Date} month comparison month. - * @return {String} state. - */ - relativeState: function(day, month) { - var states; - //var today = Calc.today; - - // 1. the date is today (real time) - if (Calc.isToday(day)) { - return Calc.PRESENT; - } - - // 2. the date is in the past (real time) - if (Calc.isPast(day)) { - states = Calc.PAST; - // 3. the date is in the future (real time) - } else { - states = Calc.FUTURE; - } - - // 4. the date is not in the current month (relative time) - if (day.getMonth() !== month.getMonth()) { - states += ' ' + Calc.OTHER_MONTH; - } - - return states; - } - - }; - - /* expose calc */ - DatePicker.Calc = Calc; - - /** - * Initialize a date picker widget. - * - * @param {HTMLELement} element target of widget creation. - */ - function DatePicker(element) { - this.element = element; - // default time is set so next/previous work - // but we do not render the initial display here. - this._position = new Date(); - - // register events - element.addEventListener('click', this); - - //XXX: When the document is localized again - // we must also re-render the month because - // the week days may have changed? - // This will only happen when we change timezones - // unless we add this information to the locales. - } - - DatePicker.prototype = { - - /** - * Internal value not exposed so we can fire events - * when the getter/setter's are used. - * - * @type Date - */ - _value: null, - - SELECTED: 'selected', - - /** - * Gets current value - * - * @return {Null|Date} date or null. - */ - get value() { - return this._value; - }, - - /** - * Sets the current value of the date picker. - * When value differs from the currently set the - * `onvaluechange` event will be fired with the new/old value. - */ - set value(value) { - var old = this._value; - if (old !== value) { - this._value = value; - this._clearSelectedDay(value); - this.onvaluechange(value, old); - } - }, - - /** - * Clears the currently selected date of its 'selected' class. - * @private - */ - _clearSelectedDay: function(value) { - var target = this.element.querySelector('.' + SELECTED); - if (target) { - target.classList.remove(SELECTED); - } - }, - - handleEvent: function(e) { - switch (e.type) { - case 'click': - var target = e.target; - //XXX: if the html of the date elements changes - // this may also need to be altered as it - // assumes that there is no nesting of elements. - if (target.dataset.date) { - var date = Calc.dateFromId(target.dataset.date); - // order here is important as setting value will - // clear all the past selected dates... - this.value = date; - this._position = date; - // must come after setting selected date - target.classList.add(SELECTED); - } - break; - } - }, - - /** - * Getter is used for date normalization. - */ - get year() { - return this._position.getFullYear(); - }, - - /** - * Getter is used for date normalization. - */ - get month() { - return this._position.getMonth(); - }, - - get date() { - return this._position.getDate(); - }, - - /** - * Find the number of days in the given month/year. - * Month is zero based like the JS date constructor. - * - * @param {Numeric} year year value. - * @param {Numeric} month month value. - * @return {Numeric} number of days in month. - */ - _daysInMonth: function(year, month) { - var end = new Date(year, month + 1); - end.setMilliseconds(-1); - return end.getDate(); - }, - - /** - * Build the container for a day element. - * Each element has classes added to it based - * on what date it is created for. - * - * _today_ is based on today's actual date. - * Each date element also contains a data-date attribute - * with its current date as a string represented in - * the following format: "yyyy-mm-dd". - * - * Possible classes: - * - past - * - present (today) - * - future - * - other-month (day of another month but falls within same week) - * - * @param {Date} date date desired. - * @return {HTMLElement} dom element for day. - */ - _renderDay: function(date) { - var dayContainer = document.createElement('li'); - var dayEl = document.createElement('span'); - - dayContainer.className = Calc.relativeState( - date, - this._position - ); - - dayEl.dataset.date = Calc.getDayId(date); - dayEl.textContent = date.getDate(); - - dayContainer.appendChild(dayEl); - - return dayContainer; - }, - - /** - * Renders a set of dates and returns an ol element - * containing each date. - * - * @private - * @param {Array[Date]} dates array of dates. - * @return {HTMLELement} container for week. - */ - _renderWeek: function(dates) { - var container = document.createElement('ol'); - var i = 0; - var len = dates.length; - - for (; i < len; i++) { - container.appendChild( - this._renderDay(dates[i]) - ); - } - - return container; - }, - - /** - * Finds all dates in a given month by week. - * Includes leading and trailing days that occur - * outside the given year/month combination. - * - * @private - * @param {Numeric} year target year. - * @param {Numeric} month target month. - * @return {Array[Date]} array of dates. - */ - _getMonthDays: function(year, month) { - var date = new Date(year, month); - var dateEnd = new Date(year, month + 1); - dateEnd.setMilliseconds(-1); - - var start = Calc.getWeekStartDate(date); - var end = Calc.getWeekEndDate(dateEnd); - return Calc.daysBetween(start, end); - }, - - /** - * Returns a section element with all - * the days of the given month/year pair. - * - * Each month has a class for the number of weeks - * it contains. - * - * Possible values: - * - weeks-4 - * - weeks-5 - * - weeks-6 - * - * @private - */ - _renderMonth: function(year, month) { - var container = document.createElement('section'); - var days = this._getMonthDays(year, month); - var daysInWeek = Calc.daysInWeek(); - var weeks = days.length / daysInWeek; - var i = 0; - - container.classList.add('weeks-' + weeks); - - for (; i < weeks; i++) { - container.appendChild(this._renderWeek( - days.splice(0, daysInWeek) - )); - } - - return container; - }, - - /** - * Moves calendar one month into the future. - */ - next: function() { - this.display(this.year, this.month + 1, this.date); - }, - - /** - * Moves calendar one month into the past. - */ - previous: function() { - this.display(this.year, this.month - 1, this.date); - }, - - /** - * Primary method to display given month. - * Will remove the current display and replace - * it with the given month. - * - * @param {Numeric} year year to display. - * @param {Numeric} month month to display. - * @param {Numeric} date date to display. - */ - display: function(year, month, date) { - - // reset the date to the last date if overflow - var lastDate = new Date(year, month + 1, 0).getDate(); - if (lastDate < date) - date = lastDate; - - // Should come before render month - this._position = new Date(year, month, date); - - var element = this._renderMonth(year, month); - - if (this.monthDisplay) { - this.monthDisplay.parentNode.removeChild( - this.monthDisplay - ); - } - - this.monthDisplay = element; - this.element.appendChild(this.monthDisplay); - - this.onmonthchange(this._position); - - // Set the date as selected if presented - this._clearSelectedDay(); - if (date) { - var dayId = Calc.getDayId(this._position); - this.value = this._position; - var selector = '[data-date="' + dayId + '"]'; - var dateElement = document.querySelector(selector); - dateElement.classList.add(SELECTED); - } - }, - - /** - * Called when the month is changed. - */ - onmonthchange: function(month, year) {}, - - /** - * Called when the selected day changes. - */ - onvaluechange: function(date) {} - }; - - return DatePicker; -}()); diff --git a/apps/system/js/value_selector/input_parser.js b/apps/system/js/value_selector/input_parser.js deleted file mode 100644 index 5eef320..0000000 --- a/apps/system/js/value_selector/input_parser.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Stateless object for input parser functions.. - * The intent is the methods here will only relate to the parsing - * of input[type="date|time"] - */ - -ValueSelector.InputParser = (function() { - - var InputParser = { - _dateParts: ['year', 'month', 'date'], - _timeParts: ['hours', 'minutes', 'seconds'], - - /** - * Import HTML5 input[type="time"] string value - * - * @param {String} value 23:20:50.52, 17:39:57. - * @return {Object} { hours: 23, minutes: 20, seconds: 50 }. - */ - importTime: function(value) { - var result = { - hours: 0, - minutes: 0, - seconds: 0 - }; - - var parts = value.split(':'); - var part; - var partName; - - var i = 0; - var len = InputParser._timeParts.length; - - for (; i < len; i++) { - partName = InputParser._timeParts[i]; - part = parts[i]; - if (part) { - result[partName] = parseInt(part.slice(0, 2), 10) || 0; - } - } - - return result; - }, - - /** - * Export date to HTML5 input[type="time"] - * - * @param {Date} value export value. - * @return {String} 17:39:57. - */ - exportTime: function(value) { - var hour = value.getHours(); - var minute = value.getMinutes(); - var second = value.getSeconds(); - - var result = ''; - - result += InputParser.padNumber(hour) + ':'; - result += InputParser.padNumber(minute) + ':'; - result += InputParser.padNumber(second); - - return result; - }, - - /** - * Import HTML5 input[type="time"] to object. - * - * @param {String} value 1997-12-19. - * @return {Object} { year: 1997, month: 12, date: 19 }. - */ - importDate: function(value) { - var result = { - year: 0, - month: 0, - date: 0 - }; - - var parts = value.split('-'); - var part; - var partName; - - var i = 0; - var len = InputParser._dateParts.length; - - for (; i < len; i++) { - partName = InputParser._dateParts[i]; - part = parts[i]; - if (part) { - result[partName] = parseInt(part, 10); - } - } - - if (result.month > 0) { - result.month = result.month - 1; - } - - result.date = result.date || 1; - - return result; - }, - - /** - * Export js date to HTML5 input[type="date"] - * - * @param {Date} value export value. - * @return {String} date string (1997-12-19). - */ - exportDate: function(value) { - var year = value.getFullYear(); - var month = value.getMonth() + 1; - var date = value.getDate(); - - var result = ''; - - result += InputParser.padNumber(year) + '-'; - result += InputParser.padNumber(month) + '-'; - result += InputParser.padNumber(date); - - return result; - }, - - /** - * Designed to take a date & time value from - * html5 input types and returns a JS Date. - * - * @param {String} date input date. - * @param {String} time input time. - * - * @return {Date} full date object from date/time. - */ - formatInputDate: function(date, time) { - time = InputParser.importTime(time); - date = InputParser.importDate(date); - - return new Date( - date.year, - date.month, - date.date, - time.hours, - time.minutes, - time.seconds - ); - }, - - /** - * @param {Numeric} numeric value. - * @return {String} Pad the numeric with a leading zero if < 10. - */ - padNumber: function(numeric) { - var value = String(numeric); - if (numeric < 10) { - return '0' + value; - } - - return value; - } - }; - - return InputParser; - -}()); diff --git a/apps/system/js/value_selector/spin_date_picker.js b/apps/system/js/value_selector/spin_date_picker.js deleted file mode 100644 index feb0602..0000000 --- a/apps/system/js/value_selector/spin_date_picker.js +++ /dev/null @@ -1,341 +0,0 @@ -/** - * SpinDatePicker is a html/js "widget" which enables users - * pick a specific date. It display the date in the way based - * on the language setting. - * - * The SpinDatePicker itself contains no UI for the controls. - * - * Example usage: - * - * // All necessary UI elements are contained in the root element. - * var picker = new SpinDatePicker(root); - * picker.value = new Date(); - * // after users pick a date - * var newDate = picker.value; - */ -var SpinDatePicker = (function SpinDatePicker() { - 'use strict'; - - var FIRST_YEAR = 1900; - var LAST_YEAR = 2099; - - function getYearText() { - var yearText = []; - var dateTimeFormat = navigator.mozL10n.DateTimeFormat(); - - for (var i = FIRST_YEAR; i <= LAST_YEAR; i++) { - var date = new Date(i, 0, 1); - yearText.push(dateTimeFormat.localeFormat(date, '%Y')); - } - - return yearText; - } - - function getMonthText() { - var monthText = []; - var date = new Date(0); - var dateTimeFormat = navigator.mozL10n.DateTimeFormat(); - - for (var i = 0; i < 12; i++) { - date.setMonth(i); - monthText.push(dateTimeFormat.localeFormat(date, '%B')); - } - - return monthText; - } - - function getDateText(days) { - var dateText = []; - var date = new Date(0); - var dateTimeFormat = navigator.mozL10n.DateTimeFormat(); - - for (var i = 1; i <= days; i++) { - date.setDate(i); - dateText.push(dateTimeFormat.localeFormat(date, '%d')); - } - - return dateText; - } - - function getDaysInMonth(year, month) { - var date = new Date(year, month + 1, 0); - return date.getDate(); - } - - /** - * Get the order of date components. - * - * @param {String} date format. - */ - function getDateComponentOrder(format) { - var format = navigator.mozL10n.get('dateTimeFormat_%x'); - var order = ''; - var tokens = format.match(/(%E.|%O.|%.)/g); - - if (tokens) { - tokens.forEach(function(token) { - switch (token) { - case '%Y': - case '%y': - case '%Oy': - case 'Ey': - case 'EY': - order += 'Y'; - break; - case '%B': - case '%b': - case '%m': - case '%Om': - order += 'M'; - break; - case '%d': - case '%e': - case '%Od': - case '%Oe': - order += 'D'; - break; - } - }); - } - - if (order.length != 3) - order = 'DMY'; - - return order; - } - - /** - * Initialize a date picker widget. - * - * @param {HTMLELement} element target of widget creation. - */ - function SpinDatePicker(element) { - this.element = element; - - this.yearPicker = null; - this.monthPicker = null; - this.datePickers = { - '28': null, - '29': null, - '30': null, - '31': null - }; - - //XXX: When the document is localized again - // we must also re-render the month because - // the week days may have changed? - // This will only happen when we change timezones - // unless we add this information to the locales. - - var pickerContainer = - element.querySelector('.picker-container'); - var yearPickerContainer = - element.querySelector('.value-picker-year'); - var monthPickerContainer = - element.querySelector('.value-picker-month'); - var tmpDatePickerContainers = - element.querySelectorAll('.value-picker-date'); - var datePickerContainers = { - '28': tmpDatePickerContainers[0], - '29': tmpDatePickerContainers[1], - '30': tmpDatePickerContainers[2], - '31': tmpDatePickerContainers[3] - }; - - var updateCurrentValue = function spd_updateCurrentValue() { - var selectedYear = this.yearPicker.getSelectedIndex() + FIRST_YEAR; - var selectedMonth = this.monthPicker.getSelectedIndex(); - var days = getDaysInMonth(selectedYear, selectedMonth); - var datePicker = this.datePickers[days]; - var selectedDate = datePicker.getSelectedIndex() + 1; - - this._value = new Date(selectedYear, selectedMonth, selectedDate); - }; - - var updateDatePickerVisibility = - function spd_updateDatePickerVisibility() { - var days = getDaysInMonth(this.yearPicker.getSelectedIndex() + - FIRST_YEAR, this.monthPicker.getSelectedIndex()); - for (var i = 28; i <= 31; i++) { - datePickerContainers[i].hidden = true; - this.datePickers[i].setSelectedIndex(this._currentSelectedDateIndex); - } - datePickerContainers[days].hidden = false; - }; - - var onvaluechangeInternal = - function spd_onvaluechangeInternal(newDateValue) { - this.yearPicker.setSelectedIndex(newDateValue.getFullYear() - FIRST_YEAR); - this.monthPicker.setSelectedIndex(newDateValue.getMonth()); - for (var i = 28; i <= 31; i++) { - this.datePickers[i].setSelectedIndex(newDateValue.getDate() - 1); - } - updateDatePickerVisibility.apply(this); - updateCurrentValue.apply(this); - }; - - var onSelectedYearChanged = - function spd_onSelectedYearChanged(selectedYear) { - updateDatePickerVisibility.apply(this); - updateCurrentValue.apply(this); - }; - - var onSelectedMonthChanged = - function spd_onSelectedMonthChanged(selectedMonth) { - updateDatePickerVisibility.apply(this); - updateCurrentValue.apply(this); - }; - - var onSelectedDateChanged = - function spd_onSelectedDateChanged(selectedDate) { - this._currentSelectedDateIndex = selectedDate; - updateCurrentValue.apply(this); - }; - - var unitClassName = 'picker-unit'; - - // year value picker - var yearUnitStyle = { - valueDisplayedText: getYearText(), - className: unitClassName - }; - if (this.yearPicker) - this.yearPicker.uninit(); - this.yearPicker = new ValuePicker(yearPickerContainer, yearUnitStyle); - this.yearPicker.onselectedindexchange = onSelectedYearChanged.bind(this); - - // month value picker - var monthUnitStyle = { - valueDisplayedText: getMonthText(), - className: unitClassName - }; - if (this.monthPicker) - this.monthPicker.uninit(); - this.monthPicker = - new ValuePicker(monthPickerContainer, monthUnitStyle); - this.monthPicker.onselectedindexchange = onSelectedMonthChanged.bind(this); - - // date value picker - for (var i = 28; i <= 31; i++) { - var datePickerContainer = datePickerContainers[i]; - var dateUnitStyle = { - valueDisplayedText: getDateText(i), - className: unitClassName - }; - var datePicker = this.datePickers[i]; - - if (datePicker) - datePicker.uninit(); - datePickerContainer.hidden = false; - this.datePickers[i] = new ValuePicker(datePickerContainer, dateUnitStyle); - this.datePickers[i].onselectedindexchange = - onSelectedDateChanged.bind(this); - } - - // set component order - var dateComponentOrder = getDateComponentOrder(); - var pickerClassList = pickerContainer.classList; - pickerClassList.remove('YMD'); - pickerClassList.remove('DMY'); - pickerClassList.remove('MDY'); - pickerClassList.add(dateComponentOrder); - - // Prevent focus being taken away by us for time picker. - // The event listener on outer box will not be triggered cause - // there is a evt.stopPropagation() in value_picker.js - this.pickerElements = [monthPickerContainer, yearPickerContainer]; - for (var i = 28; i <= 31; i++) { - this.pickerElements.push(datePickerContainers[i]); - } - - this.pickerElements.forEach((function pickerElements_forEach(picker) { - picker.addEventListener('mousedown', this); - }).bind(this)); - - this.onvaluechangeInternal = onvaluechangeInternal.bind(this); - } - - SpinDatePicker.prototype = { - - /** - * Internal value not exposed so we can fire events - * when the getter/setter's are used. - * - * @type Date - */ - _value: null, - - /** - * Gets current value - * - * @return {Null|Date} date or null. - */ - get value() { - return this._value; - }, - - /** - * Sets the current value of the date picker. - * When value differs from the currently set the - * `onvaluechange` event will be fired with the new/old value. - */ - set value(value) { - var old = this._value; - if (old !== value) { - this._value = value; - this.onvaluechangeInternal(value); - } - }, - - /** - * Getter is used for date normalization. - */ - get year() { - return this._value.getFullYear(); - }, - - /** - * Getter is used for date normalization. - */ - get month() { - return this._value.getMonth(); - }, - - get date() { - return this._value.getDate(); - }, - - handleEvent: function vs_handleEvent(evt) { - switch (evt.type) { - case 'mousedown': - // Prevent focus being taken away by us. - evt.preventDefault(); - break; - } - }, - - uninit: function() { - if (this.yearPicker) - this.yearPicker.uninit(); - if (this.monthPicker) - this.monthPicker.uninit(); - if (this.datePickers) { - for (var i = 28; i <= 31; i++) { - var datePicker = this.datePickers[i]; - datePicker.uninit(); - } - } - - this.pickerElements.forEach((function pickerElements_forEach(picker) { - picker.removeEventListener('mousedown', this); - }).bind(this)); - }, - - /** - * Called when the selected date changes. - */ - onvaluechangeInternal: function(date) {} - }; - - return SpinDatePicker; -}()); diff --git a/apps/system/js/value_selector/value_picker.js b/apps/system/js/value_selector/value_picker.js deleted file mode 100644 index 34e686f..0000000 --- a/apps/system/js/value_selector/value_picker.js +++ /dev/null @@ -1,222 +0,0 @@ -var ValuePicker = (function() { - // - // Constructor - // - function VP(e, unitStyle) { - this.element = e; - this._valueDisplayedText = unitStyle.valueDisplayedText; - this._unitClassName = unitStyle.className; - this._lower = 0; - this._upper = unitStyle.valueDisplayedText.length - 1; - this._range = unitStyle.valueDisplayedText.length; - this._currentIndex = 0; - this.init(); - } - - // - // Public methods - // - VP.prototype.getSelectedIndex = function() { - var selectedIndex = this._currentIndex; - return selectedIndex; - }; - - VP.prototype.getSelectedDisplayedText = function() { - var displayedText = this._valueDisplayedText[this._currentIndex]; - return displayedText; - }; - - VP.prototype.setSelectedIndex = function(tunedIndex, ignorePicker) { - if ((tunedIndex % 1) > 0.5) { - tunedIndex = Math.floor(tunedIndex) + 1; - } else { - tunedIndex = Math.floor(tunedIndex); - } - - if (tunedIndex < this._lower) { - tunedIndex = this._lower; - } - - if (tunedIndex > this._upper) { - tunedIndex = this._upper; - } - - if (this._currentIndex != tunedIndex) { - this._currentIndex = tunedIndex; - this.onselectedindexchange(this._currentIndex); - } - this.updateUI(tunedIndex, ignorePicker); - - return tunedIndex; - }; - - VP.prototype.setSelectedIndexByDisplayedText = function(displayedText) { - var newIndex = this._valueDisplayedText.indexOf(displayedText); - if (newIndex != -1) { - if (this._currentIndex != newIndex) { - this._currentIndex = newIndex; - this.onselectedindexchange(this._currentIndex); - } - this.updateUI(newIndex); - } - }; - - // - // Internal methods - // - VP.prototype.init = function() { - this.initUI(); - this.setSelectedIndex(0); // Default Index is zero - this.mousedonwHandler = vp_mousedown.bind(this); - this.mousemoveHandler = vp_mousemove.bind(this); - this.mouseupHandler = vp_mouseup.bind(this); - this.addEventListeners(); - }; - - VP.prototype.initUI = function() { - var lower = this._lower; - var upper = this._upper; - var unitCount = this._valueDisplayedText.length; - for (var i = 0; i < unitCount; ++i) { - this.addPickerUnit(i); - } - // cache the size of picker - this._pickerUnits = this.element.children; - this._pickerUnitsHeight = this._pickerUnits[0].clientHeight; - this._pickerHeight = this._pickerUnits[0].clientHeight * - this._pickerUnits.length; - this._space = this._pickerHeight / this._range; - }; - - VP.prototype.addPickerUnit = function(index) { - var html = this._valueDisplayedText[index]; - var unit = document.createElement('div'); - unit.className = this._unitClassName; - unit.innerHTML = html; - this.element.appendChild(unit); - }; - - VP.prototype.updateUI = function(index, ignorePicker) { - if (true !== ignorePicker) { - this.element.style.top = - (this._lower - index) * this._space + 'px'; - } - }; - - VP.prototype.addEventListeners = function() { - this.element.addEventListener('mousedown', this.mousedonwHandler, false); - }; - - VP.prototype.removeEventListeners = function() { - this.element.removeEventListener('mouseup', this.mouseupHandler, false); - this.element.removeEventListener('mousemove', this.mousemoveHandler, false); - }; - - VP.prototype.uninit = function() { - this.element.removeEventListener('mousedown', this.mousedonwHandler, false); - this.element.removeEventListener('mouseup', this.mouseupHandler, false); - this.element.removeEventListener('mousemove', this.mousemoveHandler, false); - this.element.style.top = '0px'; - this.onselectedindexchange = null; - empty(this.element); - }; - - VP.prototype.onselectedindexchange = function(index) {}; - - function cloneEvent(evt) { - if ('touches' in evt) { - evt = evt.touches[0]; - } - return { x: evt.pageX, y: evt.pageY, timestamp: evt.timeStamp }; - } - - function empty(element) { - while (element.hasChildNodes()) - element.removeChild(element.lastChild); - element.innerHTML = ''; - } - - // - // Tuneable parameters - // - var SPEED_THRESHOLD = 0.1; - var currentEvent, startEvent, currentSpeed; - var tunedIndex = 0; - - function toFixed(value) { - return parseFloat(value.toFixed(1)); - } - - function calcSpeed() { - var movingSpace = startEvent.y - currentEvent.y; - var deltaTime = currentEvent.timestamp - startEvent.timestamp; - var speed = movingSpace / deltaTime; - currentSpeed = parseFloat(speed.toFixed(2)); - } - - function calcTargetIndex(space) { - return tunedIndex - getMovingSpace() / space; - } - - // If the user swap really slow, narrow down the moving space - // So the user can fine tune value. - function getMovingSpace() { - var movingSpace = currentEvent.y - startEvent.y; - var reValue = Math.abs(currentSpeed) > SPEED_THRESHOLD ? - movingSpace : movingSpace / 4; - return reValue; - } - - function vp_mousemove(event) { - event.stopPropagation(); - event.target.setCapture(true); - currentEvent = cloneEvent(event); - - calcSpeed(); - - // move selected index - this.element.style.top = parseFloat(this.element.style.top) + - getMovingSpace() + 'px'; - - tunedIndex = calcTargetIndex(this._space); - var roundedIndex = Math.round(tunedIndex * 10) / 10; - - if (roundedIndex != this._currentIndex) { - this.setSelectedIndex(toFixed(roundedIndex), true); - } - - startEvent = currentEvent; - } - - function vp_mouseup(event) { - event.stopPropagation(); - this.removeEventListeners(); - - // Add animation back - this.element.classList.add('animation-on'); - - // Add momentum if speed is higher than a given threshold. - if (Math.abs(currentSpeed) > SPEED_THRESHOLD) { - var direction = currentSpeed > 0 ? 1 : -1; - tunedIndex += Math.min(Math.abs(currentSpeed) * 5, 5) * direction; - } - tunedIndex = this.setSelectedIndex(toFixed(tunedIndex)); - currentSpeed = 0; - } - - function vp_mousedown(event) { - event.stopPropagation(); - - // Stop animation - this.element.classList.remove('animation-on'); - - startEvent = currentEvent = cloneEvent(event); - tunedIndex = this._currentIndex; - - this.removeEventListeners(); - this.element.addEventListener('mousemove', this.mousemoveHandler, false); - this.element.addEventListener('mouseup', this.mouseupHandler, false); - } - - return VP; -}()); diff --git a/apps/system/js/value_selector/value_selector.js b/apps/system/js/value_selector/value_selector.js deleted file mode 100644 index b3381b3..0000000 --- a/apps/system/js/value_selector/value_selector.js +++ /dev/null @@ -1,526 +0,0 @@ -/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ - -'use strict'; - -var ValueSelector = { - - _containers: {}, - _popups: {}, - _buttons: {}, - _datePicker: null, - - debug: function(msg) { - var debugFlag = false; - if (debugFlag) { - console.log('[ValueSelector] ', msg); - } - }, - - init: function vs_init() { - - var self = this; - - window.navigator.mozKeyboard.onfocuschange = function onfocuschange(evt) { - var typeToHandle = ['select-one', 'select-multiple', 'date', - 'time', 'datetime', 'datetime-local', 'blur']; - - var type = evt.detail.type; - // handle the <select> element and inputs with type of date/time - // in system app for now - if (typeToHandle.indexOf(type) == -1) - return; - - var currentValue = evt.detail.value; - - switch (evt.detail.type) { - case 'select-one': - case 'select-multiple': - self.debug('select triggered' + JSON.stringify(evt.detail)); - self._currentPickerType = evt.detail.type; - self.showOptions(evt.detail); - break; - - case 'date': - self.showDatePicker(currentValue); - break; - - case 'time': - self.showTimePicker(currentValue); - break; - - case 'datetime': - case 'datetime-local': - // TODO - break; - case 'blur': - self.hide(); - break; - } - }; - - this._element = document.getElementById('value-selector'); - this._element.addEventListener('mousedown', this); - this._containers['select'] = - document.getElementById('value-selector-container'); - this._containers['select'].addEventListener('click', this); - ActiveEffectHelper.enableActive(this._containers['select']); - - this._popups['select'] = - document.getElementById('select-option-popup'); - this._popups['select'].addEventListener('submit', this); - this._popups['time'] = - document.getElementById('time-picker-popup'); - this._popups['date'] = - document.getElementById('spin-date-picker-popup'); - - this._buttons['select'] = document.getElementById('select-options-buttons'); - this._buttons['select'].addEventListener('click', this); - - this._buttons['time'] = document.getElementById('time-picker-buttons'); - this._buttons['time'].addEventListener('click', this); - this._buttons['date'] = document.getElementById('spin-date-picker-buttons'); - - this._buttons['date'].addEventListener('click', this); - - this._containers['time'] = document.getElementById('picker-bar'); - this._containers['date'] = document.getElementById('spin-date-picker'); - - ActiveEffectHelper.enableActive(this._buttons['select']); - ActiveEffectHelper.enableActive(this._buttons['time']); - ActiveEffectHelper.enableActive(this._buttons['date']); - - // Prevent focus being taken away by us for time picker. - // The event listener on outer box will not be triggered cause - // there is a evt.stopPropagation() in value_picker.js - var pickerElements = ['value-picker-hours', 'value-picker-minutes', - 'value-picker-hour24-state']; - - pickerElements.forEach((function pickerElements_forEach(id) { - var element = document.getElementById(id); - element.addEventListener('mousedown', this); - }).bind(this)); - - window.addEventListener('appopen', this); - window.addEventListener('appwillclose', this); - - // invalidate the current spin date picker when language setting changes - navigator.mozSettings.addObserver('language.current', - (function language_change(e) { - if (this._datePicker) { - this._datePicker.uninit(); - this._datePicker = null; - }}).bind(this)); - }, - - handleEvent: function vs_handleEvent(evt) { - switch (evt.type) { - case 'appopen': - case 'appwillclose': - this.hide(); - break; - - case 'click': - var currentTarget = evt.currentTarget; - switch (currentTarget) { - case this._buttons['select']: - case this._buttons['time']: - case this._buttons['date']: - var target = evt.target; - if (target.dataset.type == 'cancel') { - this.cancel(); - } else if (target.dataset.type == 'ok') { - this.confirm(); - } - break; - - case this._containers['select']: - this.handleSelect(evt.target); - break; - } - break; - - case 'submit': - // Prevent the form from submit. - case 'mousedown': - // Prevent focus being taken away by us. - evt.preventDefault(); - break; - - default: - this.debug('no event handler defined for' + evt.type); - break; - } - }, - - handleSelect: function vs_handleSelect(target) { - - if (target.dataset === undefined || - (target.dataset.optionIndex === undefined && - target.dataset.optionValue === undefined)) - return; - - if (this._currentPickerType === 'select-one') { - var selectee = this._containers['select']. - querySelectorAll('[aria-checked="true"]'); - for (var i = 0; i < selectee.length; i++) { - selectee[i].removeAttribute('aria-checked'); - } - - target.setAttribute('aria-checked', 'true'); - } else if (target.getAttribute('aria-checked') === 'true') { - target.removeAttribute('aria-checked'); - } else { - target.setAttribute('aria-checked', 'true'); - } - - // setValue here to trigger change event - var singleOptionIndex; - var optionIndices = []; - - var selectee = this._containers['select']. - querySelectorAll('[aria-checked="true"]'); - - if (this._currentPickerType === 'select-one') { - - if (selectee.length > 0) - singleOptionIndex = selectee[0].dataset.optionIndex; - - window.navigator.mozKeyboard.setSelectedOption(singleOptionIndex); - - } else if (this._currentPickerType === 'select-multiple') { - // Multiple select case - for (var i = 0; i < selectee.length; i++) { - - var index = parseInt(selectee[i].dataset.optionIndex); - optionIndices.push(index); - } - - window.navigator.mozKeyboard.setSelectedOptions(optionIndices); - } - - }, - - show: function vs_show(detail) { - this._element.hidden = false; - }, - - showPanel: function vs_showPanel(type) { - for (var p in this._containers) { - if (p === type) { - this._popups[p].hidden = false; - } else { - this._popups[p].hidden = true; - } - } - }, - - hide: function vs_hide() { - this._element.hidden = true; - }, - - cancel: function vs_cancel() { - this.debug('cancel invoked'); - window.navigator.mozKeyboard.removeFocus(); - this.hide(); - }, - - confirm: function vs_confirm() { - - if (this._currentPickerType === 'time') { - - var timeValue = TimePicker.getTimeValue(); - this.debug('output value: ' + timeValue); - - window.navigator.mozKeyboard.setValue(timeValue); - } else if (this._currentPickerType === 'date') { - var dateValue = this._datePicker.value; - // The format should be 2012-09-19 - dateValue = dateValue.toLocaleFormat('%Y-%m-%d'); - this.debug('output value: ' + dateValue); - window.navigator.mozKeyboard.setValue(dateValue); - } - - window.navigator.mozKeyboard.removeFocus(); - this.hide(); - }, - - showOptions: function vs_showOptions(detail) { - - var options = null; - if (detail.choices && detail.choices.choices) - options = detail.choices.choices; - - if (options) - this.buildOptions(options); - - this.show(); - this.showPanel('select'); - }, - - buildOptions: function(options) { - - var optionHTML = ''; - - function escapeHTML(str) { - var span = document.createElement('span'); - span.textContent = str; - return span.innerHTML; - } - - for (var i = 0, n = options.length; i < n; i++) { - - var checked = options[i].selected ? ' aria-checked="true"' : ''; - - // This for attribute is created only to avoid applying - // a general rule in building block - var forAttribute = ' for="gaia-option-' + options[i].optionIndex + '"'; - - optionHTML += '<li data-option-index="' + options[i].optionIndex + '"' + - checked + '>' + - '<label' + forAttribute + '> <span>' + - escapeHTML(options[i].text) + - '</span></label>' + - '</li>'; - } - - var optionsContainer = document.querySelector( - '#value-selector-container ol'); - if (!optionsContainer) - return; - - optionsContainer.innerHTML = optionHTML; - - - // Apply different style when the options are more than 1 page - if (options.length > 5) { - this._containers['select'].classList.add('scrollable'); - } else { - this._containers['select'].classList.remove('scrollable'); - } - - // Change the title for multiple select - var titleL10nId = 'choose-options'; - if (this._currentPickerType === 'select-one') - titleL10nId = 'choose-option'; - - var optionsTitle = document.querySelector( - '#value-selector-container h1'); - - if (optionsTitle) { - optionsTitle.dataset.l10nId = titleL10nId; - optionsTitle.textContent = navigator.mozL10n.get(titleL10nId); - } - }, - - showTimePicker: function vs_showTimePicker(currentValue) { - this._currentPickerType = 'time'; - this.show(); - this.showPanel('time'); - - if (!this._timePickerInitialized) { - TimePicker.initTimePicker(); - this._timePickerInitialized = true; - } - - var time; - if (!currentValue) { - var now = new Date(); - time = { - hours: now.getHours(), - minutes: now.getMinutes() - }; - } else { - var inputParser = ValueSelector.InputParser; - if (!inputParser) - console.error('Cannot get input parser for value selector'); - - time = inputParser.importTime(currentValue); - } - - var timePicker = TimePicker.timePicker; - // Set the value of time picker according to the current value - if (timePicker.is12hFormat) { - var hour = (time.hours % 12); - hour = (hour == 0) ? 12 : hour; - // 24-hour state value selector: AM = 0, PM = 1 - var hour24State = (time.hours >= 12) ? 1 : 0; - timePicker.hour.setSelectedIndexByDisplayedText(hour); - timePicker.hour24State.setSelectedIndex(hour24State); - } else { - timePicker.hour.setSelectedIndex(time.hours); - } - - timePicker.minute.setSelectedIndex(time.minutes); - }, - - showDatePicker: function vs_showDatePicker(currentValue) { - this._currentPickerType = 'date'; - this.show(); - this.showPanel('date'); - - if (!this._datePicker) { - this._datePicker = new SpinDatePicker(this._containers['date']); - } - - // Show current date as default value - var date = new Date(); - if (currentValue) { - var inputParser = ValueSelector.InputParser; - if (!inputParser) - console.error('Cannot get input parser for value selector'); - - date = inputParser.formatInputDate(currentValue, ''); - } - this._datePicker.value = date; - } - -}; - -var TimePicker = { - timePicker: { - hour: null, - minute: null, - hour24State: null, - is12hFormat: false - }, - - get hourSelector() { - delete this.hourSelector; - return this.hourSelector = - document.getElementById('value-picker-hours'); - }, - - get minuteSelector() { - delete this.minuteSelector; - return this.minuteSelector = - document.getElementById('value-picker-minutes'); - }, - - get hour24StateSelector() { - delete this.hour24StateSelector; - return this.hour24StateSelector = - document.getElementById('value-picker-hour24-state'); - }, - - initTimePicker: function tp_initTimePicker() { - var localeTimeFormat = navigator.mozL10n.get('dateTimeFormat_%X'); - var is12hFormat = (localeTimeFormat.indexOf('%p') >= 0); - this.timePicker.is12hFormat = is12hFormat; - this.setTimePickerStyle(); - var startHour = is12hFormat ? 1 : 0; - var endHour = is12hFormat ? (startHour + 12) : (startHour + 12 * 2); - var unitClassName = 'picker-unit'; - var hourDisplayedText = []; - for (var i = startHour; i < endHour; i++) { - var value = i; - hourDisplayedText.push(value); - } - var hourUnitStyle = { - valueDisplayedText: hourDisplayedText, - className: unitClassName - }; - this.timePicker.hour = new ValuePicker(this.hourSelector, hourUnitStyle); - - var minuteDisplayedText = []; - for (var i = 0; i < 60; i++) { - var value = (i < 10) ? '0' + i : i; - minuteDisplayedText.push(value); - } - var minuteUnitStyle = { - valueDisplayedText: minuteDisplayedText, - className: unitClassName - }; - this.timePicker.minute = - new ValuePicker(this.minuteSelector, minuteUnitStyle); - - if (is12hFormat) { - var hour24StateUnitStyle = { - valueDisplayedText: ['AM', 'PM'], - className: unitClassName - }; - this.timePicker.hour24State = - new ValuePicker(this.hour24StateSelector, hour24StateUnitStyle); - } - }, - - setTimePickerStyle: function tp_setTimePickerStyle() { - var style = (this.timePicker.is12hFormat) ? 'format12h' : 'format24h'; - document.getElementById('picker-bar').classList.add(style); - }, - - // return a string for the time value, format: "16:37" - getTimeValue: function tp_getTimeValue() { - var hour = 0; - if (this.timePicker.is12hFormat) { - var hour24Offset = 12 * this.timePicker.hour24State.getSelectedIndex(); - hour = this.timePicker.hour.getSelectedDisplayedText(); - hour = (hour == 12) ? 0 : hour; - hour = hour + hour24Offset; - } else { - hour = this.timePicker.hour.getSelectedIndex(); - } - var minute = this.timePicker.minute.getSelectedDisplayedText(); - - return hour + ':' + minute; - } -}; - -var ActiveEffectHelper = (function() { - - var lastActiveElement = null; - - function _setActive(element, isActive) { - if (isActive) { - element.classList.add('active'); - lastActiveElement = element; - } else { - element.classList.remove('active'); - if (lastActiveElement) { - lastActiveElement.classList.remove('active'); - lastActiveElement = null; - } - } - } - - function _onMouseDown(evt) { - var target = evt.target; - - _setActive(target, true); - target.addEventListener('mouseleave', _onMouseLeave); - } - - function _onMouseUp(evt) { - var target = evt.target; - - _setActive(target, false); - target.removeEventListener('mouseleave', _onMouseLeave); - } - - function _onMouseLeave(evt) { - var target = evt.target; - _setActive(target, false); - target.removeEventListener('mouseleave', _onMouseLeave); - } - - var _events = { - 'mousedown': _onMouseDown, - 'mouseup': _onMouseUp - }; - - function _enableActive(element) { - // Attach event listeners - for (var event in _events) { - var callback = _events[event] || null; - if (callback) - element.addEventListener(event, callback); - } - } - - return { - enableActive: _enableActive - }; - -})(); - -ValueSelector.init(); |