mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +01:00
Add a datepicker control
Summary: I looooove JS! It makes me giddy with glee! Test Plan: Picked dates. See screenshots. Reviewers: btrahan Reviewed By: btrahan CC: aran Differential Revision: https://secure.phabricator.com/D2086
This commit is contained in:
parent
84398fc581
commit
01767c482d
11 changed files with 679 additions and 23 deletions
|
@ -72,7 +72,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-form-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/38bc1599/rsrc/css/aphront/form-view.css',
|
||||
'uri' => '/res/aec38c95/rsrc/css/aphront/form-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -699,6 +699,18 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-error-log.js',
|
||||
),
|
||||
'javelin-behavior-fancy-datepicker' =>
|
||||
array(
|
||||
'uri' => '/res/b2d89e4c/rsrc/js/application/core/behavior-fancy-datepicker.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-util',
|
||||
2 => 'javelin-dom',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-fancy-datepicker.js',
|
||||
),
|
||||
'javelin-behavior-files-drag-and-drop' =>
|
||||
array(
|
||||
'uri' => '/res/3a7a2a8a/rsrc/js/application/core/behavior-files-drag-and-drop.js',
|
||||
|
@ -2022,7 +2034,7 @@ celerity_register_resource_map(array(
|
|||
), array(
|
||||
'packages' =>
|
||||
array(
|
||||
'943d4357' =>
|
||||
'803864a4' =>
|
||||
array(
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -2047,7 +2059,7 @@ celerity_register_resource_map(array(
|
|||
17 => 'aphront-pager-view-css',
|
||||
18 => 'phabricator-transaction-view-css',
|
||||
),
|
||||
'uri' => '/res/pkg/943d4357/core.pkg.css',
|
||||
'uri' => '/res/pkg/803864a4/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'21d01ed8' =>
|
||||
|
@ -2194,17 +2206,17 @@ celerity_register_resource_map(array(
|
|||
'reverse' =>
|
||||
array(
|
||||
'aphront-attached-file-view-css' => 'f45e0b15',
|
||||
'aphront-crumbs-view-css' => '943d4357',
|
||||
'aphront-dialog-view-css' => '943d4357',
|
||||
'aphront-form-view-css' => '943d4357',
|
||||
'aphront-crumbs-view-css' => '803864a4',
|
||||
'aphront-dialog-view-css' => '803864a4',
|
||||
'aphront-form-view-css' => '803864a4',
|
||||
'aphront-headsup-action-list-view-css' => '18be02e0',
|
||||
'aphront-list-filter-view-css' => '943d4357',
|
||||
'aphront-pager-view-css' => '943d4357',
|
||||
'aphront-panel-view-css' => '943d4357',
|
||||
'aphront-side-nav-view-css' => '943d4357',
|
||||
'aphront-table-view-css' => '943d4357',
|
||||
'aphront-tokenizer-control-css' => '943d4357',
|
||||
'aphront-typeahead-control-css' => '943d4357',
|
||||
'aphront-list-filter-view-css' => '803864a4',
|
||||
'aphront-pager-view-css' => '803864a4',
|
||||
'aphront-panel-view-css' => '803864a4',
|
||||
'aphront-side-nav-view-css' => '803864a4',
|
||||
'aphront-table-view-css' => '803864a4',
|
||||
'aphront-tokenizer-control-css' => '803864a4',
|
||||
'aphront-typeahead-control-css' => '803864a4',
|
||||
'differential-changeset-view-css' => '18be02e0',
|
||||
'differential-core-view-css' => '18be02e0',
|
||||
'differential-inline-comment-editor' => 'b2139675',
|
||||
|
@ -2262,23 +2274,23 @@ celerity_register_resource_map(array(
|
|||
'maniphest-task-detail-css' => 'f45e0b15',
|
||||
'maniphest-task-summary-css' => 'f45e0b15',
|
||||
'maniphest-transaction-detail-css' => 'f45e0b15',
|
||||
'phabricator-app-buttons-css' => '943d4357',
|
||||
'phabricator-app-buttons-css' => '803864a4',
|
||||
'phabricator-content-source-view-css' => '18be02e0',
|
||||
'phabricator-core-buttons-css' => '943d4357',
|
||||
'phabricator-core-css' => '943d4357',
|
||||
'phabricator-directory-css' => '943d4357',
|
||||
'phabricator-core-buttons-css' => '803864a4',
|
||||
'phabricator-core-css' => '803864a4',
|
||||
'phabricator-directory-css' => '803864a4',
|
||||
'phabricator-drag-and-drop-file-upload' => 'b2139675',
|
||||
'phabricator-dropdown-menu' => '21d01ed8',
|
||||
'phabricator-jump-nav' => '943d4357',
|
||||
'phabricator-jump-nav' => '803864a4',
|
||||
'phabricator-keyboard-shortcut' => '21d01ed8',
|
||||
'phabricator-keyboard-shortcut-manager' => '21d01ed8',
|
||||
'phabricator-menu-item' => '21d01ed8',
|
||||
'phabricator-object-selector-css' => '18be02e0',
|
||||
'phabricator-paste-file-upload' => '21d01ed8',
|
||||
'phabricator-remarkup-css' => '943d4357',
|
||||
'phabricator-remarkup-css' => '803864a4',
|
||||
'phabricator-shaped-request' => 'b2139675',
|
||||
'phabricator-standard-page-view' => '943d4357',
|
||||
'phabricator-transaction-view-css' => '943d4357',
|
||||
'syntax-highlighting-css' => '943d4357',
|
||||
'phabricator-standard-page-view' => '803864a4',
|
||||
'phabricator-transaction-view-css' => '803864a4',
|
||||
'syntax-highlighting-css' => '803864a4',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -31,6 +31,7 @@ phutil_register_library_map(array(
|
|||
'AphrontFileResponse' => 'aphront/response/file',
|
||||
'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
|
||||
'AphrontFormControl' => 'view/form/control/base',
|
||||
'AphrontFormDateControl' => 'view/form/control/date',
|
||||
'AphrontFormDividerControl' => 'view/form/control/divider',
|
||||
'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload',
|
||||
'AphrontFormFileControl' => 'view/form/control/file',
|
||||
|
@ -610,6 +611,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagListController' => 'applications/flag/controller/list',
|
||||
'PhabricatorFlagListView' => 'applications/flag/view/list',
|
||||
'PhabricatorFlagQuery' => 'applications/flag/query/flag',
|
||||
'PhabricatorFormExample' => 'applications/uiexample/examples/form',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
|
||||
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
|
||||
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
|
||||
|
@ -976,6 +978,7 @@ phutil_register_library_map(array(
|
|||
'AphrontFileResponse' => 'AphrontResponse',
|
||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||
'AphrontFormControl' => 'AphrontView',
|
||||
'AphrontFormDateControl' => 'AphrontFormControl',
|
||||
'AphrontFormDividerControl' => 'AphrontFormControl',
|
||||
'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl',
|
||||
'AphrontFormFileControl' => 'AphrontFormControl',
|
||||
|
@ -1447,6 +1450,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagEditController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagListController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagListView' => 'AphrontView',
|
||||
'PhabricatorFormExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
||||
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
|
||||
'PhabricatorHelpController' => 'PhabricatorController',
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorFormExample extends PhabricatorUIExample {
|
||||
|
||||
public function getName() {
|
||||
return 'Form';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return 'Use <tt>AphrontFormView</tt> to render forms.';
|
||||
}
|
||||
|
||||
public function renderExample() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$date = id(new AphrontFormDateControl())
|
||||
->setUser($user)
|
||||
->setName('date')
|
||||
->setLabel('Date');
|
||||
|
||||
$date->readValueFromRequest($request);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->appendChild($date)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Submit'));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('Form');
|
||||
$panel->appendChild($form);
|
||||
|
||||
return $panel;
|
||||
}
|
||||
}
|
18
src/applications/uiexample/examples/form/__init__.php
Normal file
18
src/applications/uiexample/examples/form/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/uiexample/examples/base');
|
||||
phutil_require_module('phabricator', 'view/form/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/date');
|
||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorFormExample.php');
|
201
src/view/form/control/date/AphrontFormDateControl.php
Normal file
201
src/view/form/control/date/AphrontFormDateControl.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class AphrontFormDateControl extends AphrontFormControl {
|
||||
|
||||
private $user;
|
||||
|
||||
public function setUser($user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function readValueFromRequest(AphrontRequest $request) {
|
||||
|
||||
$day = $request->getInt($this->getDayInputName());
|
||||
$month = $request->getInt($this->getMonthInputName());
|
||||
$year = $request->getInt($this->getYearInputName());
|
||||
|
||||
$err = $this->getError();
|
||||
|
||||
if ($day || $month || $year) {
|
||||
|
||||
// Assume invalid.
|
||||
$err = 'Invalid';
|
||||
|
||||
$tz = new DateTimeZone('UTC');
|
||||
try {
|
||||
$date = new DateTime("{$year}-{$month}-{$day} 12:00:00 AM", $tz);
|
||||
$value = $date->format('Y-m-d');
|
||||
if ($value) {
|
||||
$this->setValue($value);
|
||||
$err = null;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
// Ignore, already handled.
|
||||
}
|
||||
}
|
||||
|
||||
$this->setError($err);
|
||||
|
||||
return $err;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
if (!parent::getValue()) {
|
||||
$this->setValue($this->formatTime(time(), 'Y-m-d'));
|
||||
}
|
||||
return parent::getValue();
|
||||
}
|
||||
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'aphront-form-control-date';
|
||||
}
|
||||
|
||||
private function getMinYear() {
|
||||
$cur_year = $this->formatTime(
|
||||
time(),
|
||||
'Y');
|
||||
$val_year = $this->getYearInputValue();
|
||||
|
||||
return min($cur_year, $val_year) - 3;
|
||||
}
|
||||
|
||||
private function getMaxYear() {
|
||||
$cur_year = $this->formatTime(
|
||||
time(),
|
||||
'Y');
|
||||
$val_year = $this->getYearInputValue();
|
||||
|
||||
return max($cur_year, $val_year) + 3;
|
||||
}
|
||||
|
||||
private function getDayInputValue() {
|
||||
return (int)idx(explode('-', $this->getValue()), 2);
|
||||
}
|
||||
|
||||
private function getMonthInputValue() {
|
||||
return (int)idx(explode('-', $this->getValue()), 1);
|
||||
}
|
||||
|
||||
private function getYearInputValue() {
|
||||
return (int)idx(explode('-', $this->getValue()), 0);
|
||||
}
|
||||
|
||||
private function formatTime($epoch, $fmt) {
|
||||
return phabricator_format_local_time(
|
||||
$epoch,
|
||||
$this->user,
|
||||
$fmt);
|
||||
}
|
||||
|
||||
private function getDayInputName() {
|
||||
return $this->getName().'_d';
|
||||
}
|
||||
|
||||
private function getMonthInputName() {
|
||||
return $this->getName().'_m';
|
||||
}
|
||||
|
||||
private function getYearInputName() {
|
||||
return $this->getName().'_y';
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
$min_year = $this->getMinYear();
|
||||
$max_year = $this->getMaxYear();
|
||||
|
||||
$days = range(1, 31);
|
||||
$days = array_combine($days, $days);
|
||||
|
||||
$months = array(
|
||||
1 => 'Jan',
|
||||
2 => 'Feb',
|
||||
3 => 'Mar',
|
||||
4 => 'Apr',
|
||||
5 => 'May',
|
||||
6 => 'Jun',
|
||||
7 => 'Jul',
|
||||
8 => 'Aug',
|
||||
9 => 'Sep',
|
||||
10 => 'Oct',
|
||||
11 => 'Nov',
|
||||
12 => 'Dec',
|
||||
);
|
||||
|
||||
$years = range($this->getMinYear(), $this->getMaxYear());
|
||||
$years = array_combine($years, $years);
|
||||
|
||||
$days_sel = AphrontFormSelectControl::renderSelectTag(
|
||||
$this->getDayInputValue(),
|
||||
$days,
|
||||
array(
|
||||
'name' => $this->getDayInputName(),
|
||||
'sigil' => 'day-input',
|
||||
));
|
||||
|
||||
$months_sel = AphrontFormSelectControl::renderSelectTag(
|
||||
$this->getMonthInputValue(),
|
||||
$months,
|
||||
array(
|
||||
'name' => $this->getMonthInputName(),
|
||||
'sigil' => 'month-input',
|
||||
));
|
||||
|
||||
$years_sel = AphrontFormSelectControl::renderSelectTag(
|
||||
$this->getYearInputValue(),
|
||||
$years,
|
||||
array(
|
||||
'name' => $this->getYearInputName(),
|
||||
'sigil' => 'year-input',
|
||||
));
|
||||
|
||||
$cal_icon = javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '#',
|
||||
'class' => 'calendar-button',
|
||||
'sigil' => 'calendar-button',
|
||||
),
|
||||
'');
|
||||
|
||||
$id = celerity_generate_unique_node_id();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'fancy-datepicker',
|
||||
array(
|
||||
'root' => $id,
|
||||
));
|
||||
|
||||
return javelin_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $id,
|
||||
'class' => 'aphront-form-date-container',
|
||||
),
|
||||
self::renderSingleView(
|
||||
array(
|
||||
$days_sel,
|
||||
$months_sel,
|
||||
$years_sel,
|
||||
$cal_icon,
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
19
src/view/form/control/date/__init__.php
Normal file
19
src/view/form/control/date/__init__.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/select');
|
||||
phutil_require_module('phabricator', 'view/utils');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('AphrontFormDateControl.php');
|
|
@ -60,7 +60,7 @@ final class AphrontFormSelectControl extends AphrontFormControl {
|
|||
phutil_escape_html($label));
|
||||
}
|
||||
|
||||
return phutil_render_tag(
|
||||
return javelin_render_tag(
|
||||
'select',
|
||||
$attrs,
|
||||
implode("\n", $option_tags));
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
|
|
@ -167,3 +167,123 @@ table.aphront-form-control-checkbox-layout th {
|
|||
background: #99ff99;
|
||||
border-color: #669966;
|
||||
}
|
||||
|
||||
.calendar-button {
|
||||
padding: 11px;
|
||||
right: -30px;
|
||||
top: -3px;
|
||||
|
||||
background: url(/rsrc/image/icon/fatcow/calendar_edit.png)
|
||||
no-repeat center center;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.aphront-form-date-container {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.aphront-form-date-container select{
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.fancy-datepicker {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -8px;
|
||||
width: 240px;
|
||||
padding-bottom: 6em;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core {
|
||||
padding: 1px;
|
||||
font-size: 11px;
|
||||
font-family: Verdana;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .month-table,
|
||||
.fancy-datepicker-core .day-table {
|
||||
margin: 0 auto;
|
||||
border-collapse: separate;
|
||||
border-spacing: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .month-table {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .month-table td.lrbutton {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .month-table td {
|
||||
padding: 4px;
|
||||
font-weight: bold;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .month-table td.lrbutton {
|
||||
background: #e6e6e6;
|
||||
border: 1px solid;
|
||||
border-color: #a6a6a6 #969696 #868686 #a6a6a6;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td {
|
||||
overflow: hidden;
|
||||
background: #f6f6f6;
|
||||
vertical-align: center;
|
||||
text-align: center;
|
||||
border: 1px solid #d6d6d6;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td.day-placeholder {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td.weekend {
|
||||
color: #666666;
|
||||
border-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td.day-name {
|
||||
background: transparent;
|
||||
border: 1px transparent;
|
||||
vertical-align: bottom;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td.today {
|
||||
background: #eeee99;
|
||||
border-color: #aaaa66;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core .day-table td.datepicker-selected {
|
||||
background: #0099ff;
|
||||
border-color: #0066cc;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fancy-datepicker-core td.novalue {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.picker-open .calendar-button,
|
||||
.fancy-datepicker-core {
|
||||
background-color: white;
|
||||
border: 1px solid #777777;
|
||||
|
||||
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.picker-open .calendar-button {
|
||||
border-left: 1px solid white;
|
||||
}
|
||||
|
|
BIN
webroot/rsrc/image/icon/fatcow/calendar_edit.png
Executable file
BIN
webroot/rsrc/image/icon/fatcow/calendar_edit.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 794 B |
228
webroot/rsrc/js/application/core/behavior-fancy-datepicker.js
Normal file
228
webroot/rsrc/js/application/core/behavior-fancy-datepicker.js
Normal file
|
@ -0,0 +1,228 @@
|
|||
/**
|
||||
* @provides javelin-behavior-fancy-datepicker
|
||||
* @requires javelin-behavior
|
||||
* javelin-util
|
||||
* javelin-dom
|
||||
*/
|
||||
|
||||
JX.behavior('fancy-datepicker', function(config) {
|
||||
|
||||
var picker;
|
||||
|
||||
var value_y;
|
||||
var value_m;
|
||||
var value_d;
|
||||
|
||||
var onopen = function(e) {
|
||||
e.kill();
|
||||
|
||||
// If you click the calendar icon while the date picker is open, close it
|
||||
// without writing the change.
|
||||
|
||||
if (picker) {
|
||||
onclose(e);
|
||||
return;
|
||||
}
|
||||
|
||||
picker = JX.$N(
|
||||
'div',
|
||||
{className: 'fancy-datepicker'},
|
||||
JX.$N('div', {className: 'fancy-datepicker-core'}));
|
||||
root.appendChild(picker);
|
||||
|
||||
JX.DOM.alterClass(root, 'picker-open', true);
|
||||
|
||||
read_date();
|
||||
render();
|
||||
};
|
||||
|
||||
var onclose = function(e) {
|
||||
if (!picker) {
|
||||
return;
|
||||
}
|
||||
|
||||
JX.DOM.remove(picker);
|
||||
picker = null;
|
||||
JX.DOM.alterClass(root, 'picker-open', false);
|
||||
e.kill();
|
||||
};
|
||||
|
||||
var get_inputs = function() {
|
||||
return {
|
||||
y: JX.DOM.find(root, 'select', 'year-input'),
|
||||
m: JX.DOM.find(root, 'select', 'month-input'),
|
||||
d: JX.DOM.find(root, 'select', 'day-input')
|
||||
};
|
||||
}
|
||||
|
||||
var read_date = function() {
|
||||
var i = get_inputs();
|
||||
value_y = i.y.value;
|
||||
value_m = i.m.value;
|
||||
value_d = i.d.value;
|
||||
};
|
||||
|
||||
var write_date = function() {
|
||||
var i = get_inputs();
|
||||
i.y.value = value_y;
|
||||
i.m.value = value_m;
|
||||
i.d.value = value_d;
|
||||
};
|
||||
|
||||
var render = function() {
|
||||
JX.DOM.setContent(
|
||||
picker.firstChild,
|
||||
[
|
||||
render_month(),
|
||||
render_day(),
|
||||
]);
|
||||
};
|
||||
|
||||
// Render a cell for the date picker.
|
||||
var cell = function(label, value, selected, class_name) {
|
||||
|
||||
class_name = class_name || '';
|
||||
|
||||
if (selected) {
|
||||
class_name += ' datepicker-selected';
|
||||
}
|
||||
if (!value) {
|
||||
class_name += ' novalue';
|
||||
}
|
||||
|
||||
return JX.$N('td', {meta: {value: value}, className: class_name}, label);
|
||||
}
|
||||
|
||||
|
||||
// Render the top bar which allows you to pick a month and year.
|
||||
var render_month = function() {
|
||||
var months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'];
|
||||
|
||||
var buttons = [
|
||||
cell("\u25C0", 'm:-1', false, 'lrbutton'),
|
||||
cell(months[value_m - 1] + ' ' + value_y, null),
|
||||
cell("\u25B6", 'm:1', false, 'lrbutton')];
|
||||
|
||||
return JX.$N(
|
||||
'table',
|
||||
{className: 'month-table'},
|
||||
JX.$N('tr', {}, buttons));
|
||||
};
|
||||
|
||||
|
||||
// Render the day-of-week and calendar views.
|
||||
var render_day = function() {
|
||||
var weeks = [];
|
||||
|
||||
// First, render the weekday names.
|
||||
var weekdays = 'SMTWTFS';
|
||||
var weekday_names = [];
|
||||
for (var ii = 0; ii < weekdays.length; ii++) {
|
||||
weekday_names.push(cell(weekdays.charAt(ii), null, false, 'day-name'));
|
||||
}
|
||||
weeks.push(JX.$N('tr', {}, weekday_names));
|
||||
|
||||
|
||||
// Render the calendar itself. NOTE: Javascript uses 0-based month indexes
|
||||
// while we use 1-based month indexes, so we have to adjust for that.
|
||||
var days = [];
|
||||
var start = new Date(value_y, value_m - 1, 1).getDay();
|
||||
while (start--) {
|
||||
days.push(cell('', null, false, 'day-placeholder'));
|
||||
}
|
||||
|
||||
var today = new Date();
|
||||
|
||||
for (var ii = 1; ii <= 31; ii++) {
|
||||
var date = new Date(value_y, value_m - 1, ii);
|
||||
if (date.getMonth() != (value_m - 1)) {
|
||||
// We've spilled over into the next month, so stop rendering.
|
||||
break;
|
||||
}
|
||||
|
||||
var is_today = (today.getYear() == date.getYear() &&
|
||||
today.getMonth() == date.getMonth() &&
|
||||
today.getDate() == date.getDate());
|
||||
|
||||
var classes = [];
|
||||
if (is_today) {
|
||||
classes.push('today');
|
||||
}
|
||||
if (date.getDay() == 0 || date.getDay() == 6) {
|
||||
classes.push('weekend');
|
||||
}
|
||||
|
||||
days.push(cell(ii, 'd:'+ii, value_d == ii, classes.join(' ')));
|
||||
}
|
||||
|
||||
// Slice the days into weeks.
|
||||
for (var ii = 0; ii < days.length; ii += 7) {
|
||||
weeks.push(JX.$N('tr', {}, days.slice(ii, ii + 7)));
|
||||
}
|
||||
|
||||
return JX.$N('table', {className: 'day-table'}, weeks);
|
||||
};
|
||||
|
||||
|
||||
var root = JX.$(config.root);
|
||||
|
||||
JX.DOM.listen(
|
||||
root,
|
||||
'click',
|
||||
'calendar-button',
|
||||
onopen);
|
||||
|
||||
JX.DOM.listen(
|
||||
root,
|
||||
'click',
|
||||
'tag:td',
|
||||
function(e) {
|
||||
e.kill();
|
||||
|
||||
var data = e.getNodeData('tag:td');
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
var p = data.value.split(':');
|
||||
switch (p[0]) {
|
||||
case 'm':
|
||||
// User clicked left or right month selection buttons.
|
||||
value_m = value_m - 1;
|
||||
value_m = value_m + parseInt(p[1]);
|
||||
if (value_m >= 12) {
|
||||
value_m -= 12;
|
||||
value_y += 1;
|
||||
} else if (value_m < 0) {
|
||||
value_m += 12;
|
||||
value_y -= 1;
|
||||
}
|
||||
value_m = value_m + 1;
|
||||
break;
|
||||
case 'd':
|
||||
// User clicked a day.
|
||||
value_d = parseInt(p[1]);
|
||||
write_date();
|
||||
|
||||
// Wait a moment to close the selector so they can see the effect
|
||||
// of their action.
|
||||
setTimeout(JX.bind(null, onclose, e), 150);
|
||||
break;
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in a new issue