diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8653ebf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.tmp* +*~ +.sass-cache/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b424bc --- /dev/null +++ b/README.md @@ -0,0 +1,386 @@ +# Fork Improvements + +- Months and years are now displayed in tables instead of divs. +- A bootstrap-like theme is available. Sources are written in SCSS and can be easily modified as the positionning is no longer done in absolute. + +MooTools-DatePicker +=================== + +MooTools DatePicker is a nice modular and themable DatePicker. It has many features and options, such as +year, month, day or timepicker only modes, min and max dates, Localization and a lot more. + +This Plugin makes use of MooTools' Locale and Date plugins in MooTools More, to provide a localized +datepicker, as well as easy formatting and parsing Dates. + +This DatePicker is a fork of the original [MonkeyPhysics DatePicker](http://www.monkeyphysics.com/mootools/script/2/datepicker), +and has improved a lot since then. Though it should be (almost) backward compatible. + + + +As of version 1.60 the datepicker will only work with MooTools 1.3. + +How to use +---------- + +Below you will find a description and some docs how you can use the datepicker. +If you find any weird things, please create a ticket at github or fork and fix it! + +The DatePicker consists out of three layers, a Picker class, which can be used to create any form of Picker, a Picker.Attach class, +which handles stuff like attaching the Picker to a input or anchor element. Finally there is the DatePicker class, which you'll probably +use. Every option of the Picker or Picker.Attach classes can be used in the DatePicker class. + +Basic Example +------------- + +First you need to include the following html tags: + + #HTML + + + + + + + +Then you can simply use, for example: + + #JS + new Picker.Date($$('input'), { + timePicker: true, + positionOffset: {x: 5, y: 0}, + pickerClass: 'datepicker_dashboard', + useFadeInOut: !Browser.ie + }); + + +### Theming: + +Theming is done with CSS files, there are five themes available, which you can find in the Source folder. + +Just include the CSS file and set the `pickerClass` option. + + +### Localization + +The DatePicker uses the MooTools More Date Class, which already includes many localized strings. +For some specific strings DatePicker has its own localizations, which you can find in the Locale.__-__.DatePicker.js files. +Just include the file in your page with a script tag to use the translations. + +Currently the following languages are supported + +- af-ZA +- cs-CZ +- de-DE +- en-US +- es-ES +- fr-FR +- he-IL +- hu-HU +- it-IT +- nl-NL +- pl-PL +- pt-BR +- ru-RU +- sv-SE +- uk-UA + +You can set the current language with: + + #JS + Locale.use('nl-NL'); + + +Class: DatePicker +----------------- + +### Syntax + + #JS + var dp = new DatePicker([element, options]); + +### Arguments + +1. element: (*element*, *string*, *array*) The element(s) to attach the datepicker to +2. options: (*object*, optional) The options object + +### Options: + +All the options of the Picker and Picker.Attach classes, and: + +- minDate: (*Date instance*, *string*, defaults to `null`) Minimum date allowed to pick. Blocks anything before. +- maxDate: (*Date instance*, *string*, defaults to `null`) Maximum date allowed to pick. Blocks anything after. +- availableDates: (*object*, defaults to `null`) When only a few dates should be selectable. An object like `{2011: {1: [19, 29, 31], 3: [5, 19, 24]}}` with all the dates (year -> months -> days). +- invertAvailable: (*boolean*, defaults to `false`) Invert the `availableDates` option. +- format: (*string*, defaults to the default localized format) The format to output into the input field. Uses [Date.format](http://mootools.net/docs/more/Types/Date#Date:format) +- timePicker: (*boolean*, defaults to 1 `false`) Enable/disable timepicker functionality. Hours/Minutes values can be changed using the scrollwheel. +- timeWheelStep: (*number*, defaults to `1`) The number of minutes the minutes field will change in the timepicker when using the scrollwheel, for example 5, 10, 15. The value will always be k * timeWheelStep. +- yearPicker: (*boolean*, defaults to `true`) Enable/disable yearpicker functionality. Makes it much easier to change years. +- yearPerPage: (*number*, defaults to `20`) Amount of years to show in the year-picking view. Be aware that this may affect your layout. +- startView: (*string*, defaults to `days`) The view that will be showed when the picker opens. The options are `time`, `days`, `months` and `years` +- startDay: (*number*, defaults to 1) The day number for the start of the week. 0 - sunday, 1 - monday, etc. +- openLastView: (*boolean*, defaults to `false`) Opens the last opened view after the picker is opened again, instead of the `startView` +- pickOnly: (*string*, defaults to `false`) If you just want to pick a year, month, day or time. The options are `time`, `days`, `months` and `years` +- canAlwaysGoUp: (*array*, defaults to `['months', 'days']`) The views where you can click the title to go up. The options are `time`, `days`, `months` and `years` +- updateAll (*boolean*, defaults to `false`) whether or not to update all inputs when selecting a date +- weeknumbers (*boolean*, defaults to `false`) display weeknumbers for the `days` view +- months_abbr: (*array*) An array with the month name abbreviations. If nothing is set, it will automatically use MooTools Locale to get the abbreviations +- days_abbr: (*array*) An array with the day name abbreviations. If nothing is set, it will automatically use MooTools Locale to get the abbreviations +- years_title: (*function*, defaults to a function which returns `year + '-' + (year + options.yearsPerPage - 1)`) A function that returns the title for the yearpicker with as arguments the date object and the options object. +- months_title: (*function*, defaults to a function which returns `date.format('%b %Y')`) A function that returns the title for the monthpicker with as arguments the date object and the options object. +- days_title: (*function*, defaults to a function which returns `date.format('%b %Y')`) A function that returns the title for the daypicker with as arguments the date object and the options object. +- time_title: (*function*, defaults to a function which returns `(options.pickOnly == 'time') ? Locale.get('DatePicker.select_a_time') : date.format('%d %B, %Y')`) A function that returns the title for the timepicker with as arguments the date object and the options object. + + + +### Events: + +- onSelect: Will fire when the user has selected a date + +#### signature + + #JS + onSelect(date) + +#### arguments + +1. date - A Date object. You could use [Date.format](http://mootools.net/docs/more/Types/Date#Date:format) to format it into a string. For example to set it into a hidden field which will be sent to the server. + + +### Examples + + #JS + new DatePicker('inputField', { + timePicker: true, + pickerClass: 'datepicker_jqui', + onSelect: function(date){ + myHiddenField.set('value', date.format('%s')); + } + }); + +Picker.Date method: select +-------------------------- + +Selects a date manually. + +### Syntax: + + picker.select(date[, all]); + +### Arguments: + +1. date (*Date instance*) the date instance of the new date +2. all (*boolean*, optional) Whether it should update all inputs (defaults to the *updateAll* option) + +### Returns: + +- Picker.Date instance. + + +Class: Picker.Date.Range +------------------------ + +The range picker can be used to select date ranges, with a start date and a end date. + +### Syntax: + + #JS + var dp = new Picker.Date.Range([element, options]); + +### Arguments: + +#### Options: + +All `Picker.Date` options plus: + +- getStartEndDate: (*function*) Parses the two dates in the input field to `Date` instances. Signature: `function(input)` +- setStartEndDate: (*function*) Formats the dates and sets the input field value. Signature: `function(input, dates)` +- columns: (*number*, defaults to `3`) Number of columns +- footer: (*boolean*, defaults to `true`) Creates an extra footer element + + +Class: Picker.Attach +-------------------- + +Picker.Attach handles all the links from elements to the Picker. It handles the onfocus events of input elements etc. +The Class itself is not very useful on its own, but it is useful to extend this Class. +This class adds a outerclick as well to close the Picker if you click outside the picker. + +#### Syntax: + + #JS + new Picker.Attach(attachTo, options); + +#### Options: + +- toggle: (*element*, *string*, *array*) A collection of elements which will toggle the picker when such a link is clicked. +- togglesOnly: (*boolean, defaults to `true`) If the `toggle` option is set, this option determines if the focus/blur events on the input fields are still added as well. +- blockKeydown: (*boolean*, defaults to `true`) Whether it should block keydown events, so the user can type into the input field or not. + +### Picker.Attach Method: attach + +This will attach links and input elements to the picker + +#### Syntax + + #JS + myPicker.attach(attachTo); + +#### Arguments + +1. attachTo: (*element*, *string*, *array*) The elements or element to attach to the Picker. Can be a input element for onfocus events or other elements for click events. + + +### Picker.Attach Method: detach + +This will detach links and input elements from the picker + +#### Syntax + + #JS + myPicker.detach(detach); + +#### Arguments + +1. detach: (*element*, *string*, *array*) The elements or element to detach from the Picker. Can be a input element for onfocus events or other elements for click events. + + +Class: Picker +------------- + +This is a generic Picker Class, which is used for the basic things, like positioning, Fx, open, close, stuff like that. + +#### Syntax: + + #JS + new Picker(options); + +#### Options: + +- pickerClass: (*string*, defaults to `datepicker`) CSS class for the main datepicker container element. You can use multiple classes by separating them by a space, e.g. `class1 class2 class3` +- inject: (*element*, defaults to `document.body`) This is where the Picker element will be injected to. +- anitmationDuration: (*number*, defaults to `400`) Duration of the slide/fade animations in milliseconds. +- useFadeInOut: (*boolean*, defaults to `true`) Whether to fade-in/out the datepicker popup. You might want to set this to `false` in IE. +- positionOffset: (*object*, defaults to `{x: 0, y: 0}`) Allows you to tweak the position at which the datepicker appears, relative to the input element. Formatted as an object with x and y properties. Values can be negative. +- pickerPosition: (*string*, defaults to `bottom`) If the picker is positioned relative to an element, you can choose to position it top, bottom, left or right. +- draggable: (*boolean*, defaults to `true`) Will make the picker draggable, if Drag from MooTools More is included. +- columns: (*number*, defaults to `1`) Number of columns +- footer: (*boolean*, defaults to `false`) Creates an extra footer element + +#### Events: + +- open - triggered when the Picker will open (before the fx) +- close - triggered after the Picker is will get closed (before the fx) +- show - triggered when the Picker is shown +- hide - triggered when the Picker is hidden + + + +### Picker Method: show + +A method to show the Picker manually, with a Fx. + +#### Syntax + + #JS + dp.show() + +### Picker Method: close + +Closes the Picker with a Fx. + +#### Syntax + + #JS + dp.close(); + + +### Picker Method: toggle + +Toggles the Picker with a Fx. + +#### Syntax + + #JS + picker.toggle(); + +### Picker Method: show + +Opens the Picker directly. + +#### Syntax + + #JS + dp.show(); + + +### Picker Method: hide + +Hides the Picker directly. + +#### Syntax + + #JS + dp.hide(); + + +### Picker Method: destroy + +Destroys the Picker + + #JS + picker.destroy(); + + +### Picker Method: position + +Positions the Picker. + +#### Syntax: + + #JS + picker.position(x, y); + // or + picker.position(myElement, where); + +#### Arguments + +1. x: (*number*) Number of pixels from the left +2. y: (*number*) Number of pixels from the top + +Or + +1. myElement - (*element*) A element the Picker should be positioned relative to. +2. where - (*string*, optional) Position the Picker `left` or `right` to the element. + + +### Picker Method: setContent + +Set the content of the Picker, either elements or text. + +#### Syntax: + + #JS + picker.setContent(element, fx); + +#### Arguments: + +1. element: (*element*, *string*) Set the content of the Picker with this value +2. fx: (*string*, optional) Set the content of the picker, and apply it with this Fx. Options: 'fade', 'right', 'left' + + +### Picker Method: setTitle + +Sets the Picker title text. + + #JS + picker.setTitle(text); + +#### Arguments: + +1. text: (*string*) The text which will be set into the title. + + +License +------- + +- [MIT License](http://www.opensource.org/licenses/mit-license.php) diff --git a/Source/Locale.af-ZA.DatePicker.js b/Source/Locale.af-ZA.DatePicker.js new file mode 100644 index 0000000..7395543 --- /dev/null +++ b/Source/Locale.af-ZA.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.af-ZA.DatePicker +description: Afrikaans Language File for DatePicker +authors: Werner Mollentze +requires: [More/Locale] +provides: Locale.af-ZA.DatePicker +... +*/ + + +Locale.define('af-ZA', 'DatePicker', { + select_a_time: 'Kies \'n tyd', + use_mouse_wheel: 'Gebruik die muiswiel om vinnig \'n waarde te verander', + time_confirm_button: 'OK', + apply_range: 'OK', + cancel: 'Kanseleer', + week: 'Wk' +}); diff --git a/Source/Locale.cs-CZ.DatePicker.js b/Source/Locale.cs-CZ.DatePicker.js new file mode 100644 index 0000000..53387f7 --- /dev/null +++ b/Source/Locale.cs-CZ.DatePicker.js @@ -0,0 +1,16 @@ +/* +--- +name: Locale.cs-CZ.DatePicker +description: Czech Language File for DatePicker +authors: Jan Cerny +requires: [More/Locale] +provides: Locale.cs-CZ.DatePicker +... +*/ + + +Locale.define('cs-CZ', 'DatePicker', { + select_a_time: 'Vyberte čas', + use_mouse_wheel: 'Použijte kolečko myši k rychlé změně hodnoty', + time_confirm_button: 'Zvolte čas' +}); diff --git a/Source/Locale.de-DE.DatePicker.js b/Source/Locale.de-DE.DatePicker.js new file mode 100644 index 0000000..78c1ebd --- /dev/null +++ b/Source/Locale.de-DE.DatePicker.js @@ -0,0 +1,16 @@ +/* +--- +name: Locale.de-DE.DatePicker +description: German Language File for DatePicker +authors: Bastian Bringenberg +requires: [More/Locale] +provides: Locale.de-DE.DatePicker +... +*/ + + +Locale.define('de-DE', 'DatePicker', { + select_a_time: 'Wähle eine Zeit', + use_mouse_wheel: 'Mit dem Mausrad kannst du schneller die Werte ändern', + time_confirm_button: 'OK' +}); diff --git a/Source/Locale.en-US.DatePicker.js b/Source/Locale.en-US.DatePicker.js new file mode 100644 index 0000000..fc69c25 --- /dev/null +++ b/Source/Locale.en-US.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.en-US.DatePicker +description: English Language File for DatePicker +authors: Arian Stolwijk +requires: [More/Locale] +provides: Locale.en-US.DatePicker +... +*/ + + +Locale.define('en-US', 'DatePicker', { + select_a_time: 'Select a time', + use_mouse_wheel: 'Use the mouse wheel to quickly change value', + time_confirm_button: 'OK', + apply_range: 'Apply', + cancel: 'Cancel', + week: 'Wk' +}); diff --git a/Source/Locale.es-ES-DatePicker.js b/Source/Locale.es-ES-DatePicker.js new file mode 100644 index 0000000..e05dca4 --- /dev/null +++ b/Source/Locale.es-ES-DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.es-ES.DatePicker +description: Spanish Language File for DatePicker +authors: ["Juan Lago D.", "Carlos Cerrillo"] +requires: [More/Locale] +provides: Locale.es-ES.DatePicker +... +*/ + + +Locale.define('es-ES', 'DatePicker', { + select_a_time: 'Selecciona una fecha', + use_mouse_wheel: 'Utiliza la rueda del raton para cambiar rapidamente de valor', + time_confirm_button: 'OK', + apply_range: 'Aplicar', + cancel: 'Cancelar', + week: 'S' +}); diff --git a/Source/Locale.fr-FR.DatePicker.js b/Source/Locale.fr-FR.DatePicker.js new file mode 100644 index 0000000..f523685 --- /dev/null +++ b/Source/Locale.fr-FR.DatePicker.js @@ -0,0 +1,18 @@ +/* +--- +name: Locale.fr-FR.DatePicker +description: French Language File for DatePicker +authors: ["Arian Stolwijk", "charlouze", "Abric Armand"] +requires: [More/Locale] +provides: Locale.fr-FR.DatePicker +... +*/ + +Locale.define('fr-FR', 'DatePicker', { + select_a_time: 'Choisir l\'heure', + use_mouse_wheel: 'Utiliser la molette pour changer l\'heure rapidement', + time_confirm_button: 'OK', + apply_range: 'Appliquer', + cancel: 'Annuler', + week: 'Sem' +}); diff --git a/Source/Locale.he-IL.DatePicker.js b/Source/Locale.he-IL.DatePicker.js new file mode 100644 index 0000000..9d5297d --- /dev/null +++ b/Source/Locale.he-IL.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.he-IL.DatePicker +description: Hebrew Language File for DatePicker +authors: Amitay Horwitz +requires: [More/Locale] +provides: Locale.he-IL.DatePicker +... +*/ + + +Locale.define('he-IL', 'DatePicker', { + select_a_time: 'בחר זמן', + use_mouse_wheel: 'השתמש בגלגלת העכבר לשינוי מהיר', + time_confirm_button: 'אישור', + apply_range: 'החל', + cancel: 'ביטול', + week: 'שבוע' +}); diff --git a/Source/Locale.hu-HU.DatePicker.js b/Source/Locale.hu-HU.DatePicker.js new file mode 100644 index 0000000..853c11f --- /dev/null +++ b/Source/Locale.hu-HU.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.hu-HU.DatePicker +description: English Language File for DatePicker +authors: Gabor Garami +requires: [More/Locale] +provides: Locale.hu-HU.DatePicker +... +*/ + + +Locale.define('hu-HU', 'DatePicker', { + select_a_time: 'Időpont választása', + use_mouse_wheel: 'Az egérgörgő segítségével gyorsabb a választás', + time_confirm_button: 'OK', + apply_range: 'Alkalmaz', + cancel: 'Mégsem', + week: 'Hét' +}); diff --git a/Source/Locale.it-IT.DatePicker.js b/Source/Locale.it-IT.DatePicker.js new file mode 100644 index 0000000..7e2a146 --- /dev/null +++ b/Source/Locale.it-IT.DatePicker.js @@ -0,0 +1,15 @@ +/* +--- +name: Locale.it-IT.DatePicker +description: Italian Language File for DatePicker +authors: danielec (https://github.com/danielec) +requires: [More/Locale] +provides: Locale.it-IT.DatePicker +... +*/ + +Locale.define('it-IT', 'DatePicker', { + select_a_time: 'Scegli un orario', + use_mouse_wheel: 'Utilizza la rotellina del mouse per cambiare valore velocemente', + time_confirm_button: 'OK' +}); diff --git a/Source/Locale.ja-JP.DatePicker.js b/Source/Locale.ja-JP.DatePicker.js new file mode 100644 index 0000000..afb21ad --- /dev/null +++ b/Source/Locale.ja-JP.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.ja-JP.DatePicker +description: Japanese Language File for DatePicker +authors: Satoru Yoshida +requires: [More/Locale] +provides: Locale.ja-JP.DatePicker +... +*/ + + +Locale.define('ja-JP', 'DatePicker', { + select_a_time: '時刻を選択', + use_mouse_wheel: '値をすばやく変更するにはマウス・ホイールを使用します', + time_confirm_button: 'OK', + apply_range: '適用', + cancel: 'キャンセル', + week: '週' +}); diff --git a/Source/Locale.nl-NL.DatePicker.js b/Source/Locale.nl-NL.DatePicker.js new file mode 100644 index 0000000..471fd72 --- /dev/null +++ b/Source/Locale.nl-NL.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.nl-NL.DatePicker +description: Dutch Language File for DatePicker +authors: Arian Stolwijk +requires: [More/Locale] +provides: Locale.nl-NL.DatePicker +... +*/ + + +Locale.define('nl-NL', 'DatePicker', { + select_a_time: 'Selecteer een tijd', + use_mouse_wheel: 'Gebruik uw scrollwiel om door de tijd te scrollen', + time_confirm_button: 'OK', + apply_range: 'OK', + cancel: 'Annuleer', + week: 'W' +}); diff --git a/Source/Locale.pl-PL.DatePicker.js b/Source/Locale.pl-PL.DatePicker.js new file mode 100644 index 0000000..8146a83 --- /dev/null +++ b/Source/Locale.pl-PL.DatePicker.js @@ -0,0 +1,16 @@ +/* +--- +name: Locale.pl-PL.DatePicker +description: Polish Language File for DatePicker +authors: Tomek Wójcik +requires: [More/Locale] +provides: Locale.pl-PL.DatePicker +... +*/ + + +Locale.define('pl-PL', 'DatePicker', { + select_a_time: 'Wybierz czas', + use_mouse_wheel: 'Użyj rolki myszy aby szybko zmienić wartość', + time_confirm_button: 'OK' +}); diff --git a/Source/Locale.pt-BR.DatePicker.js b/Source/Locale.pt-BR.DatePicker.js new file mode 100644 index 0000000..97d5f8e --- /dev/null +++ b/Source/Locale.pt-BR.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.pt-BR.DatePicker +description: Portuguese Language File for DatePicker +authors: Jonnathan Soares +requires: [More/Locale] +provides: Locale.pt-BR.DatePicker +... +*/ + + +Locale.define('pt-BR', 'DatePicker', { + select_a_time: 'Selecione uma hora', + use_mouse_wheel: 'Use a roda do mouse para rapidamente trocar de valor', + time_confirm_button: 'OK', + apply_range: 'Aplicar', + cancel: 'Cancelar', + week: 'Sem.' +}); diff --git a/Source/Locale.ru-RU.DatePicker.js b/Source/Locale.ru-RU.DatePicker.js new file mode 100644 index 0000000..31cc3d8 --- /dev/null +++ b/Source/Locale.ru-RU.DatePicker.js @@ -0,0 +1,16 @@ +/* +--- +name: Locale.ru-RU.DatePicker +description: Russian Language File for DatePicker +authors: https://github.com/rwz +requires: [More/Locale] +provides: Locale.ru-RU.DatePicker +... +*/ + + +Locale.define('ru-RU', 'DatePicker', { + select_a_time: 'Выберите время', + use_mouse_wheel: 'Используйте колесо мышки для быстрой смены значения', + time_confirm_button: 'OK' +}); diff --git a/Source/Locale.sv-SE.DatePicker.js b/Source/Locale.sv-SE.DatePicker.js new file mode 100644 index 0000000..1c11034 --- /dev/null +++ b/Source/Locale.sv-SE.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.sv-SE.DatePicker +description: Swedish Language File for DatePicker +authors: Leon Radley +requires: [More/Locale] +provides: Locale.sv-SE.DatePicker +... +*/ + + +Locale.define('sv-SE', 'DatePicker', { + select_a_time: 'Välj en tid', + use_mouse_wheel: 'Scrolla för att snabbt ändra värde', + time_confirm_button: 'OK', + apply_range: 'Välj', + cancel: 'Avbryt', + week: 'v.' +}); diff --git a/Source/Locale.uk-UA.DatePicker.js b/Source/Locale.uk-UA.DatePicker.js new file mode 100644 index 0000000..f6a12ed --- /dev/null +++ b/Source/Locale.uk-UA.DatePicker.js @@ -0,0 +1,19 @@ +/* +--- +name: Locale.uk-UA.DatePicker +description: Ukrainian Language File for DatePicker +authors: Arian Stolwijk, Jon Baker, Artem Godlevskyy +requires: [More/Locale] +provides: Locale.uk-UA.DatePicker +... +*/ + + +Locale.define('uk-UA', 'DatePicker', { + select_a_time: 'Встановіть час', + use_mouse_wheel: 'Використовуйте прокрутку для швидкої зміни значення', + time_confirm_button: 'Гаразд', + apply_range: 'Застосувати', + cancel: 'Скасувати', + week: 'Т-нь' +}); diff --git a/Source/Picker.Attach.js b/Source/Picker.Attach.js new file mode 100644 index 0000000..83067a6 --- /dev/null +++ b/Source/Picker.Attach.js @@ -0,0 +1,161 @@ +/* +--- +name: Picker.Attach +description: Adds attach and detach methods to the Picker, to attach it to element events +authors: Arian Stolwijk +requires: [Picker, Core/Element.Event] +provides: Picker.Attach +... +*/ + + +Picker.Attach = new Class({ + + Extends: Picker, + + options: {/* + onAttached: function(event){}, + + toggleElements: null, // deprecated + toggle: null, // When set it deactivate toggling by clicking on the input */ + togglesOnly: true, // set to false to always make calendar popup on input element, if true, it depends on the toggles elements set. + showOnInit: false, // overrides the Picker option + blockKeydown: true + }, + + initialize: function(attachTo, options){ + this.parent(options); + + this.attachedEvents = []; + this.attachedElements = []; + this.toggles = []; + this.inputs = []; + + var documentEvent = function(event){ + if (this.attachedElements.contains(event.target)) return; + this.close(); + }.bind(this); + var document = this.picker.getDocument().addEvent('click', documentEvent); + + var preventPickerClick = function(event){ + event.stopPropagation(); + return false; + }; + this.picker.addEvent('click', preventPickerClick); + + // Support for deprecated toggleElements + if (this.options.toggleElements) this.options.toggle = document.getElements(this.options.toggleElements); + + this.attach(attachTo, this.options.toggle); + }, + + attach: function(attachTo, toggle){ + if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo); + if (typeOf(toggle) == 'string') toggle = document.id(toggle); + + var elements = Array.convert(attachTo), + toggles = Array.convert(toggle), + allElements = [].append(elements).combine(toggles), + self = this; + + var closeEvent = function(event){ + var stopInput = self.options.blockKeydown + && event.type == 'keydown' + && !(['tab', 'esc'].contains(event.key)), + isCloseKey = event.type == 'keydown' + && (['tab', 'esc'].contains(event.key)), + isA = event.target.get('tag') == 'a'; + + if (stopInput || isA) event.preventDefault(); + if (isCloseKey || isA) self.close(); + }; + + var getOpenEvent = function(element){ + return function(event){ + var tag = event.target.get('tag'); + if (tag == 'input' && event.type == 'click' && !element.match(':focus') || (self.opened && self.input == element)) return; + if (tag == 'a') event.stop(); + self.position(element); + self.open(); + self.fireEvent('attached', [event, element]); + }; + }; + + var getToggleEvent = function(open, close){ + return function(event){ + if (self.opened) close(event); + else open(event); + }; + }; + + allElements.each(function(element){ + + // The events are already attached! + if (self.attachedElements.contains(element)) return; + + var events = {}, + tag = element.get('tag'), + openEvent = getOpenEvent(element), + // closeEvent does not have a depency on element + toggleEvent = getToggleEvent(openEvent, closeEvent); + + if (tag == 'input'){ + // Fix in order to use togglers only + if (!self.options.togglesOnly || !toggles.length){ + events = { + focus: openEvent, + click: openEvent, + keydown: closeEvent + }; + } + self.inputs.push(element); + } else { + if (toggles.contains(element)){ + self.toggles.push(element); + events.click = toggleEvent + } else { + events.click = openEvent; + } + } + element.addEvents(events); + self.attachedElements.push(element); + self.attachedEvents.push(events); + }); + return this; + }, + + detach: function(attachTo, toggle){ + if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo); + if (typeOf(toggle) == 'string') toggle = document.id(toggle); + + var elements = Array.convert(attachTo), + toggles = Array.convert(toggle), + allElements = [].append(elements).combine(toggles), + self = this; + + if (!allElements.length) allElements = self.attachedElements; + + allElements.each(function(element){ + var i = self.attachedElements.indexOf(element); + if (i < 0) return; + + var events = self.attachedEvents[i]; + element.removeEvents(events); + delete self.attachedEvents[i]; + delete self.attachedElements[i]; + + var toggleIndex = self.toggles.indexOf(element); + if (toggleIndex != -1) delete self.toggles[toggleIndex]; + + var inputIndex = self.inputs.indexOf(element); + if (toggleIndex != -1) delete self.inputs[inputIndex]; + }); + return this; + }, + + destroy: function(){ + this.detach(); + return this.parent(); + } + +}); diff --git a/Source/Picker.Date.Range.js b/Source/Picker.Date.Range.js new file mode 100644 index 0000000..6f29389 --- /dev/null +++ b/Source/Picker.Date.Range.js @@ -0,0 +1,132 @@ +/* +--- +name: Picker.Date.Range +description: Select a Range of Dates +authors: Arian Stolwijk +requires: [Picker, Picker.Date] +provides: Picker.Date.Range +... +*/ + +Picker.Date.Range = new Class({ + + Extends: Picker.Date, + + options: { + getStartEndDate: function(input){ + return input.get('value').split('-').map(function(date){ + var parsed = Date.parse(date); + return Date.isValid(parsed) ? parsed : null; + }).clean(); + }, + setStartEndDate: function(input, dates){ + input.set('value', dates.map(function(date){ + return date.format(this.options.format); + }, this).join(' - ')); + }, + footer: true, + columns: 3 + }, + + getInputDate: function(input){ + if (!input) return; + + var dates = input.retrieve('datepicker:value'); + if (dates && dates.length) dates = dates.map(Date.parse); + if (!dates || !dates.length || dates.some(function(date){ + return !Date.isValid(date); + })){ + dates = this.options.getStartEndDate.call(this, input); + if (!dates.length || !dates.every(function(date){ + return Date.isValid(date); + })) dates = [this.date]; + } + if (dates.length == 1) this.date = this.startDate = this.endDate = dates[0]; + else if (dates.length == 2){ + this.date = this.startDate = dates[0]; + this.endDate = dates[1]; + } + }, + + constructPicker: function(){ + this.parent(); + var footer = this.footer, self = this; + if (!footer) return; + + var events = { + click: function(){ + this.focus(); + }, + blur: function(){ + var date = Date.parse(this.get('value')); + if (date.isValid) self[(this == startInput ? 'start' : 'end') + 'Date'] = date; + self.updateRangeSelection(); + }, + keydown: function(event){ + if (event.key == 'enter') self.selectRange(); + } + }; + + var startInput = this.startInput = new Element('input', {events: events}).inject(footer); + new Element('span', {text: ' - '}).inject(footer); + var endInput = this.endInput = new Element('input', {events: events}).inject(footer); + + this.applyButton = new Element('button.apply', { + text: Locale.get('DatePicker.apply_range'), + events: {click: self.selectRange.pass([], self)} + }).inject(footer); + + this.cancelButton = new Element('button.cancel', { + text: Locale.get('DatePicker.cancel'), + events: {click: self.close.pass(false, self)} + }).inject(footer); + }, + + renderDays: function(){ + this.parent.apply(this, arguments); + this.updateRangeSelection(); + }, + + select: function(date){ + if (this.startDate && (this.endDate == this.startDate || date > this.endDate) && date >= this.startDate) this.endDate = date; + else { + this.startDate = date; + this.endDate = date; + } + this.updateRangeSelection(); + }, + + selectRange: function(){ + this.date = this.startDate; + var dates = [this.startDate, this.endDate], input = this.input; + + this.options.setStartEndDate.call(this, input, dates); + input.store('datepicker:value', dates.map(function(date){ + return date.strftime(); + })).fireEvent('change'); + + this.fireEvent('select', dates, input); + this.close(); + return this; + }, + + updateRangeSelection: function(){ + var start = this.startDate, + end = this.endDate || start; + + if (this.dateElements) for (var i = this.dateElements.length; i--;){ + var el = this.dateElements[i]; + if (el.time >= start && el.time <= end) el.element.addClass('selected'); + else el.element.removeClass('selected'); + } + + var formattedFirst = start.format(this.options.format), + formattedEnd = end.format(this.options.format); + + this.startInput.set('value', formattedFirst); + this.endInput.set('value', formattedEnd); + + return this; + } + +}); diff --git a/Source/Picker.Date.js b/Source/Picker.Date.js new file mode 100644 index 0000000..83310d0 --- /dev/null +++ b/Source/Picker.Date.js @@ -0,0 +1,683 @@ +/* +--- +name: Picker.Date +description: Creates a DatePicker, can be used for picking years/months/days and time, or all of them +authors: Arian Stolwijk +requires: [Picker, Picker.Attach, Locale.en-US.DatePicker, More/Locale, More/Date] +provides: Picker.Date +... +*/ + + +(function(){ + +this.DatePicker = Picker.Date = new Class({ + + Extends: Picker.Attach, + + options: {/* + onSelect: function(date){}, + + minDate: new Date('3/4/2010'), // Date object or a string + maxDate: new Date('3/4/2011'), // same as minDate + availableDates: {}, // + invertAvailable: false, + + format: null,*/ + + timePicker: false, + timePickerOnly: false, // deprecated, use onlyView = 'time' + timeWheelStep: 1, // 10,15,20,30 + + yearPicker: true, + yearsPerPage: 20, + + startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin + rtl: false, + + startView: 'days', // allowed values: {time, days, months, years} + openLastView: false, + pickOnly: false, // 'years', 'months', 'days', 'time' + canAlwaysGoUp: ['months', 'days'], + updateAll : false, //whether or not to update all inputs when selecting a date + + weeknumbers: false, + + // if you like to use your own translations + months_abbr: null, + days_abbr: null, + years_title: function(date, options){ + var year = date.get('year'); + return year + '-' + (year + options.yearsPerPage - 1); + }, + months_title: function(date, options){ + return date.get('year'); + }, + days_title: function(date, options){ + return date.format('%b %Y'); + }, + time_title: function(date, options){ + return (options.pickOnly == 'time') ? Locale.get('DatePicker.select_a_time') : date.format('%d %B, %Y'); + } + }, + + initialize: function(attachTo, options){ + this.parent(attachTo, options); + + this.setOptions(options); + options = this.options; + + // If we only want to use one picker / backwards compatibility + ['year', 'month', 'day', 'time'].some(function(what){ + if (options[what + 'PickerOnly']){ + options.pickOnly = what; + return true; + } + return false; + }); + if (options.pickOnly){ + options[options.pickOnly + 'Picker'] = true; + options.startView = options.pickOnly; + } + + // backward compatibility for startView + var newViews = ['days', 'months', 'years']; + ['month', 'year', 'decades'].some(function(what, i){ + return (options.startView == what) && (options.startView = newViews[i]); + }); + + options.canAlwaysGoUp = options.canAlwaysGoUp ? Array.convert(options.canAlwaysGoUp) : []; + + // Set the min and max dates as Date objects + if (options.minDate){ + if (!(options.minDate instanceof Date)) options.minDate = Date.parse(options.minDate); + options.minDate.clearTime(); + } + if (options.maxDate){ + if (!(options.maxDate instanceof Date)) options.maxDate = Date.parse(options.maxDate); + options.maxDate.clearTime(); + } + + if (!options.format){ + options.format = (options.pickOnly != 'time') ? Locale.get('Date.shortDate') : ''; + if (options.timePicker) options.format = (options.format) + (options.format ? ' ' : '') + Locale.get('Date.shortTime'); + } + + // Some link or input has fired an event! + this.addEvent('attached', function(event, element){ + + // This is where we store the selected date + if (!this.currentView || !options.openLastView) this.currentView = options.startView; + + this.date = limitDate(new Date(), options.minDate, options.maxDate); + var tag = element.get('tag'), input; + if (tag == 'input') input = element; + else { + var index = this.toggles.indexOf(element); + if (this.inputs[index]) input = this.inputs[index]; + } + this.getInputDate(input); + this.input = input; + this.setColumns(this.originalColumns); + }.bind(this), true); + + }, + + getInputDate: function(input){ + this.date = new Date(); + if (!input) return; + var date = Date.parse(input.get('value')); + if (date == null || !date.isValid()){ + var storeDate = input.retrieve('datepicker:value'); + if (storeDate) date = Date.parse(storeDate); + } + if (date != null && date.isValid()) this.date = date; + }, + + // Control the previous and next elements + + constructPicker: function(){ + this.parent(); + + if (!this.options.rtl){ + this.previous = new Element('div.previous[html=«]').inject(this.header); + this.next = new Element('div.next[html=»]').inject(this.header); + } else { + this.next = new Element('div.previous[html=«]').inject(this.header); + this.previous = new Element('div.next[html=»]').inject(this.header); + } + }, + + hidePrevious: function(_next, _show){ + this[_next ? 'next' : 'previous'].setStyle('display', _show ? 'block' : 'none'); + return this; + }, + + showPrevious: function(_next){ + return this.hidePrevious(_next, true); + }, + + setPreviousEvent: function(fn, _next){ + this[_next ? 'next' : 'previous'].removeEvents('click'); + if (fn) this[_next ? 'next' : 'previous'].addEvent('click', fn); + return this; + }, + + hideNext: function(){ + return this.hidePrevious(true); + }, + + showNext: function(){ + return this.showPrevious(true); + }, + + setNextEvent: function(fn){ + return this.setPreviousEvent(fn, true); + }, + + setColumns: function(columns, view, date, viewFx){ + var ret = this.parent(columns), method; + + if ((view || this.currentView) + && (method = 'render' + (view || this.currentView).capitalize()) + && this[method] + ) this[method](date || this.date.clone(), viewFx); + + return ret; + }, + + // Render the Pickers + + renderYears: function(date, fx){ + var options = this.options, pages = options.columns, perPage = options.yearsPerPage, + _columns = [], _dates = []; + this.dateElements = []; + + // start neatly at interval (eg. 1980 instead of 1987) + date = date.clone().decrement('year', date.get('year') % perPage); + + var iterateDate = date.clone().decrement('year', Math.floor((pages - 1) / 2) * perPage); + + for (var i = pages; i--;){ + var _date = iterateDate.clone(); + _dates.push(_date); + _columns.push(renderers.years( + timesSelectors.years(options, _date.clone()), + options, + this.date.clone(), + this.dateElements, + function(date){ + if (options.pickOnly == 'years') this.select(date); + else this.renderMonths(date, 'fade'); + this.date = date; + }.bind(this) + )); + iterateDate.increment('year', perPage); + } + + this.setColumnsContent(_columns, fx); + this.setTitle(_dates, options.years_title); + + // Set limits + var limitLeft = (options.minDate && date.get('year') <= options.minDate.get('year')), + limitRight = (options.maxDate && (date.get('year') + options.yearsPerPage) >= options.maxDate.get('year')); + this[(limitLeft ? 'hide' : 'show') + 'Previous'](); + this[(limitRight ? 'hide' : 'show') + 'Next'](); + + this.setPreviousEvent(function(){ + this.renderYears(date.decrement('year', perPage), 'left'); + }.bind(this)); + + this.setNextEvent(function(){ + this.renderYears(date.increment('year', perPage), 'right'); + }.bind(this)); + + // We can't go up! + this.setTitleEvent(null); + + this.currentView = 'years'; + }, + + renderMonths: function(date, fx){ + var options = this.options, years = options.columns, _columns = [], _dates = [], + iterateDate = date.clone().decrement('year', Math.floor((years - 1) / 2)); + this.dateElements = []; + + for (var i = years; i--;){ + var _date = iterateDate.clone(); + _dates.push(_date); + _columns.push(renderers.months( + timesSelectors.months(options, _date.clone()), + options, + this.date.clone(), + this.dateElements, + function(date){ + if (options.pickOnly == 'months') this.select(date); + else this.renderDays(date, 'fade'); + this.date = date; + }.bind(this) + )); + iterateDate.increment('year', 1); + } + + this.setColumnsContent(_columns, fx); + this.setTitle(_dates, options.months_title); + + // Set limits + var year = date.get('year'), + limitLeft = (options.minDate && year <= options.minDate.get('year')), + limitRight = (options.maxDate && year >= options.maxDate.get('year')); + this[(limitLeft ? 'hide' : 'show') + 'Previous'](); + this[(limitRight ? 'hide' : 'show') + 'Next'](); + + this.setPreviousEvent(function(){ + this.renderMonths(date.decrement('year', years), 'left'); + }.bind(this)); + + this.setNextEvent(function(){ + this.renderMonths(date.increment('year', years), 'right'); + }.bind(this)); + + var canGoUp = options.yearPicker && (options.pickOnly != 'months' || options.canAlwaysGoUp.contains('months')); + var titleEvent = (canGoUp) ? function(){ + this.renderYears(date, 'fade'); + }.bind(this) : null; + this.setTitleEvent(titleEvent); + + this.currentView = 'months'; + }, + + renderDays: function(date, fx){ + var options = this.options, months = options.columns, _columns = [], _dates = [], + iterateDate = date.clone().decrement('month', Math.floor((months - 1) / 2)); + this.dateElements = []; + + for (var i = months; i--;){ + var _date = iterateDate.clone(); + _dates.push(_date); + _columns.push(renderers.days( + timesSelectors.days(options, _date.clone()), + options, + this.date.clone(), + this.dateElements, + function(date){ + if (options.pickOnly == 'days' || !options.timePicker) this.select(date) + else this.renderTime(date, 'fade'); + this.date = date; + }.bind(this) + )); + iterateDate.increment('month', 1); + } + + this.setColumnsContent(_columns, fx); + this.setTitle(_dates, options.days_title); + + var yearmonth = date.format('%Y%m').toInt(), + limitLeft = (options.minDate && yearmonth <= options.minDate.format('%Y%m')), + limitRight = (options.maxDate && yearmonth >= options.maxDate.format('%Y%m')); + this[(limitLeft ? 'hide' : 'show') + 'Previous'](); + this[(limitRight ? 'hide' : 'show') + 'Next'](); + + this.setPreviousEvent(function(){ + this.renderDays(date.decrement('month', months), 'left'); + }.bind(this)); + + this.setNextEvent(function(){ + this.renderDays(date.increment('month', months), 'right'); + }.bind(this)); + + var canGoUp = options.pickOnly != 'days' || options.canAlwaysGoUp.contains('days'); + var titleEvent = (canGoUp) ? function(){ + this.renderMonths(date, 'fade'); + }.bind(this) : null; + this.setTitleEvent(titleEvent); + + this.currentView = 'days'; + }, + + renderTime: function(date, fx){ + var options = this.options; + this.setTitle(date, options.time_title); + + var originalColumns = this.originalColumns = options.columns; + this.currentView = null; // otherwise you'd get crazy recursion + if (originalColumns != 1) this.setColumns(1); + + this.setContent(renderers.time( + options, + date.clone(), + function(date){ + this.select(date); + }.bind(this) + ), fx); + + // Hide « and » buttons + this.hidePrevious() + .hideNext() + .setPreviousEvent(null) + .setNextEvent(null); + + var canGoUp = options.pickOnly != 'time' || options.canAlwaysGoUp.contains('time'); + var titleEvent = (canGoUp) ? function(){ + this.setColumns(originalColumns, 'days', date, 'fade'); + }.bind(this) : null; + this.setTitleEvent(titleEvent); + + this.currentView = 'time'; + }, + + select: function(date, all){ + this.date = date; + var formatted = date.format(this.options.format), + time = date.strftime(), + inputs = (!this.options.updateAll && !all && this.input) ? [this.input] : this.inputs; + + inputs.each(function(input){ + input.set('value', formatted).store('datepicker:value', time).fireEvent('change'); + }, this); + + this.fireEvent('select', [date].concat(inputs)); + this.close(); + return this; + } + +}); + + +// Renderers only output elements and calculate the limits! + +var timesSelectors = { + + years: function(options, date){ + var times = []; + for (var i = 0; i < options.yearsPerPage; i++){ + times.push(+date); + date.increment('year', 1); + } + return times; + }, + + months: function(options, date){ + var times = []; + date.set('month', 0); + for (var i = 0; i <= 11; i++){ + times.push(+date); + date.increment('month', 1); + } + return times; + }, + + days: function(options, date){ + var times = []; + date.set('date', 1); + while (date.get('day') != options.startDay) date.set('date', date.get('date') - 1); + for (var i = 0; i < 42; i++){ + times.push(+date); + date.increment('day', 1); + } + return times; + } + +}; + +var renderers = { + + years: function(years, options, currentDate, dateElements, fn){ + var container = new Element('table.years'), + today = new Date(), + rows = [], + element, classes; + + years.each(function(_year, i){ + var date = new Date(_year), year = date.get('year'); + if (i % 4 === 0) { + rows.push(new Element('tr')); + rows[rows.length - 1].inject(container) + } + classes = '.year.year' + i; + if (year == today.get('year')) classes += '.today'; + if (year == currentDate.get('year')) classes += '.selected'; + element = new Element('td' + classes, {text: year}).inject(rows[rows.length - 1]); + + dateElements.push({element: element, time: _year}); + + if (isUnavailable('year', date, options)) element.addClass('unavailable'); + else element.addEvent('click', fn.pass(date)); + }); + + return container; + }, + + months: function(months, options, currentDate, dateElements, fn){ + var today = new Date(), + month = today.get('month'), + thisyear = today.get('year'), + selectedyear = currentDate.get('year'), + container = new Element('table.months'), + monthsAbbr = options.months_abbr || Locale.get('Date.months_abbr'), + rows = [], + element, classes; + + months.each(function(_month, i){ + var date = new Date(_month), year = date.get('year'); + if (i % 3 === 0) { + rows.push(new Element('tr')); + rows[rows.length - 1].inject(container) + } + + classes = '.month.month' + (i + 1); + if (i == month && year == thisyear) classes += '.today'; + if (i == currentDate.get('month') && year == selectedyear) classes += '.selected'; + element = new Element('td' + classes, {text: monthsAbbr[i]}).inject(rows[rows.length - 1]); + dateElements.push({element: element, time: _month}); + + if (isUnavailable('month', date, options)) element.addClass('unavailable'); + else element.addEvent('click', fn.pass(date)); + }); + + return container; + }, + + days: function(days, options, currentDate, dateElements, fn){ + var month = new Date(days[14]).get('month'), + todayString = new Date().toDateString(), + currentString = currentDate.toDateString(), + weeknumbers = options.weeknumbers, + container = new Element('table.days' + (weeknumbers ? '.weeknumbers' : ''), { + role: 'grid', 'aria-labelledby': this.titleID + }), + header = new Element('thead').inject(container), + body = new Element('tbody').inject(container), + titles = new Element('tr.titles').inject(header), + localeDaysShort = options.days_abbr || Locale.get('Date.days_abbr'), + day, classes, element, weekcontainer, dateString, + where = options.rtl ? 'top' : 'bottom'; + + if (weeknumbers) new Element('th.title.day.weeknumber', { + text: Locale.get('DatePicker.week') + }).inject(titles); + + for (day = options.startDay; day < (options.startDay + 7); day++){ + new Element('th.title.day.day' + (day % 7), { + text: localeDaysShort[(day % 7)], + role: 'columnheader' + }).inject(titles, where); + } + + days.each(function(_date, i){ + var date = new Date(_date); + + if (i % 7 == 0){ + weekcontainer = new Element('tr.week.week' + (Math.floor(i / 7))).set('role', 'row').inject(body); + if (weeknumbers) new Element('th.day.weeknumber', {text: date.get('week'), scope: 'row', role: 'rowheader'}).inject(weekcontainer); + } + + dateString = date.toDateString(); + classes = '.day.day' + date.get('day'); + if (dateString == todayString) classes += '.today'; + if (date.get('month') != month) classes += '.otherMonth'; + element = new Element('td' + classes, {text: date.getDate(), role: 'gridcell'}).inject(weekcontainer, where); + + if (dateString == currentString) element.addClass('selected').set('aria-selected', 'true'); + else element.set('aria-selected', 'false'); + + dateElements.push({element: element, time: _date}); + + if (isUnavailable('date', date, options)) element.addClass('unavailable'); + else element.addEvent('click', fn.pass(date.clone())); + }); + + return container; + }, + + time: function(options, date, fn){ + var container = new Element('div.time'), + // make sure that the minutes are timeWheelStep * k + initMinutes = (date.get('minutes') / options.timeWheelStep).round() * options.timeWheelStep + + if (initMinutes >= 60) initMinutes = 0; + date.set('minutes', initMinutes); + + var hoursInput = new Element('input.hour[type=text]', { + title: Locale.get('DatePicker.use_mouse_wheel'), + value: date.format('%H'), + events: { + click: function(event){ + event.target.focus(); + event.stop(); + }, + mousewheel: function(event){ + event.stop(); + hoursInput.focus(); + var value = hoursInput.get('value').toInt(); + value = (event.wheel > 0) ? ((value < 23) ? value + 1 : 0) + : ((value > 0) ? value - 1 : 23) + date.set('hours', value); + hoursInput.set('value', date.format('%H')); + }.bind(this) + }, + maxlength: 2 + }).inject(container); + + new Element('div.separator[text=:]').inject(container); + + var minutesInput = new Element('input.minutes[type=text]', { + title: Locale.get('DatePicker.use_mouse_wheel'), + value: date.format('%M'), + events: { + click: function(event){ + event.target.focus(); + event.stop(); + }, + mousewheel: function(event){ + event.stop(); + minutesInput.focus(); + var value = minutesInput.get('value').toInt(); + value = (event.wheel > 0) ? ((value < 59) ? (value + options.timeWheelStep) : 0) + : ((value > 0) ? (value - options.timeWheelStep) : (60 - options.timeWheelStep)); + if (value >= 60) value = 0; + date.set('minutes', value); + minutesInput.set('value', date.format('%M')); + }.bind(this) + }, + maxlength: 2 + }).inject(container); + + + new Element('input.ok', { + 'type': 'submit', + value: Locale.get('DatePicker.time_confirm_button'), + events: {click: function(event){ + event.stop(); + date.set({ + hours: hoursInput.get('value').toInt(), + minutes: minutesInput.get('value').toInt() + }); + fn(date.clone()); + }} + }).inject(container); + + return container; + } + +}; + + +Picker.Date.defineRenderer = function(name, fn){ + renderers[name] = fn; + return this; +}; + +Picker.Date.getRenderer = function(name) { + return renderers[name]; +} + +var limitDate = function(date, min, max){ + if (min && date < min) return min; + if (max && date > max) return max; + return date; +}; + +var isUnavailable = function(type, date, options){ + var minDate = options.minDate, + maxDate = options.maxDate, + availableDates = options.availableDates, + year, month, day, ms; + + if (!minDate && !maxDate && !availableDates) return false; + date.clearTime(); + + if (type == 'year'){ + year = date.get('year'); + return ( + (minDate && year < minDate.get('year')) || + (maxDate && year > maxDate.get('year')) || + ( + (availableDates != null && !options.invertAvailable) && ( + availableDates[year] == null || + Object.getLength(availableDates[year]) == 0 || + Object.getLength( + Object.filter(availableDates[year], function(days){ + return (days.length > 0); + }) + ) == 0 + ) + ) + ); + } + + if (type == 'month'){ + year = date.get('year'); + month = date.get('month') + 1; + ms = date.format('%Y%m').toInt(); + return ( + (minDate && ms < minDate.format('%Y%m').toInt()) || + (maxDate && ms > maxDate.format('%Y%m').toInt()) || + ( + (availableDates != null && !options.invertAvailable) && ( + availableDates[year] == null || + availableDates[year][month] == null || + availableDates[year][month].length == 0 + ) + ) + ); + } + + // type == 'date' + year = date.get('year'); + month = date.get('month') + 1; + day = date.get('date'); + + var dateAllow = (minDate && date < minDate) || (maxDate && date > maxDate); + if (availableDates != null){ + dateAllow = dateAllow + || availableDates[year] == null + || availableDates[year][month] == null + || !availableDates[year][month].contains(day); + if (options.invertAvailable) dateAllow = !dateAllow; + } + + return dateAllow; +}; + +})(); diff --git a/Source/Picker.js b/Source/Picker.js new file mode 100644 index 0000000..ebbe085 --- /dev/null +++ b/Source/Picker.js @@ -0,0 +1,344 @@ +/* +--- +name: Picker +description: Creates a Picker, which can be used for anything +authors: Arian Stolwijk +requires: [Core/Element.Dimensions, Core/Fx.Tween, Core/Fx.Transitions] +provides: Picker +... +*/ + + +var Picker = new Class({ + + Implements: [Options, Events], + + options: {/* + onShow: function(){}, + onOpen: function(){}, + onHide: function(){}, + onClose: function(){},*/ + + pickerClass: 'datepicker', + inject: null, + animationDuration: 400, + useFadeInOut: true, + positionOffset: {x: 0, y: 0}, + pickerPosition: 'bottom', + draggable: true, + showOnInit: true, + columns: 1, + footer: false + }, + + initialize: function(options){ + this.setOptions(options); + this.constructPicker(); + if (this.options.showOnInit) this.show(); + }, + + constructPicker: function(){ + var options = this.options; + + var picker = this.picker = new Element('div', { + 'class': options.pickerClass, + styles: { + left: 0, + top: 0, + display: 'none', + opacity: 0 + } + }).inject(options.inject || document.body); + picker.addClass('column_' + options.columns); + + if (options.useFadeInOut){ + picker.set('tween', { + duration: options.animationDuration, + link: 'cancel' + }); + } + + // Build the header + var header = this.header = new Element('div.header').inject(picker); + + var title = this.title = new Element('div.title').inject(header); + var titleID = this.titleID = 'pickertitle-' + String.uniqueID(); + this.titleText = new Element('div', { + 'role': 'heading', + 'class': 'titleText', + 'id': titleID, + 'aria-live': 'assertive', + 'aria-atomic': 'true' + }).inject(title); + + this.closeButton = new Element('div.closeButton[text=x][role=button]') + .addEvent('click', this.close.pass(false, this)) + .inject(header); + + // Build the body of the picker + var body = this.body = new Element('div.body').inject(picker); + + if (options.footer){ + this.footer = new Element('div.footer').inject(picker); + picker.addClass('footer'); + } + + // oldContents and newContents are used to slide from the old content to a new one. + var slider = this.slider = new Element('div.slider', { + styles: { + position: 'absolute', + top: 0, + left: 0 + } + }).set('tween', { + duration: options.animationDuration, + transition: Fx.Transitions.Quad.easeInOut + }).inject(body); + + this.newContents = new Element('div', { + styles: { + position: 'absolute', + top: 0, + left: 0 + } + }).inject(slider); + + this.oldContents = new Element('div', { + styles: { + position: 'absolute', + top: 0 + } + }).inject(slider); + + this.originalColumns = options.columns; + this.setColumns(options.columns); + + // IFrameShim for select fields in IE + var shim = this.shim = window['IframeShim'] ? new IframeShim(picker) : null; + + // Dragging + if (options.draggable && typeOf(picker.makeDraggable) == 'function'){ + this.dragger = picker.makeDraggable(shim ? { + onDrag: shim.position.bind(shim) + } : null); + picker.setStyle('cursor', 'move'); + } + }, + + open: function(noFx){ + if (this.opened == true) return this; + this.opened = true; + var self = this, + picker = this.picker.setStyle('display', 'block').set('aria-hidden', 'false') + if (this.shim) this.shim.show(); + this.fireEvent('open'); + if (this.options.useFadeInOut && !noFx){ + picker.get('tween').start('opacity', 1).chain(function(){ + self.fireEvent('show'); + this.callChain(); + }); + } else { + picker.setStyle('opacity', 1); + this.fireEvent('show'); + } + return this; + }, + + show: function(){ + return this.open(true); + }, + + close: function(noFx){ + if (this.opened == false) return this; + this.opened = false; + this.fireEvent('close'); + var self = this, picker = this.picker, hide = function(){ + picker.setStyle('display', 'none').set('aria-hidden', 'true'); + if (self.shim) self.shim.hide(); + self.fireEvent('hide'); + }; + if (this.options.useFadeInOut && !noFx){ + picker.get('tween').start('opacity', 0).chain(hide); + } else { + picker.setStyle('opacity', 0); + hide(); + } + return this; + }, + + hide: function(){ + return this.close(true); + }, + + toggle: function(){ + return this[this.opened == true ? 'close' : 'open'](); + }, + + destroy: function(){ + this.picker.destroy(); + if (this.shim) this.shim.destroy(); + }, + + position: function(x, y){ + var offset = this.options.positionOffset, + scroll = document.getScroll(), + size = document.getSize(), + pickersize = this.picker.getSize(); + + if (typeOf(x) == 'element'){ + var element = x, + where = y || this.options.pickerPosition; + + var elementCoords = element.getCoordinates(); + + x = (where == 'left') ? elementCoords.left - pickersize.x + : (where == 'bottom' || where == 'top') ? elementCoords.left + : elementCoords.right + y = (where == 'bottom') ? elementCoords.bottom + : (where == 'top') ? elementCoords.top - pickersize.y + : elementCoords.top; + } + + x += offset.x * ((where && where == 'left') ? -1 : 1); + y += offset.y * ((where && where == 'top') ? -1: 1); + + if ((x + pickersize.x) > (size.x + scroll.x)) x = (size.x + scroll.x) - pickersize.x; + if ((y + pickersize.y) > (size.y + scroll.y)) y = (size.y + scroll.y) - pickersize.y; + if (x < 0) x = 0; + if (y < 0) y = 0; + + this.picker.setStyles({ + left: x, + top: y + }); + if (this.shim) this.shim.position(); + return this; + }, + + setBodySize: function(){ + var bodysize = this.bodysize = this.body.getSize(); + + this.slider.setStyles({ + width: 2 * bodysize.x, + height: bodysize.y + }); + this.oldContents.setStyles({ + left: bodysize.x, + width: bodysize.x, + height: bodysize.y + }); + this.newContents.setStyles({ + width: bodysize.x, + height: bodysize.y + }); + }, + + setColumnContent: function(column, content){ + var columnElement = this.columns[column]; + if (!columnElement) return this; + + var type = typeOf(content); + if (['string', 'number'].contains(type)) columnElement.set('text', content); + else columnElement.empty().adopt(content); + + return this; + }, + + setColumnsContent: function(content, fx){ + var old = this.columns; + this.columns = this.newColumns; + this.newColumns = old; + + content.forEach(function(_content, i){ + this.setColumnContent(i, _content); + }, this); + return this.setContent(null, fx); + }, + + setColumns: function(columns){ + var _columns = this.columns = new Elements, _newColumns = this.newColumns = new Elements; + for (var i = columns; i--;){ + _columns.push(new Element('div.column').addClass('column_' + (columns - i))); + _newColumns.push(new Element('div.column').addClass('column_' + (columns - i))); + } + + var oldClass = 'column_' + this.options.columns, newClass = 'column_' + columns; + this.picker.removeClass(oldClass).addClass(newClass); + + this.options.columns = columns; + return this; + }, + + setContent: function(content, fx){ + if (content) return this.setColumnsContent([content], fx); + + // swap contents so we can fill the newContents again and animate + var old = this.oldContents; + this.oldContents = this.newContents; + this.newContents = old; + this.newContents.empty(); + + this.newContents.adopt(this.columns); + + this.setBodySize(); + + if (fx){ + this.fx(fx); + } else { + this.slider.setStyle('left', 0); + this.oldContents.setStyles({left: this.bodysize.x, opacity: 0}); + this.newContents.setStyles({left: 0, opacity: 1}); + } + return this; + }, + + fx: function(fx){ + var oldContents = this.oldContents, + newContents = this.newContents, + slider = this.slider, + bodysize = this.bodysize; + if (fx == 'right'){ + oldContents.setStyles({left: 0, opacity: 1}); + newContents.setStyles({left: bodysize.x, opacity: 1}); + slider.setStyle('left', 0).tween('left', 0, -bodysize.x); + } else if (fx == 'left'){ + oldContents.setStyles({left: bodysize.x, opacity: 1}); + newContents.setStyles({left: 0, opacity: 1}); + slider.setStyle('left', -bodysize.x).tween('left', -bodysize.x, 0); + } else if (fx == 'fade'){ + slider.setStyle('left', 0); + oldContents.setStyle('left', 0).set('tween', { + duration: this.options.animationDuration / 2 + }).tween('opacity', 1, 0).get('tween').chain(function(){ + oldContents.setStyle('left', bodysize.x); + }); + newContents.setStyles({opacity: 0, left: 0}).set('tween', { + duration: this.options.animationDuration + }).tween('opacity', 0, 1); + } + }, + + toElement: function(){ + return this.picker; + }, + + setTitle: function(content, fn){ + if (!fn) fn = Function.from; + this.titleText.empty().adopt( + Array.convert(content).map(function(item, i){ + return typeOf(item) == 'element' + ? item + : new Element('div.column', {text: fn(item, this.options)}).addClass('column_' + (i + 1)); + }, this) + ); + return this; + }, + + setTitleEvent: function(fn){ + this.titleText.removeEvents('click'); + if (fn) this.titleText.addEvent('click', fn); + this.titleText.setStyle('cursor', fn ? 'pointer' : ''); + return this; + } + +}); diff --git a/datepicker.css b/Source/datepicker.css similarity index 76% rename from datepicker.css rename to Source/datepicker.css index 1c54ebb..e182958 100644 --- a/datepicker.css +++ b/Source/datepicker.css @@ -7,6 +7,23 @@ height: 221px; background: #fff; line-height: normal; + z-index: 3003; +} + +.datepicker.column_2 { + width: 393px; +} + +.datepicker.column_3 { + width: 592px; +} + +.datepicker.column_4 { + width: 791px; +} + +.datepicker.column_5 { + width: 990px; } /* header @@ -17,17 +34,19 @@ height: 21px; padding-top: 4px; margin-bottom: 3px; + overflow: hidden; } .datepicker .header .title { text-align: center; padding-top: 1px; - margin: 0px 42px 0 20px; + position: absolute; + color: #fff; + font-weight: bold; + width: 99999px; } .datepicker .header .titleText { - color: #fff; - font-weight: bold; } .datepicker .header .next, .datepicker .header .previous, @@ -57,12 +76,45 @@ position: relative; top: 0px; left: 0px; - width: 194px; - border-right: 2px solid #fff; height: 193px; overflow: hidden; } +/* Columns */ + +.datepicker .body .column { + float: left; + width: 194px; + min-height: 193px; + margin-left: 5px; +} + +.datepicker .body .column.column_1 { + margin-left: 0; +} + +.datepicker .titleText .column { + float: left; + width: 194px; + margin-left: 5px; +} + +.datepicker .titleText .column.column_1 { + margin-left: 0; +} + +/* Footer */ + +.datepicker.footer { + height: 280px; +} + +.datepicker .footer { + margin-top: 3px; + padding: 15px 5px; + height: 26px; +} + /* time ********************************************************/ .datepicker .time { @@ -207,7 +259,12 @@ color: #fff !important; } -.datepicker .unavailable { +.datepicker .days .otherMonth.selected { + background: #bbbfc8 !important; +} + +.datepicker .unavailable, +.datepicker .body .days .week .day.unavailable:hover { background: #edd !important; color: #b88 !important; cursor: default !important; @@ -218,4 +275,24 @@ .datepicker .years .year:hover { background: #5D6E95 !important; color: #fff !important; -} \ No newline at end of file +} + +.datepicker .days.weeknumbers .day { + width: 22px; +} + +.datepicker .days.weeknumbers .day.weeknumber, +.datepicker .days.weeknumbers .day.weeknumber:hover { + color: #AAA !important; + width: 16px !important; + background: #EEE !important; +} + +.datepicker table { + border-spacing: 0; +} + +.datepicker th, +.datepicker td { + padding: 0; +} diff --git a/Source/datepicker_bootstrap/_mixins_and_vars.scss b/Source/datepicker_bootstrap/_mixins_and_vars.scss new file mode 100644 index 0000000..9e036f4 --- /dev/null +++ b/Source/datepicker_bootstrap/_mixins_and_vars.scss @@ -0,0 +1,34 @@ +// Colors +$gray : #777; +$grayLight : #999; +$grayLightBetween : #CCC; +$grayLighter : #EEE; +$blue : #049cdb; +$hoveredColor : $grayLighter; +$selectedColor : $blue; + +// Dimensions +$datepicker-width : 18em; +$datepicker-height : 18em; +$datepicker-header-height : 2.5em; +$datepicker-padding : 0.3em; + + +// Mixins +@mixin border-radius($radius) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + border-radius: $radius; +} + +@mixin box-shadow($shadow) { + -webkit-box-shadow: $shadow; + -moz-box-shadow: $shadow; + box-shadow: $shadow; +} + +@mixin box-sizing($boxmodel) { + -webkit-box-sizing: $boxmodel; + -moz-box-sizing: $boxmodel; + box-sizing: $boxmodel; +} diff --git a/Source/datepicker_bootstrap/datepicker_bootstrap.css b/Source/datepicker_bootstrap/datepicker_bootstrap.css new file mode 100644 index 0000000..2bb60a3 --- /dev/null +++ b/Source/datepicker_bootstrap/datepicker_bootstrap.css @@ -0,0 +1,173 @@ +/* + * Made by Nima Izadi (@Nima_Izadi / github.com/nim1989) + */ +.selected-button, .datepicker_bootstrap .days .day.selected, .datepicker_bootstrap .days .day.selected:hover, .datepicker_bootstrap .months .month.selected, .datepicker_bootstrap .months .month.selected:hover, .datepicker_bootstrap .years .year.selected, .datepicker_bootstrap .years .year.selected:hover { + background-color: #049cdb; + color: white; } + +.datepicker_bootstrap:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; } + +.datepicker_bootstrap:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #cccccc; + position: absolute; + top: -7px; + left: 6px; } + +.datepicker_bootstrap { + position: absolute; + font-size: 1em; + color: #000; + line-height: normal; + width: 18em; + padding: 0.3em; + background-color: white; + border: 1px solid #cccccc; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + /* header + ********************************************************/ + /* body + ********************************************************/ + /* days-grid + ********************************************************/ + /* months-grid + ********************************************************/ + /* years-grid + ********************************************************/ + /* years months and days style + ********************************************************/ + /* global + ********************************************************/ + /* time + ********************************************************/ } + .datepicker_bootstrap .header { + text-align: center; + border-bottom: inset 1px rgba(204, 204, 204, 0.55); + padding-bottom: 0.3em; + font-weight: bold; } + .datepicker_bootstrap .header .title { + text-align: center; + margin: auto; + display: inline-block; } + .datepicker_bootstrap .header .title .titleText { + margin: auto; + padding: 0.5em; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; } + .datepicker_bootstrap .header .title .titleText:hover { + background-color: #eeeeee; } + .datepicker_bootstrap .header .previous, .datepicker_bootstrap .header .next { + position: absolute; + cursor: pointer; + overflow: hidden; + top: 1em; + display: block; + border-top: 6px solid white; + border-bottom: 6px solid white; + height: 0px; } + .datepicker_bootstrap .header .previous { + left: 0.4em; + border-right: 7px solid black; } + .datepicker_bootstrap .header .previous:hover { + border-right-color: #eeeeee; } + .datepicker_bootstrap .header .next { + right: 0.4em; + border-left: 7px solid black; } + .datepicker_bootstrap .header .next:hover { + border-left-color: #999999; } + .datepicker_bootstrap .header .closeButton { + display: none; } + .datepicker_bootstrap .body { + position: relative; + top: 0; + left: 0; + height: 15.5em; + overflow: hidden; + margin-top: 0.2em; } + .datepicker_bootstrap .body .slider > div { + height: 15.5em; } + .datepicker_bootstrap .body .slider > div > div, .datepicker_bootstrap .body .slider > div table { + height: 100%; + width: 100%; + text-align: center; } + .datepicker_bootstrap .body .slider > div thead th { + height: 2em; } + .datepicker_bootstrap .body .slider > div thead th:hover { + background-color: transparent; } + .datepicker_bootstrap .days td { + width: 14.28571%; } + .datepicker_bootstrap .days .otherMonth { + color: #999999; } + .datepicker_bootstrap .months td { + width: 33.33333%; } + .datepicker_bootstrap .years td { + width: 25%; } + .datepicker_bootstrap .days .day, .datepicker_bootstrap .months .month, .datepicker_bootstrap .years .year { + cursor: pointer; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; } + .datepicker_bootstrap .days .day:hover, .datepicker_bootstrap .months .month:hover, .datepicker_bootstrap .years .year:hover { + background-color: #eeeeee; } + .datepicker_bootstrap .unavailable { + color: #eeeeee !important; + cursor: default !important; + text-decoration: line-through; } + .datepicker_bootstrap table { + border-spacing: 0; } + .datepicker_bootstrap th, + .datepicker_bootstrap td { + padding: 0; } + .datepicker_bootstrap .time { + position: relative; + width: 100%; + padding-top: 3em; + text-align: center; } + .datepicker_bootstrap .time > div { + display: inline-block; } + .datepicker_bootstrap .time .hour, + .datepicker_bootstrap .time .separator, + .datepicker_bootstrap .time .minutes { + width: 3em; + font-size: 1.5em; + text-align: center; + padding: 0.2em; } + .datepicker_bootstrap .time .separator { + width: 1em; } + .datepicker_bootstrap .time .ok:active { + -webkit-box-shadow: inset 0 1px 5px #999999; + -moz-box-shadow: inset 0 1px 5px #999999; + box-shadow: inset 0 1px 5px #999999; } + .datepicker_bootstrap .time .ok:hover { + background-color: #eeeeee; } + .datepicker_bootstrap .time .ok { + margin: 1em auto; + display: block; + font-size: 1.5em; + width: 5em; + text-align: center; + cursor: pointer; + box-shadow: none; + border: solid 1px #CCC; + outline: none; + background-color: white; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; } diff --git a/Source/datepicker_bootstrap/datepicker_bootstrap.scss b/Source/datepicker_bootstrap/datepicker_bootstrap.scss new file mode 100644 index 0000000..96ab5ba --- /dev/null +++ b/Source/datepicker_bootstrap/datepicker_bootstrap.scss @@ -0,0 +1,213 @@ +/* + * Made by Nima Izadi (@Nima_Izadi / github.com/nim1989) + */ + +@import "mixins_and_vars"; + +.selected-button { + // If you have a specific selected-button style, you can do @extend .my-btn--selected + background-color: $selectedColor; + color : white; +} + +.datepicker_bootstrap:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; +} +.datepicker_bootstrap:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid $grayLightBetween; + position: absolute; + top: -7px; + left: 6px; +} + +$datepicker-width: 18em; +$datepicker-height: 18em; +$datepicker-header-height: 2.5em; + +.datepicker_bootstrap { + position : absolute; + font-size : 1em; + color : #000; + line-height : normal; + width : $datepicker-width; + padding : 0.3em; + background-color: white; + border : 1px solid $grayLightBetween; + @include box-shadow(0 5px 10px rgba(0,0,0,0.2)); + @include border-radius(3px); + + /* header + ********************************************************/ + .header { + text-align : center; + border-bottom : inset 1px rgba(204, 204, 204, 0.55); + padding-bottom : 0.3em; + font-weight : bold; + .title { + text-align: center; + margin : auto; + display: inline-block; + .titleText{ + margin : auto; + padding : 0.5em; + @include border-radius(3px); + &:hover { + background-color: $hoveredColor; + } + } + } + .previous, .next { + position : absolute; + cursor : pointer; + overflow : hidden; + top : 1em; + display : block; + border-top : 6px solid white; + border-bottom : 6px solid white; + height : 0px; + } + .previous { + left : 0.4em; + border-right : 7px solid black; + &:hover { border-right-color: $hoveredColor; } + } + .next { + right: 0.4em; + border-left: 7px solid black; + &:hover { border-left-color: $grayLight; } + } + .closeButton { + display: none; + } + } + /* body + ********************************************************/ + .body { + position : relative; + top : 0; + left : 0; + height : $datepicker-height - $datepicker-header-height; + overflow : hidden; + margin-top : 0.2em; + .slider > div { + height : $datepicker-height - $datepicker-header-height; + > div, table { + height: 100%; + width : 100%; + text-align : center; + } + thead th { + height: 2em; + } + thead th:hover { + background-color: transparent; + } + } + } + + + /* days-grid + ********************************************************/ + .days { + td { + width: (100% / 7); + } + .otherMonth { + color: $grayLight; + } + } + /* months-grid + ********************************************************/ + .months td { + width: (100% / 3); + } + /* years-grid + ********************************************************/ + .years td { + width: (100% / 4); + } + + /* years months and days style + ********************************************************/ + .days .day, .months .month, .years .year { + cursor: pointer; + @include border-radius(3px); + &:hover { + background-color: $hoveredColor; + } + &.selected, &.selected:hover { + @extend .selected-button; + } + } + + /* global + ********************************************************/ + .unavailable { + color : $grayLighter !important; + cursor : default !important; + text-decoration : line-through; + } + + table { + border-spacing: 0; + } + th, + td { + padding: 0; + } + + /* time + ********************************************************/ + .time { + position : relative; + width : 100%; + padding-top: 3em; + text-align: center; + > div { + display: inline-block; + } + } + + .time .hour, + .time .separator, + .time .minutes { + width : 3em; + font-size : 1.5em; + text-align : center; + padding : 0.2em; + } + + .time .separator { + width: 1em; + } + .time .ok:active { + @include box-shadow(inset 0 1px 5px $grayLight); + } + .time .ok:hover { + background-color: $grayLighter; + } + .time .ok { + margin : 1em auto; + display : block; + font-size : 1.5em; + width : 5em; + text-align : center; + cursor : pointer; + box-shadow : none; + border : solid 1px #CCC; + outline : none; + background-color: white; + @include border-radius(2px); + } +} diff --git a/Source/datepicker_dashboard/buttons.png b/Source/datepicker_dashboard/buttons.png new file mode 100644 index 0000000..8de1438 Binary files /dev/null and b/Source/datepicker_dashboard/buttons.png differ diff --git a/datepicker_dashboard/datepicker_dashboard.css b/Source/datepicker_dashboard/datepicker_dashboard.css similarity index 97% rename from datepicker_dashboard/datepicker_dashboard.css rename to Source/datepicker_dashboard/datepicker_dashboard.css index 3b8f06f..760910d 100644 --- a/datepicker_dashboard/datepicker_dashboard.css +++ b/Source/datepicker_dashboard/datepicker_dashboard.css @@ -130,6 +130,7 @@ padding-top: 1px; height: 14px; margin: 0 1px 1px 0; + font-weight: normal; } .datepicker_dashboard .days .titles { height: 15px; @@ -243,3 +244,12 @@ cursor: default !important; text-decoration: line-through; } + +.datepicker_dashboard table { + border-spacing: 0; +} + +.datepicker_dashboard th, +.datepicker_dashboard td { + padding: 0; +} diff --git a/Source/datepicker_dashboard/frame.png b/Source/datepicker_dashboard/frame.png new file mode 100644 index 0000000..841e247 Binary files /dev/null and b/Source/datepicker_dashboard/frame.png differ diff --git a/Source/datepicker_jqui/arrows.png b/Source/datepicker_jqui/arrows.png new file mode 100644 index 0000000..d0d259b Binary files /dev/null and b/Source/datepicker_jqui/arrows.png differ diff --git a/datepicker_jqui/datepicker_jqui.css b/Source/datepicker_jqui/datepicker_jqui.css similarity index 98% rename from datepicker_jqui/datepicker_jqui.css rename to Source/datepicker_jqui/datepicker_jqui.css index 2d12d5b..902db5b 100644 --- a/datepicker_jqui/datepicker_jqui.css +++ b/Source/datepicker_jqui/datepicker_jqui.css @@ -278,3 +278,12 @@ color: #ccc !important; border: 1px solid #ccc !important; } + +.datepicker_jqui table { + border-spacing: 0; +} + +.datepicker_jqui th, +.datepicker_jqui td { + padding: 0; +} diff --git a/Source/datepicker_jqui/frame.png b/Source/datepicker_jqui/frame.png new file mode 100644 index 0000000..c8187af Binary files /dev/null and b/Source/datepicker_jqui/frame.png differ diff --git a/Source/datepicker_minimal/datepicker_minimal.css b/Source/datepicker_minimal/datepicker_minimal.css new file mode 100644 index 0000000..0330602 --- /dev/null +++ b/Source/datepicker_minimal/datepicker_minimal.css @@ -0,0 +1,281 @@ +/* + Minimal Theme + Author: Leon Radley (github.com/leon) + + Selected: #95adb8 + Hover: #c6d2d8 + Current: #f60 + Apply: #82a7b8 +*/ +.datepicker_minimal { + position: absolute; + width: 220px; + height: 200px; + background: #fff; + border: 1px solid #ddd; + border-radius: 2px; + font-size: 11px; + line-height: normal; + z-index: 3003; +} + .datepicker_minimal.column_2 { + width: 440px; + } + .datepicker_minimal.column_3 { + width: 660px; + } + .datepicker_minimal.column_4 { + width: 880px; + } + .datepicker_minimal.column_5 { + width: 1100px; + } + .datepicker_minimal th, + .datepicker_minimal td { + margin: 0; + padding: 0; + } + .datepicker_minimal .selected { + background: #95adb8 !important; + color: #fff !important; + } + + .datepicker_minimal .unavailable, + .datepicker_minimal .day.unavailable:hover { + background: #edd !important; + color: #b88 !important; + cursor: default !important; + } + + .datepicker_minimal td.day:hover, + .datepicker_minimal .month:hover, + .datepicker_minimal .year:hover { + background: #c6d2d8 !important; + color: #222 !important; + } + +/* + Header +*/ +.datepicker_minimal .header { + position: relative; + background: #333; + height: 25px; +} + .datepicker_minimal .header .title { + //margin: 0 50px; + text-align: center; + line-height: 25px; + } + .datepicker_minimal .header .titleText { + color: #fff; + font-weight: bold; + } + .datepicker_minimal .header .column { + float: left; + width: 220px; + } + .datepicker_minimal .header .next, + .datepicker_minimal .header .previous, + .datepicker_minimal .header .closeButton { + position: absolute; + top: 0; + width: 25px; + height: 25px; + line-height: 25px; + text-align: center; + color: #fff; + cursor: pointer; + } + .datepicker_minimal .header .previous { + left: 0; + } + .datepicker_minimal .header .next { + right: 25px; + } + .datepicker_minimal .header .closeButton { + right: 0; + } + +/* + Body +*/ +.datepicker_minimal .body { + position: relative; + top: 0; + left: 0; + width: 100%; + height: 175px; + overflow: hidden; +} + .datepicker_minimal .body .column { + float: left; + width: 219px; + height: 100%; + border-left: 1px solid #555; + } + .datepicker_minimal .body .column.column_1 { + border-left: 1px solid transparent; + } + +/* + Days +*/ +.datepicker_minimal .days { + width: 100%; + border: 0; + border-spacing: 0; +} + .datepicker_minimal .days .title { + font-weight: bold; + color: #444; + cursor: default; + width: 14.2857142857143%; /* 100/7 */ + } + .datepicker_minimal .days.weeknumbers .title { + width: 12.5%; + } + .datepicker_minimal .days .otherMonth { + background: #eee; + color: #aaa; + } + .datepicker_minimal .day { + cursor: pointer; + text-align: center; + overflow: hidden; + line-height: 25px; + } + .datepicker_minimal .today { + color: #f60; + font-weight: bold; + } + .datepicker_minimal .day0 { + margin-right: 0; + } + .datepicker_minimal .days .week5 .day { + margin-bottom: 0; + } + +/* + Months +*/ +.datepicker_minimal .months { + height: 100%; +} + .datepicker_minimal .month { + float: left; + display: inline; + width: 33.33333333%; + line-height: 44px; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: hidden; + } + +/* + Years +*/ +.datepicker_minimal .years { + height: 100%; +} + .datepicker_minimal .year { + float: left; + display: inline; + width: 25%; + line-height: 35px; + cursor: pointer; + text-align: center; + overflow: hidden; + } + +/* + Time +*/ +.datepicker_minimal .time { + width: 100%; + height: 100%; + background: #fff; +} + .datepicker_minimal .time .hour, + .datepicker_minimal .time .separator, + .datepicker_minimal .time .minutes { + position: absolute; + top: 50px; + width: 50px; + border: 1px dashed #ddd; + font-size: 32px; + text-align: center; + } + .datepicker_minimal .time .hour { + left: 40px; + } + .datepicker_minimal .time .separator { + background: transparent; + border: 0px; + width: 10px; + left: 100px; + } + + .datepicker_minimal .time .minutes { + left: 120px; + } + .datepicker_minimal .time .ok { + position: absolute; + top: 105px; + width: 136px; + left: 40px; + font-size: 20px; + } + +/* + Footer +*/ +.datepicker_minimal.footer { + height: 260px; +} + .datepicker_minimal .footer { + width: 100%; + height: 60px; + overflow: hidden; + } + .datepicker_minimal .footer input, + .datepicker_minimal .footer button { + float: left; + display: block; + width: 50%; + height: 30px; + line-height: 30px; + margin: 0; + padding: 8px 0; + border: 0; + background-color: #fff; + text-align: center; + } + .datepicker_minimal .footer input:focus { + outline: 0; + } + .datepicker_minimal .footer span { + display: none; + } + .datepicker_minimal .footer input { + height: 14px; + } + .datepicker_minimal .footer button { + height: 30px; + line-height: 30px; + padding: 0; + border-top: 1px solid #eee; + cursor: pointer; + } + .datepicker_minimal .footer button:hover { + background: #c6d2d8; + } + .datepicker_minimal .footer .apply { + background-color: #95adb8; + color: #fff; + font-weight: bold; + } + .datepicker_minimal .footer .apply:hover { + background-color: #82a7b8; + } diff --git a/Source/datepicker_vista/buttons.png b/Source/datepicker_vista/buttons.png new file mode 100644 index 0000000..a26e8fa Binary files /dev/null and b/Source/datepicker_vista/buttons.png differ diff --git a/datepicker_vista/datepicker_vista.css b/Source/datepicker_vista/datepicker_vista.css similarity index 97% rename from datepicker_vista/datepicker_vista.css rename to Source/datepicker_vista/datepicker_vista.css index ae02e6f..9276730 100644 --- a/datepicker_vista/datepicker_vista.css +++ b/Source/datepicker_vista/datepicker_vista.css @@ -245,3 +245,12 @@ color: #fbb !important; cursor: default !important; } + +.datepicker_vista table { + border-spacing: 0; +} + +.datepicker_vista th, +.datepicker_vista td { + padding: 0; +} diff --git a/Source/datepicker_vista/days.png b/Source/datepicker_vista/days.png new file mode 100644 index 0000000..ce768f6 Binary files /dev/null and b/Source/datepicker_vista/days.png differ diff --git a/Source/datepicker_vista/frame.png b/Source/datepicker_vista/frame.png new file mode 100644 index 0000000..eff17d8 Binary files /dev/null and b/Source/datepicker_vista/frame.png differ diff --git a/Source/datepicker_vista/months.png b/Source/datepicker_vista/months.png new file mode 100644 index 0000000..0a1585b Binary files /dev/null and b/Source/datepicker_vista/months.png differ diff --git a/Source/datepicker_vista/years.png b/Source/datepicker_vista/years.png new file mode 100644 index 0000000..cfb061d Binary files /dev/null and b/Source/datepicker_vista/years.png differ diff --git a/Test/Locale.nl-NL.Date.js b/Test/Locale.nl-NL.Date.js new file mode 100644 index 0000000..b8c4e06 --- /dev/null +++ b/Test/Locale.nl-NL.Date.js @@ -0,0 +1,67 @@ +/* +--- + +name: Locale.nl-NL.Date + +description: Date messages for Dutch. + +license: MIT-style license + +authors: + - Lennart Pilon + - Tim Wienk + +requires: + - /Locale + +provides: [Locale.nl-NL.Date] + +... +*/ + +Locale.define('nl-NL', 'Date', { + + months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + months_abbr: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + days_abbr: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + + // Culture's date order: DD-MM-YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d-%m-%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + + // Date.Extras + ordinal: 'e', + + lessThanMinuteAgo: 'minder dan een minuut geleden', + minuteAgo: 'ongeveer een minuut geleden', + minutesAgo: '{delta} minuten geleden', + hourAgo: 'ongeveer een uur geleden', + hoursAgo: 'ongeveer {delta} uur geleden', + dayAgo: 'een dag geleden', + daysAgo: '{delta} dagen geleden', + weekAgo: 'een week geleden', + weeksAgo: '{delta} weken geleden', + monthAgo: 'een maand geleden', + monthsAgo: '{delta} maanden geleden', + yearAgo: 'een jaar geleden', + yearsAgo: '{delta} jaar geleden', + + lessThanMinuteUntil: 'over minder dan een minuut', + minuteUntil: 'over ongeveer een minuut', + minutesUntil: 'over {delta} minuten', + hourUntil: 'over ongeveer een uur', + hoursUntil: 'over {delta} uur', + dayUntil: 'over ongeveer een dag', + daysUntil: 'over {delta} dagen', + weekUntil: 'over een week', + weeksUntil: 'over {delta} weken', + monthUntil: 'over een maand', + monthsUntil: 'over {delta} maanden', + yearUntil: 'over een jaar', + yearsUntil: 'over {delta} jaar' + +}); diff --git a/Test/columns.html b/Test/columns.html new file mode 100644 index 0000000..136f9e6 --- /dev/null +++ b/Test/columns.html @@ -0,0 +1,39 @@ + + +
+ +
+ Inputs:
+
+
+ This shows the different Events DatePicker fires. You can use the onSelect event if you want to
+ display the selected date elsewhere for example.
+
+ Inputs: + +
+ + + + \ No newline at end of file diff --git a/Test/full.html b/Test/full.html new file mode 100644 index 0000000..0f0c0ec --- /dev/null +++ b/Test/full.html @@ -0,0 +1,51 @@ + + + + +
+ This is a demo of a all the pickers, including years, months, days and time.
+ You should begin at the days view, but you can click the title to go up to months and years.
+ If you've selected a day, you should see the timepicker.
+
+ Inputs:
+
+
+ Empty default value:
+
+
+ Select one of the pickers you would like to see. The demo will load in the iframe. +
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Click to attach the datepicker to this field
+Click to detach the datepicker from this field
+Show datepicker (after you attached it)
+
+
+
+
+
+
+
+
+Toggle
+
+
+
+
+
+
+
+
+
+
+ This datepicker should parse the date as february 8th 1991 (dd-mm-yyyy). And should format it that way when it is ready. + The month and day names should be localized as well. +
+ ++ Inputs: + +
+ ++ This demo uses a custom set of day/month name abbreviations +
+ ++ Inputs: + +
+ ++ This demo uses custom titlebar texts +
+ ++ Inputs: +
+ + + \ No newline at end of file diff --git a/Test/minmaxdate.html b/Test/minmaxdate.html new file mode 100644 index 0000000..4f717d7 --- /dev/null +++ b/Test/minmaxdate.html @@ -0,0 +1,122 @@ + + + + +
+ This demo shows the minDate and maxDate options. You can set them to a certain date so the user
+ cannot select a date outside that limits. The limits are set to and .
+
+ Input:
+
+
+ Input (option is set as string):
+
+
+ Input (only a few dates are available):
+
+
+ Input (invert previously available dates):
+
+
+ Input with only maxDate:
+
+
+ This test shows a monthpicker only. It should parse the default month (December) and it should format it that way.
+ You can go up to the years picker, but you cannot pick a specific day.
+
+ Inputs: + +
+ + + + \ No newline at end of file diff --git a/Test/mootools-core.js b/Test/mootools-core.js new file mode 100644 index 0000000..b3c2dbb --- /dev/null +++ b/Test/mootools-core.js @@ -0,0 +1,6382 @@ +/* MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2016 [Valerio Proietti](http://mad4milk.net/).*/ +/*! +Web Build: http://mootools.net/core/builder/e426a9ae7167c5807b173d5deff673fc +*/ +/* +--- + +name: Core + +description: The heart of MooTools. + +license: MIT-style license. + +copyright: Copyright (c) 2006-2015 [Valerio Proietti](http://mad4milk.net/). + +authors: The MooTools production team (http://mootools.net/developers/) + +inspiration: + - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) + - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) + +provides: [Core, MooTools, Type, typeOf, instanceOf, Native] + +... +*/ +/*! MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2015 [Valerio Proietti](http://mad4milk.net/).*/ +(function(){ + +this.MooTools = { + version: '1.6.0', + build: '529422872adfff401b901b8b6c7ca5114ee95e2b' +}; + +// typeOf, instanceOf + +var typeOf = this.typeOf = function(item){ + if (item == null) return 'null'; + if (item.$family != null) return item.$family(); + + if (item.nodeName){ + if (item.nodeType == 1) return 'element'; + if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; + } else if (typeof item.length == 'number'){ + if ('callee' in item) return 'arguments'; + if ('item' in item) return 'collection'; + } + + return typeof item; +}; + +var instanceOf = this.instanceOf = function(item, object){ + if (item == null) return false; + var constructor = item.$constructor || item.constructor; + while (constructor){ + if (constructor === object) return true; + constructor = constructor.parent; + } + /*+ Manual: + Open + Close + Toggle +
+ ++ Links: + Open 1 + Open 2 + Open 3 +
+ ++ Inputs: + + + +
+ ++ The picker should float over the select item in IE6. + You can drag the Picker over the select box. +
+ +
+ You should be able to edit this text. The text of the above input fields
+ should not be editable.
+
+