The Small Calendar – jQuery + PHP w praktyce

Na Grafish Design’s Blog natknąłem się na The Small Calendar. Czyli mały kalendarz, który umożliwia sprawdzenie wybranej daty pod kątem dnia tygodnia. Przedstawiony tam kalendarz jest wersją do druku (dostępny do pobrania w ponad 30 językach). Kalendarz ten różni się od standardowych tym, że ma tylko jeden blok z dniami miesiąca. Postanowiłem stworzyć taki kalendarzyk on-line przy wykorzystaniu PHP oraz odrobiny jQuery, plus oczywiście CSS.

Cel

Kalendarz w wersji oryginalnej wygląda następująco:

The Small Calendar

The Small Calendar - Grafish Design's Blog


źródło: Grafish Design’s Blog – The small calendar 2010 (en) 1 .

Planowana przeze mnie wersja będzie miała:

  1. Wygląd przypominający oryginał
  2. Wybór roku innego niż aktualny
  3. Wielojęzyczność
  4. Możliwość podświetlania kolumny dat
  5. Możliwość podświetlania wierszy miesięcy
  6. Możliwość podświetlania dni tygodnia dla wybranych dat
  7. Możliwość przenoszenia bloku dat po kliknięciu na wiersz miesiąca

Generowanie kalendarza: PHP

Do wygenerowania kalendarza posłużymy się prostą klasą, która wygeneruje nam kilka rzeczy. Po pierwsze stworzymy listę miesięcy, dla każdego z nich sprawdzimy liczbę dni, sprawdzimy dzień tygodnia dla pierwszego dnia miesiąca. Następnie stworzymy na tej podstawie tablicę, która będzie zawierała skróty nazw miesięcy (w wybranym języku), dla wybranego roku, do każdego z miesięcy przypiszemy ilość dni oraz odpowiednio posortowane skróty nazw dni tygodnia.

Klasa ta jest bardzo prosta, zatem możemy ją nazwać imitacją klasy, zbiorem funkcji opakowanych w klasę.

Lista metod:

  1. public __construct – 2 parametry opcjonalne: rok, tablica z danymi języka | konstruktor
  2. public setLanguage – 1 parametr: tablica z tłumaczeniami | ustawienie języka
  3. public getdatesTable – 0 parametrów | tworzy tablicę dni
  4. public getCalendar – 0 parametrów | pobiera tablicę z kalendarzem
  5. private generateMonths – 0 parametrów | generuje miesiące
  6. private generateDayNames – 0 parametrów | tworzy dni tygodnia w odpowiedniej kolejności

Kod klasy

Część po stronie serwera jest na tyle prosta, że nie wymaga większego opisu. Konstruktor może przyjmować 2 parametry: rok oraz tablicę z tłumaczeniem na inny język. Oba parametry są opcjonalne, domyślnym rokiem jest aktualny, język – polski.

Metoda setLanguage ustawia język. Kolejna, getDatesTable generuje tablicę z dniami miesięcy (1-31), oznaczając odpowiednio ilości dni wg miesięcy. getCalendar wywołuje dwie inne: generateMonths – generuję tablicę miesięcy, wraz z ważnymi danymi, oraz generateDayNames – generującą skrótowe nazwy dni tygodnia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php
/**
 * VokielSmallCalendar class
 * @author Vokiel | http://blog.vokiel.com
 */
class VokielSmallCalendar {
	/**
	 * Selected year
	 * @var int	$year
	 */
	private $year;
	/**
	 * Shortcuts names for months
	 * @var	array $monthsShort
	 */
	private $monthsShort = array();
	/**
	 * Shortcuts names for week days
	 * @var	array	$daysShort 
	 */
	private $daysShort = array(); 
	/**
	 * Generated months table for the calendar
	 * @var array $months
	 */
	private $months = array();
 
	/**
	 * Construct, 
	 * @param	int	$year
	 * @param	array	$lang
	 */
	public function __construct($year=0,$lang=null){
		$this->year = ($year)? (int)$year : date('Y');
		if (!is_array($lang) || !is_array($lang['months']) || !is_array($lang['days'])){
			$lang = null;
		}
		$this->setLanguage($lang);
	}//end of __construct
 
	/**
	 * Setting the monts shortcuts names and days shorcuts names.
	 * Polish is default language.
	 * @param	array	$lang	Monts and days array starting from 1 = January and 1 = Monday
	 */
	public function setLanguage($lang=null){
		$this->monthsShort = (is_array($lang['months']))? $lang['months'] : array(1=>'Sty',2=>'Lut',3=>'Mar',4=>'Kwi',5=>'Maj',6=>'Cze',7=>'Lip',8=>'Sie',9=>'Wrz',10=>'Paź',11=>'Lis',12=>'Gru');	
        $this->daysShort = (is_array($lang['days']))? $lang['days'] : array(1=>'Pon',2=>'Wt',3=>'Śr',4=>'Czw',5=>'Pt',6=>'Sob',7=>'Nie');
	}//end of setLanguage method
 
	/**
	 * Generating the dates table (numbers from 1 to 31)
	 * @return	string	$datesTable	Generated HTML table
	 */
	public function getDatesTable(){
		$datesTable = '<table><tbody><tr>';
		for ($i=1;$i<32;$i++){
			$class = '';
			if ($i>=28){
				if ($i == $this->months[2]['daysCount'] || $i>29){
					$class = 'class="days_'.$i.'"';
				}
			}
			$datesTable .= '<td '.$class.'>'.$i.'</td>';
			if ($i%7==0){
				$datesTable .= '</tr><tr>';
			}
		}
		$datesTable .= '</tr></tbody></table>';
		return $datesTable;
	}//end of getdatesTable method
 
	/**
	 * Getting the calendar
	 * @return	array	$this->months Array with months shortcuts names and sorted week days shortcuts names	 
	 */
	public function getCalendar(){
		$this->generateMonths();
		$this->generateDayNames();
		return $this->months;
	}//end of getCalendar method
 
	/**
	 * Generating months table
	 */
	private function generateMonths(){
		for ($i=1;$i<13;$i++){
			$time = mktime(0,0,0,$i,1,$this->year);
			$this->months[$i]['weekDay'] = date('N',$time);
			$this->months[$i]['montShort'] = $this->monthsShort[$i];
			$this->months[$i]['daysCount'] = date('t',$time);
		}
	}//end of generateMonths method
 
	/**
	 * Generating week day names shortcuts
	 */
	private function generateDayNames(){
		foreach ($this->months as $month => $dayStart){
			$dayStart = $dayStart['weekDay'];
			for ($i=0;$i<7;$i++){
				$day = ($dayStart+$i>7)? (-(7-$dayStart-$i)) : ($dayStart+$i);
				$this->months[$month]['weekdays'][] = $this->daysShort[$day];
				unset($this->months[$month]['weekDay']);
			}
		}
	}//end of generateDayNames method
 
}// end of VokielSmallCalendar class
?>

Obsługa kalendarza: JavaScript

W tej części zajmiemy się oskrytpowaniem kalendarza w JavaScript (korzystając z jQuery) w celu nadania mu funkcjonalności, dynamiki i użytecznosci. Z naszych założeń wynika, że musimy utworzyć kilka funkcji, które będą reagować na przesuwanie kursora myszki ponad polami kalendarza.

Kod JavaScript

Pierwsza funkcja ma za zadanie podświetlać kolumny tabel w reakcji na najazd myszy na wiersze miesięcy. Następna jej część usuwa podświetlenie po zejściu kursora myszy z danego elementu. Taki hover-rollover.

Kolejny fragment listingu odpowiada za kliknięcie na danym miesiącu, co skutkuje małą animacją. Blok z listą dni zmienia pozycję, ustawiając się tuż pod wybranym miesiącem (klikniętym).

Funkcja sendYear ma za zadanie wskazać skryptowi PHP wybrany przez użytkownika rok. Kolejne akcje są już raczej kosmetyczne: akceptowanie tylko danych liczbowych w polu roku, automatyczne wysłanie formularza po naciśnięciu klawisza enter, wyczyszczenie pola po kliknięciu klawisza esc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
$(document).ready(function(){
	/**
	 * Actions after moving cursor over chosen month
	 * Hilights column in dates table and chosen month
	 */
	$('li:not(.daysTable) span').mouseenter(function(){
		var $this = $(this);
		var spanNr = $this.parent('li').children('span').index($this); /* nr dnia tygodnia (od zera) */
 
		$this.parent('li').addClass('hover');
 
		var tab = $this.closest('ul').find('li.daysTable table tr');
		tab.each(function(){
			$(this).find('td:eq('+spanNr+')').addClass('hover');
		});
	}).mouseleave(function(){
		var $this = $(this);
		var spanNr = $this.parent('li').children('span').index($this); /* nr dnia tygodnia (od zera) */
 
		$this.parent('li').removeClass('hover');
 
		var tab = $this.closest('ul').find('li.daysTable table tr');
		tab.each(function(){
			$(this).find('td:eq('+spanNr+')').removeClass('hover');
		});
	});
	/**
	 * Clicking on chosen month
	 * Moves table with dates just after the clicked month
	 */
	$('li:not(.datesTable)').click(function(e){
		e.preventDefault();
		var $this = $(this);
		var dt = $this.parent('ul').children('li.datesTable');
		dt.slideUp(1000,function(){
			$this.after(dt);	
		}).delay(50).slideDown('normal');
 
	});
	/**
	 * Action after moving cursor over chosen day in the table of days (1-31)
	 * Hilights days over all months
	 */
	$('li.datesTable table tr td').mouseenter(function(){
		var $this = $(this);
		var tdNr = $this.parent('tr').children('td').index($this); /* nr dnia miesiaca (od zera) */
		var sp = $this.closest('ul').children('li:not(.datesTable)');
		sp.each(function(){
			$(this).find('span:eq('+tdNr+')').addClass('hover');
		});
 
		var tab = $this.closest('table tbody').children('tr');
		tab.each(function(){
			$(this).find('td:eq('+tdNr+')').addClass('hover');
		});
	}).mouseleave(function(){
		var $this = $(this);
		var tdNr = $this.parent('tr').children('td').index($this); /* nr dnia miesiaca (od zera) */
		var sp = $this.closest('ul').children('li:not(.s)');
		sp.each(function(){
			$(this).find('span:eq('+tdNr+')').removeClass('hover');
		});
 
		var tab = $this.closest('table tbody').children('tr');
		tab.each(function(){
			$(this).find('td:eq('+tdNr+')').removeClass('hover');
		});
	});
	/**
	 * Function for sending new typed year
	 */
	function sendYear(){
		var y = parseInt($('#year').val());
		if (y>1901 && y<2038){
			location.href = '?year='+y;
		}else{
			$('#year').addClass('error');
		}
	}
	/**
	 * Accept button clicked - sending new date
	 */
	$('#accept').click(function(e){
		e.preventDefault();
		sendYear();
	});
	/**
	 * Accept only numbers
	 */
	$('#year').keyup(function(e){
		$(this).val($(this).val().replace(/\D/g,'')); 
	});
	/**
	 * Checking key code on year field (esc, enter keys only)
	 */
	$('#year').keydown(function(e){
		if (e.keyCode==13){ /* enter - send new year */
			sendYear();
		}else if (e.keyCode==27){ /* esc - clear form */
			$(this).val("");
		}
	});
	/**
	 * Remove 'error' clas after focusing year field 
	 */
	$('#year').focus(function(){
		$(this).removeClass('error');
	});
	/**
	 * Changing language
	 */
	$('#lang').change(function(){
		var url = new String(document.location); 
		url.toLowerCase();
		if (url.indexOf('year') > -1) {
			if (url.indexOf('lang') > -1){
				url = url.replace(/lang(.*)/gi,'');
			}
			document.location = url + '&lang=' + $(this).find(':selected').val();
		}else{
			document.location =  '?lang=' + $(this).find(':selected').val();
		}
	});
});

Ostylowanie CSS

Style CSS nie wymagają szerokiego omówienia. Ważne jest tylko zwrócenie uwagi na kilka znaczników, które dają możliwość szczegółowego dopasowania wyglądu kalendarza do swoich potrzeb. Kolory wierszy dla miesięcy, w których liczba dni równa się 30, 31 oraz 28 lub 29 są oznaczone wybranymi klasami:
.days_31, .days_30, .days_29 lub .days_28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* reset css */
@CHARSET "UTF-8";
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
	margin: 0;
	padding: 0;
	border: 0;
	outline: 0;
	font-size: 100%;
	vertical-align: baseline;
	background: transparent;
	font-family: "Trebuchet MS", Tahoma, Verdana, Arial, sans-serif;
	font-size: 14px;
	overflow: auto;
}
 
#vokiel_small_calendar {
    /* width: 327px; */
    width: 427px;
    margin: 0 auto;
    background: #fff;
    overflow: auto;
}
 
#vokiel_small_calendar ul {
	border: 1px solid silver;
	padding: 0px 2px;
}
#vokiel_small_calendar ul, #vokiel_small_calendar ul li {
    list-style: none;
    overflow: auto;
}
#vokiel_small_calendar ul li{
	margin: 2px 0px;
	padding: 2px 0px;
	text-align: right;
}
#vokiel_small_calendar ul li strong{
    text-align: center;
    display: block;
    float: left;
    width: 59px;
    clear: left;
    border-right: 1px solid #fff;
}
#vokiel_small_calendar ul li span{
    display: block;
    text-align: center;
    float: left;
    width: 40px;
    border-right: 1px solid #fff;
    cursor: pointer;
}
#vokiel_small_calendar .days_31 {
	background: #46A12A;
	color: #fff;
}
#vokiel_small_calendar .days_30 {
	background: #F29400;
	color: #fff;
}
#vokiel_small_calendar .days_28, #vokiel_small_calendar .days_29 {
	background: #EA609C;
	color: #fff;
}
#vokiel_small_calendar table {
	border-collapse: collapse;
	float: right;
}
#vokiel_small_calendar table, #vokiel_small_calendar table tbody, #vokiel_small_calendar table tr{
	width: 287px;
}
#vokiel_small_calendar table tr td{
	text-align: center;
	border-right: 1px solid #fff;
	cursor: default;
}
#vokiel_small_calendar ul li.hover, #vokiel_small_calendar ul li span.hover, #vokiel_small_calendar ul li table tbody tr td.hover {
	background-color: #59ACEF;
	color: #fff;
}
#vokiel_small_calendar ul li .giveYear {
	float: right; 
	width: 75px;
	text-align: center;
}
#vokiel_small_calendar ul li .giveYear #accept {
	width: 16px;
	height: 16px;
	vertical-align: middle;
}
#vokiel_small_calendar ul li .giveYear #year{
	width: 35px;
	border: 1px solid silver;
}
#vokiel_small_calendar ul li .giveYear #year.error {
	border: 1px solid red;
}
#vokiel_small_calendar ul li .giveYear p {
	font-size: 12px;
}
#vokiel_small_calendar ul li .giveYear small {
	font-size: 10px;
}

Voila

Całość została napisana przy użyciu PHP i JavaScript. Taki był pierwszy pomysł, który miał na celu zintegrować kalendarz z ważnymi wydarzeniami. Jednak nic nie stoi na przeszkodzie, aby przepisać część tworzoną w PHP na JS. Dzięki czemu otrzymalibyśmy kalendarz, który nie wymaga skryptu po stronie serwera.

W planie na bliżej nieokreśloną przyszłość mam utworzenie wersji bez udziału PHP, następnie upięcie tego w widżet opery, ewentualnie w gadżet do windy 7.

Źródła

  1. http://www.grafishdesign.it/blog/the-small-calendar-eng []
 

Przeczytaj także

Komentarze: 5

Dodaj komentarz »

 
 
 

Troszkę słaby silnik tego kalendarza skoro generuje dla lutego 31 dni. I troszkę „animacja” – toggle – średnio działająca.

 

Odpowiedz

 

Hi dear, good job!!
But in the demo didn’t run the language!! :S
I think this my idea could be very usefull if it will became a desktop widjet: are you able to do so?

 

Odpowiedz

 

@piotroo89 nie generuje 31 dni dla lutego. Jak się dobrze przyjrzysz, to luty jest tego samego koloru, co 28 w tabeli dni. Styczeń, marzec, maj itd są tego samego koloru co 31 w tabeli dni, a kwiecień, czerwiec – co 30 w tabeli dni. Kolorowanie wierszy nie jest przypadkowe. Kolory tła odpowiadają ilościom dni. Tabela dni jest uniwersalna dla wszystkich miesięcy.

Grafish Design yes, there is problem with the demo, tomorrow I will fix it, thx for info.
I’m going to do a desktop widget, an also english version of this post 😉

edit: language error fixed

 

Odpowiedz

 

Ok dear, I wait for the widget!! Give me this present!! 🙂
P.s.
Oh, dear, don’t forget to develope the calendar traslated in 32 languages, for all the users of the world! 😛
Ahahahhahahahahahaha!! 🙂

Bye!

 

Odpowiedz

 

[…] jak zaplanowałem na końcu wpisu The Small Calendar – jQuery + PHP w praktyce, postanowiłem stworzyć systemowy (windowsowy) gadżet z The Small Calendar. W tym wpisie postaram […]

 

Odpowiedz

 

Dodaj komentarz

 
(nie będzie publikowany)
 
 
Komentarz
 
 

Dozwolone tagi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
© 2009 - 2016 Vokiel.com
WordPress Theme by Arcsin modified by Vokiel