├── .gitignore ├── .hgignore ├── .travis.yml ├── CHANGELOG.md ├── tests ├── _coverage.html ├── assets │ ├── utils.js │ ├── mock.js │ ├── qunit-logging.js │ ├── coverage.js │ └── qunit.css ├── suites │ ├── keyboard_navigation │ │ ├── all.js │ │ ├── 2011.js │ │ └── 2012.js │ ├── inline.js │ ├── mouse_navigation │ │ ├── all.js │ │ ├── 2011.js │ │ └── 2012.js │ ├── calendar-weeks.js │ ├── events.js │ ├── component.js │ ├── formats.js │ └── options.js ├── tests.html ├── README.md └── run-qunit.js ├── js ├── locales │ ├── bootstrap-datepicker.ja.js │ ├── bootstrap-datepicker.kr.js │ ├── bootstrap-datepicker.zh-TW.js │ ├── bootstrap-datepicker.zh-CN.js │ ├── bootstrap-datepicker.hr.js │ ├── bootstrap-datepicker.id.js │ ├── bootstrap-datepicker.he.js │ ├── bootstrap-datepicker.ms.js │ ├── bootstrap-datepicker.tr.js │ ├── bootstrap-datepicker.ca.js │ ├── bootstrap-datepicker.da.js │ ├── bootstrap-datepicker.rs.js │ ├── bootstrap-datepicker.sv.js │ ├── bootstrap-datepicker.th.js │ ├── bootstrap-datepicker.bg.js │ ├── bootstrap-datepicker.el.js │ ├── bootstrap-datepicker.es.js │ ├── bootstrap-datepicker.nl.js │ ├── bootstrap-datepicker.pt-BR.js │ ├── bootstrap-datepicker.rs-latin.js │ ├── bootstrap-datepicker.ru.js │ ├── bootstrap-datepicker.sl.js │ ├── bootstrap-datepicker.nb.js │ ├── bootstrap-datepicker.uk.js │ ├── bootstrap-datepicker.ro.js │ ├── bootstrap-datepicker.cs.js │ ├── bootstrap-datepicker.is.js │ ├── bootstrap-datepicker.pt.js │ ├── bootstrap-datepicker.de.js │ ├── bootstrap-datepicker.fi.js │ ├── bootstrap-datepicker.sk.js │ ├── bootstrap-datepicker.fr.js │ ├── bootstrap-datepicker.hu.js │ ├── bootstrap-datepicker.it.js │ ├── bootstrap-datepicker.lv.js │ ├── bootstrap-datepicker.lt.js │ ├── bootstrap-datepicker.pl.js │ └── bootstrap-datepicker.sw.js └── bootstrap-datepicker.js ├── less └── datepicker.less ├── css └── datepicker.css ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | instrumented/ 2 | tests/coverage.html 3 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | instrumented/ 2 | tests/coverage.html 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - cd ./tests 3 | - echo "new Date().toString();" | phantomjs 4 | script: phantomjs run-qunit.js tests.html 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v1.0.0 5 | ------ 6 | 7 | Initial release: 8 | 9 | * format option 10 | * weekStart option 11 | * calendarWeeks option 12 | * startDate / endDate options 13 | * daysOfWeekDisabled option 14 | * autoclose option 15 | * startView / mnViewMode options 16 | * todayBtn / todayHighlight options 17 | * keyboardNavigation option 18 | * language option 19 | * forceParse option 20 | -------------------------------------------------------------------------------- /tests/_coverage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 | 22 | COLORIZED_LINE_HTML 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.ja.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Japanese translation for bootstrap-datepicker 3 | * Norio Suzuki 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['ja'] = { 7 | days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜", "日曜"], 8 | daysShort: ["日", "月", "火", "水", "木", "金", "土", "日"], 9 | daysMin: ["日", "月", "火", "水", "木", "金", "土", "日"], 10 | months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"], 11 | monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"] 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.kr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Korean translation for bootstrap-datepicker 3 | * Gu Youn 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['kr'] = { 7 | days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"], 8 | daysShort: ["일", "월", "화", "수", "목", "금", "토", "일"], 9 | daysMin: ["일", "월", "화", "수", "목", "금", "토", "일"], 10 | months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], 11 | monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"] 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.zh-TW.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Traditional Chinese translation for bootstrap-datepicker 3 | * Rung-Sheng Jang 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['zh-TW'] = { 7 | days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], 8 | daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"], 9 | daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"], 10 | months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], 11 | monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"] 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.zh-CN.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simplified Chinese translation for bootstrap-datepicker 3 | * Yuan Cheung 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['zh-CN'] = { 7 | days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], 8 | daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"], 9 | daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"], 10 | months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], 11 | monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], 12 | today: "今日" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.hr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Croatian localisation 3 | */ 4 | ;(function($){ 5 | $.fn.datepicker.dates['hr'] = { 6 | days: ["Nedjelja", "Ponedjelja", "Utorak", "Srijeda", "Četrtak", "Petak", "Subota", "Nedjelja"], 7 | daysShort: ["Ned", "Pon", "Uto", "Srr", "Čet", "Pet", "Sub", "Ned"], 8 | daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su", "Ne"], 9 | months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"], 10 | monthsShort: ["Sije", "Velj", "Ožu", "Tra", "Svi", "Lip", "Jul", "Kol", "Ruj", "Lis", "Stu", "Pro"], 11 | today: "Danas" 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.id.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bahasa translation for bootstrap-datepicker 3 | * Azwar Akbar 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['id'] = { 7 | days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"], 8 | daysShort: ["Mgu", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Mgu"], 9 | daysMin: ["Mg", "Sn", "Sl", "Ra", "Ka", "Ju", "Sa", "Mg"], 10 | months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"] 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.he.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hebrew translation for bootstrap-datepicker 3 | * Sagie Maoz 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['he'] = { 7 | days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"], 8 | daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"], 9 | daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"], 10 | months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"], 11 | monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"], 12 | today: "היום", 13 | rtl: true 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.ms.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Malay translation for bootstrap-datepicker 3 | * Ateman Faiz 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['ms'] = { 7 | days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu", "Ahad"], 8 | daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab", "Aha"], 9 | daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa", "Ah"], 10 | months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"], 12 | today: "Hari Ini" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.tr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Turkish translation for bootstrap-datepicker 3 | * Serkan Algur 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['tr'] = { 7 | days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"], 8 | daysShort: ["Pz", "Pzt", "Sal", "Çrş", "Prş", "Cu", "Cts", "Pz"], 9 | daysMin: ["Pz", "Pzt", "Sa", "Çr", "Pr", "Cu", "Ct", "Pz"], 10 | months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"], 11 | monthsShort: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"], 12 | today: "Bugün" 13 | }; 14 | }(jQuery)); 15 | 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.ca.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Catalan translation for bootstrap-datepicker 3 | * J. Garcia 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['ca'] = { 7 | days: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"], 8 | daysShort: ["Diu", "Dil", "Dmt", "Dmc", "Dij", "Div", "Dis", "Diu"], 9 | daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds", "dg"], 10 | months: ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"], 11 | monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"], 12 | today: "Avui" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.da.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Danish translation for bootstrap-datepicker 3 | * Christian Pedersen 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['da'] = { 7 | days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"], 8 | daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"], 9 | daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"], 10 | months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 12 | today: "I Dag" 13 | }; 14 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.rs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Serbian cyrillic translation for bootstrap-datepicker 3 | * Bojan Milosavlević 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['rs'] = { 7 | days: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота", "Недеља"], 8 | daysShort: ["Нед", "Пон", "Уто", "Сре", "Чет", "Пет", "Суб", "Нед"], 9 | daysMin: ["Н", "По", "У", "Ср", "Ч", "Пе", "Су", "Н"], 10 | months: ["Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"], 11 | monthsShort: ["Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Нов", "Дец"], 12 | today: "Данас" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.sv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Swedish translation for bootstrap-datepicker 3 | * Patrik Ragnarsson 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['sv'] = { 7 | days: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"], 8 | daysShort: ["Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör", "Sön"], 9 | daysMin: ["Sö", "Må", "Ti", "On", "To", "Fr", "Lö", "Sö"], 10 | months: ["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 12 | today: "I Dag" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.th.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thai translation for bootstrap-datepicker 3 | * Suchau Jiraprapot 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['th'] = { 7 | days: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"], 8 | daysShort: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"], 9 | daysMin: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"], 10 | months: ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"], 11 | monthsShort: ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."], 12 | today: "วันนี้" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /tests/assets/utils.js: -------------------------------------------------------------------------------- 1 | function UTCDate(){ 2 | return new Date(Date.UTC.apply(Date, arguments)); 3 | } 4 | 5 | 6 | function format_date(date){ 7 | var y = date.getUTCFullYear(), 8 | m = date.getUTCMonth() + 1, 9 | d = date.getUTCDate(), 10 | h = date.getUTCHours(), 11 | i = date.getUTCMinutes(), 12 | s = date.getUTCSeconds(), 13 | l = date.getUTCMilliseconds(); 14 | function z(i){return (i <= 9 ? '0'+i : i);} 15 | return y+'-'+z(m)+'-'+z(d)+' '+z(h)+':'+z(i)+':'+z(s)+'.'+z(l); 16 | } 17 | 18 | 19 | function datesEqual(actual, expected, message){ 20 | QUnit.push(QUnit.equiv(actual, expected), format_date(actual), format_date(expected), message); 21 | } 22 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.bg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bulgarian translation for bootstrap-datepicker 3 | * Apostol Apostolov 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['bg'] = { 7 | days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"], 8 | daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб", "Нед"], 9 | daysMin: ["Н", "П", "В", "С", "Ч", "П", "С", "Н"], 10 | months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"], 11 | monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"], 12 | today: "днес" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.el.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Greek translation for bootstrap-datepicker 3 | */ 4 | ;(function($){ 5 | $.fn.datepicker.dates['el'] = { 6 | days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"], 7 | daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"], 8 | daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"], 9 | months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"], 10 | monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"], 11 | today: "Σήμερα" 12 | }; 13 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Spanish translation for bootstrap-datepicker 3 | * Bruno Bonamin 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['es'] = { 7 | days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"], 8 | daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"], 9 | daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"], 10 | months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 11 | monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 12 | today: "Hoy" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.nl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dutch translation for bootstrap-datepicker 3 | * Reinier Goltstein 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['nl'] = { 7 | days: ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"], 8 | daysShort: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"], 9 | daysMin: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"], 10 | months: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"], 11 | monthsShort: ["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 12 | today: "Vandaag" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.pt-BR.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Brazilian translation for bootstrap-datepicker 3 | * Cauan Cabral 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['pt-BR'] = { 7 | days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"], 8 | daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"], 9 | daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"], 10 | months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 11 | monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 12 | today: "Hoje" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.rs-latin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Serbian latin translation for bootstrap-datepicker 3 | * Bojan Milosavlević 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['rs'] = { 7 | days: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota", "Nedelja"], 8 | daysShort: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub", "Ned"], 9 | daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su", "N"], 10 | months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"], 12 | today: "Danas" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.ru.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Russian translation for bootstrap-datepicker 3 | * Victor Taranenko 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['ru'] = { 7 | days: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"], 8 | daysShort: ["Вск", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Вск"], 9 | daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"], 10 | months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"], 11 | monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"], 12 | today: "Сегодня" 13 | }; 14 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.sl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Slovene translation for bootstrap-datepicker 3 | * Gregor Rudolf 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['sl'] = { 7 | days: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota", "Nedelja"], 8 | daysShort: ["Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob", "Ned"], 9 | daysMin: ["Ne", "Po", "To", "Sr", "Če", "Pe", "So", "Ne"], 10 | months: ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"], 12 | today: "Danes" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.nb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Norwegian (bokmål) translation for bootstrap-datepicker 3 | * Fredrik Sundmyhr 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['nb'] = { 7 | days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"], 8 | daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"], 9 | daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"], 10 | months: ["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"], 12 | today: "I Dag" 13 | }; 14 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.uk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ukrainian translation for bootstrap-datepicker 3 | * Andrey Vityuk 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['uk'] = { 7 | days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота", "Неділя"], 8 | daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Нед"], 9 | daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд"], 10 | months: ["Січень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"], 11 | monthsShort: ["Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"], 12 | today: "Сьогодні" 13 | }; 14 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.ro.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Romanian translation for bootstrap-datepicker 3 | * Cristian Vasile 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['ro'] = { 7 | days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă", "Duminică"], 8 | daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm", "Dum"], 9 | daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ", "Du"], 10 | months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"], 11 | monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"], 12 | today: "Astăzi", 13 | weekStart: 1 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.cs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Czech translation for bootstrap-datepicker 3 | * Matěj Koubík 4 | * Fixes by Michal Remiš 5 | */ 6 | ;(function($){ 7 | $.fn.datepicker.dates['cs'] = { 8 | days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"], 9 | daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob", "Ned"], 10 | daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"], 11 | months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"], 12 | monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"], 13 | today: "Dnes" 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.is.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Icelandic translation for bootstrap-datepicker 3 | * Hinrik Örn Sigurðsson 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['is'] = { 7 | days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur", "Sunnudagur"], 8 | daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau", "Sun"], 9 | daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La", "Su"], 10 | months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"], 11 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"], 12 | today: "Í Dag" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.pt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Portuguese translation for bootstrap-datepicker 3 | * Original code: Cauan Cabral 4 | * Tiago Melo 5 | */ 6 | ;(function($){ 7 | $.fn.datepicker.dates['pt'] = { 8 | days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"], 9 | daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"], 10 | daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"], 11 | months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 12 | monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"] 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.de.js: -------------------------------------------------------------------------------- 1 | /** 2 | * German translation for bootstrap-datepicker 3 | * Sam Zurcher 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['de'] = { 7 | days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], 8 | daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"], 9 | daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], 10 | months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"], 11 | monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"], 12 | today: "Heute", 13 | weekStart: 1, 14 | format: "dd.mm.yyyy" 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.fi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Finnish translation for bootstrap-datepicker 3 | * Jaakko Salonen 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['fi'] = { 7 | days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"], 8 | daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"], 9 | daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"], 10 | months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"], 11 | monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"], 12 | today: "tänään" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.sk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Slovak translation for bootstrap-datepicker 3 | * Marek Lichtner 4 | * Fixes by Michal Remiš 5 | */ 6 | ;(function($){ 7 | $.fn.datepicker.dates["sk"] = { 8 | days: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"], 9 | daysShort: ["Ned", "Pon", "Uto", "Str", "Štv", "Pia", "Sob", "Ned"], 10 | daysMin: ["Ne", "Po", "Ut", "St", "Št", "Pia", "So", "Ne"], 11 | months: ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"], 12 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Máj", "Jún", "Júl", "Aug", "Sep", "Okt", "Nov", "Dec"], 13 | today: "Dnes" 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.fr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * French translation for bootstrap-datepicker 3 | * Nico Mollet 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['fr'] = { 7 | days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"], 8 | daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"], 9 | daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"], 10 | months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"], 11 | monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"], 12 | today: "Aujourd'hui", 13 | weekStart: 1, 14 | format: "dd/mm/yyyy" 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.hu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hungarian translation for bootstrap-datepicker 3 | * Sotus László 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['hu'] = { 7 | days: ["Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat", "Vasárnap"], 8 | daysShort: ["Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo", "Vas"], 9 | daysMin: ["Va", "Hé", "Ke", "Sz", "Cs", "Pé", "Sz", "Va"], 10 | months: ["Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"], 11 | monthsShort: ["Jan", "Feb", "Már", "Ápr", "Máj", "Jún", "Júl", "Aug", "Sze", "Okt", "Nov", "Dec"], 12 | today: "Ma", 13 | weekStart: 1, 14 | format: "yyyy.mm.dd" 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.it.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Italian translation for bootstrap-datepicker 3 | * Enrico Rubboli 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['it'] = { 7 | days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"], 8 | daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"], 9 | daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"], 10 | months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"], 11 | monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"], 12 | today: "Oggi", 13 | weekStart: 1, 14 | format: "dd/mm/yyyy" 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.lv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Latvian translation for bootstrap-datepicker 3 | * Artis Avotins 4 | */ 5 | 6 | ;(function($){ 7 | $.fn.datepicker.dates['lv'] = { 8 | days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena", "Svētdiena"], 9 | daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S", "Sv"], 10 | daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "St", "Sv"], 11 | months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"], 12 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec."], 13 | today: "Šodien", 14 | weekStart: 1 15 | }; 16 | }(jQuery)); -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.lt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Lithuanian translation for bootstrap-datepicker 3 | * Šarūnas Gliebus 4 | */ 5 | 6 | ;(function($){ 7 | $.fn.datepicker.dates['lt'] = { 8 | days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis", "Sekmadienis"], 9 | daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š", "S"], 10 | daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št", "Sk"], 11 | months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"], 12 | monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"], 13 | today: "Šiandien", 14 | weekStart: 1 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /tests/suites/keyboard_navigation/all.js: -------------------------------------------------------------------------------- 1 | module('Keyboard Navigation (All)', { 2 | setup: function(){ 3 | this.input = $('') 4 | .appendTo('#qunit-fixture') 5 | .datepicker({format: "dd-mm-yyyy"}) 6 | .focus(); // Activate for visibility checks 7 | this.dp = this.input.data('datepicker') 8 | this.picker = this.dp.picker; 9 | }, 10 | teardown: function(){ 11 | this.picker.remove(); 12 | } 13 | }); 14 | 15 | test('TAB hides picker', function(){ 16 | var target; 17 | 18 | ok(this.picker.is(':visible'), 'Picker is visible'); 19 | 20 | this.input.trigger({ 21 | type: 'keydown', 22 | keyCode: 9 23 | }); 24 | 25 | ok(this.picker.is(':not(:visible)'), 'Picker is hidden'); 26 | }); 27 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.pl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Polish translation for bootstrap-datepicker 3 | * Robert 4 | */ 5 | ;(function($){ 6 | $.fn.datepicker.dates['pl'] = { 7 | days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"], 8 | daysShort: ["Nie", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nie"], 9 | daysMin: ["N", "Pn", "Wt", "Śr", "Cz", "Pt", "So", "N"], 10 | months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"], 11 | monthsShort: ["Sty", "Lu", "Mar", "Kw", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa", "Lis", "Gru"], 12 | today: "Dzisiaj", 13 | weekStart: 1 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /js/locales/bootstrap-datepicker.sw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Swahili translation for bootstrap-datepicker 3 | * Edwin Mugendi 4 | * Source: http://scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=xnfaqyzcku 5 | */ 6 | ;(function($){ 7 | $.fn.datepicker.dates['sw'] = { 8 | days: ["Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi", "Jumapili"], 9 | daysShort: ["J2", "J3", "J4", "J5", "Alh", "Ij", "J1", "J2"], 10 | daysMin: ["2", "3", "4", "5", "A", "I", "1", "2"], 11 | months: ["Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba", "Oktoba", "Novemba", "Desemba"], 12 | monthsShort: ["Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des"], 13 | today: "Leo" 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /tests/suites/inline.js: -------------------------------------------------------------------------------- 1 | module('Inline', { 2 | setup: function(){ 3 | this.component = $('
') 4 | .appendTo('#qunit-fixture') 5 | .datepicker({format: "dd-mm-yyyy"}); 6 | this.dp = this.component.data('datepicker') 7 | this.picker = this.dp.picker; 8 | }, 9 | teardown: function(){ 10 | this.picker.remove(); 11 | } 12 | }); 13 | 14 | 15 | test('Picker gets date/viewDate from data-date attr', function(){ 16 | datesEqual(this.dp.date, UTCDate(2012, 1, 12)); 17 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 12)); 18 | }); 19 | 20 | 21 | test('Visible after init', function(){ 22 | ok(this.picker.is(':visible')); 23 | }); 24 | 25 | test('update', function(){ 26 | this.dp.update('13-03-2012') 27 | datesEqual(this.dp.date, UTCDate(2012, 2, 13)); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/assets/mock.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | window.patch_date = function patch(f){ 4 | var NativeDate = window.Date; 5 | var date = function date(y,m,d,h,i,s,j){ 6 | switch(arguments.length){ 7 | case 0: return date.now ? new NativeDate(date.now) : new NativeDate(); 8 | case 1: return new NativeDate(y); 9 | case 2: return new NativeDate(y,m); 10 | case 3: return new NativeDate(y,m,d); 11 | case 4: return new NativeDate(y,m,d,h); 12 | case 5: return new NativeDate(y,m,d,h,i); 13 | case 6: return new NativeDate(y,m,d,h,i,s); 14 | case 7: return new NativeDate(y,y,m,d,h,i,s,j); 15 | } 16 | }; 17 | date.UTC = NativeDate.UTC; 18 | return function(){ 19 | Array.prototype.push.call(arguments, date); 20 | window.Date = date; 21 | res = f.apply(this, arguments); 22 | window.Date = NativeDate; 23 | } 24 | } 25 | 26 | }()); -------------------------------------------------------------------------------- /tests/assets/qunit-logging.js: -------------------------------------------------------------------------------- 1 | // Dummy logging calls (ie, if tests are run in IE) 2 | window.console = window.console || {}; 3 | window.console.log = window.console.log || function(){}; 4 | window.console.debug = window.console.debug || function(){}; 5 | window.console.info = window.console.info || function(){}; 6 | window.console.warn = window.console.warn || function(){}; 7 | window.console.error = window.console.error || function(){}; 8 | 9 | (function() { 10 | var modName, testName; 11 | 12 | //arg: { name } 13 | QUnit.testStart = function(t) { 14 | modName = t.module; 15 | testName = t.name; 16 | }; 17 | 18 | //arg: { name, failed, passed, total } 19 | QUnit.testDone = function(t) { 20 | if (t.failed) 21 | console.log('Test "' + t.module + ': ' + t.name + '" completed: ' + (0 === t.failed ? 'pass' : 'FAIL') + '\n') 22 | }; 23 | 24 | //{ result, actual, expected, message } 25 | QUnit.log = function(t) { 26 | if (!t.result) 27 | console.log('Test "' + modName + ': ' + testName + '" assertion failed. Expected <' + t.expected + '> Actual <' + t.actual + '>' + (t.message ? ': \'' + t.message + '\'' : '')); 28 | }; 29 | }()); 30 | -------------------------------------------------------------------------------- /tests/suites/mouse_navigation/all.js: -------------------------------------------------------------------------------- 1 | module('Mouse Navigation (All)', { 2 | setup: function(){ 3 | this.input = $('') 4 | .appendTo('#qunit-fixture') 5 | .datepicker({format: "dd-mm-yyyy"}) 6 | .focus(); // Activate for visibility checks 7 | this.dp = this.input.data('datepicker') 8 | this.picker = this.dp.picker; 9 | }, 10 | teardown: function(){ 11 | this.picker.remove(); 12 | } 13 | }); 14 | 15 | test('Clicking datepicker does not hide datepicker', function(){ 16 | ok(this.picker.is(':visible'), 'Picker is visible'); 17 | this.picker.trigger('mousedown'); 18 | ok(this.picker.is(':visible'), 'Picker is still visible'); 19 | }); 20 | 21 | test('Clicking outside datepicker hides datepicker', function(){ 22 | var $otherelement = $('
'); 23 | $('body').append($otherelement); 24 | 25 | ok(this.picker.is(':visible'), 'Picker is visible'); 26 | this.input.trigger('click'); 27 | ok(this.picker.is(':visible'), 'Picker is still visible'); 28 | 29 | $otherelement.trigger('mousedown'); 30 | ok(this.picker.is(':not(:visible)'), 'Picker is hidden'); 31 | 32 | $otherelement.remove(); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/suites/calendar-weeks.js: -------------------------------------------------------------------------------- 1 | module('Calendar Weeks', { 2 | setup: function(){ 3 | this.input = $('') 4 | .appendTo('#qunit-fixture') 5 | .val('2013-01-14') 6 | .datepicker({ 7 | format: 'yyyy-mm-dd', 8 | calendarWeeks: true 9 | }) 10 | .focus(); // Activate for visibility checks 11 | this.dp = this.input.data('datepicker') 12 | this.picker = this.dp.picker; 13 | }, 14 | teardown: function(){ 15 | this.picker.remove(); 16 | } 17 | }); 18 | 19 | test('adds cw header column', function(){ 20 | var target = this.picker.find('.datepicker-days thead th:first-child'); 21 | ok(target.hasClass('cw'), 'First column heading is from cw column'); 22 | }); 23 | 24 | test('adds calendar week cells to each day row', function(){ 25 | var target = this.picker.find('.datepicker-days tbody tr'); 26 | 27 | expect(target.length); 28 | target.each(function(i){ 29 | var t = $(this).children().first(); 30 | ok(t.hasClass('cw'), "First column is cw column"); 31 | }); 32 | }); 33 | 34 | test('displays correct calendar week', function(){ 35 | var target = this.picker.find('.datepicker-days tbody tr'); 36 | 37 | expect(target.length); 38 | target.each(function(i){ 39 | var t = $(this).children().first(); 40 | equal(t.text(), i+1, "Displays correct calendar weeks"); 41 | }); 42 | }); 43 | 44 | test('it prepends column to switcher thead row', function(){ 45 | var target = this.picker.find('.datepicker-days thead tr:first-child'); 46 | equal(target.children().length, 4, 'first row has 4 columns'); 47 | ok(target.children().first().hasClass('cw'), 'cw column is prepended'); 48 | }); 49 | -------------------------------------------------------------------------------- /tests/assets/coverage.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | //we want this at global scope so outside callers can find it. In a more realistic implementation we 3 | //should probably put it in a namespace. 4 | window.getCoverageByLine = function(silent) { 5 | var key = null; 6 | var lines = null; 7 | var source = null; 8 | //look for code coverage data 9 | if (typeof window._$jscoverage === 'object') { 10 | for (key in _$jscoverage) {} 11 | lines = _$jscoverage[key]; 12 | } 13 | 14 | if (!lines && !silent) { 15 | console.log('code coverage data is NOT available'); 16 | } 17 | 18 | return { 'key': key, 'lines': lines }; 19 | }; 20 | 21 | QUnit.done = function(t) { 22 | var cvgInfo = getCoverageByLine(true); 23 | if (!!cvgInfo.key) { 24 | var testableLines = 0; 25 | var testedLines = 0; 26 | var untestableLines = 0; 27 | for (lineIdx in cvgInfo.lines) { 28 | var cvg = cvgInfo.lines[lineIdx]; 29 | if (typeof cvg === 'number') { 30 | testableLines += 1; 31 | if (cvg > 0) { 32 | testedLines += 1; 33 | } 34 | } else { 35 | untestableLines += 1; 36 | } 37 | } 38 | var coverage = '' + Math.floor(100 * testedLines / testableLines) + '%'; 39 | 40 | var result = document.getElementById('qunit-testresult'); 41 | if (result != null) { 42 | result.innerHTML = result.innerHTML + ' ' + coverage + ' test coverage of ' + cvgInfo.key; 43 | } else { 44 | console.log('can\'t find test-result element to update'); 45 | } 46 | } 47 | }; 48 | }()); -------------------------------------------------------------------------------- /tests/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |

bootstrap-datepicker

41 |

42 |
43 |

44 |
    45 |
    46 | 47 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Unit tests, written with [QUnit](http://docs.jquery.com/QUnit), are used to 2 | expose bugs for squashing, prevent bugs from respawning, and suppress new 3 | bugs when adding new features and making changes. 4 | 5 | # Running the tests 6 | 7 | The simplest way to run the tests is to open `tests/tests.html` in your browser. 8 | The test suites will automatically run themselves and present their results. 9 | 10 | To run the tests from the command line, download and install 11 | [PhantomJS](http://phantomjs.org/), and run `run-qunit.js` with it: 12 | 13 | $ cd tests/ 14 | $ phantomjs run-qunit.js tests.html 15 | 16 | Failed tests and their failed assertions will be printed to the console. A 17 | results summary will be printed at the end. 18 | 19 | To generate coverage statistics, use [JSCoverage](http://siliconforks.com/jscoverage/) 20 | to instrument the js files: 21 | 22 | $ cd tests/ 23 | $ jscoverage ../js/ ../instrumented/ 24 | $ phantomjs run-qunit.js tests.html 25 | 26 | Coverage percentage will be included in the output summary, and a highlighted 27 | line-by-line html file will be generated. 28 | 29 | # Shout-out 30 | 31 | Thanks to Rod @ While One Fork for the 32 | [CIS guide](http://whileonefork.blogspot.com/2011/10/integrating-javascript-tests-into-cli.html) 33 | on putting the above together. 34 | 35 | # Adding tests 36 | 37 | Tests go in js files in the `tests/suites/` directory tree. QUnit organizes 38 | tests into suites called "modules"; there is one module per js file. If the 39 | tests you are adding do not fit into an existing module, create a new one at 40 | `tests/suites/.js`, where `` is a broad yet 41 | descriptive name for the suite. If tests have many year-specific cases (ie, 42 | behave differently in leap years vs normal years, or have specific buggy 43 | behavior in a certain year), create the module in a new directory, 44 | `tests/suites//.js`, where `` is the decriptive 45 | name and `` is the four-digit year the tests pertain to. 46 | 47 | In order for new tests to be run, they must be imported into `tests/tests.html`. 48 | Find the script includes headed by the html comment ``, and 49 | add a new one to the list which includes the new js files. 50 | 51 | # Can I use this? 52 | 53 | By all means, please do! Just note that I stopped working on this structure 54 | once it fit my needs, there's no real support for it, and it may change in the 55 | future. Otherwise, have at it. 56 | -------------------------------------------------------------------------------- /tests/suites/mouse_navigation/2011.js: -------------------------------------------------------------------------------- 1 | module('Mouse Navigation 2011', { 2 | setup: function(){ 3 | /* 4 | Tests start with picker on March 31, 2011. 5 | */ 6 | this.input = $('') 7 | .appendTo('#qunit-fixture') 8 | .datepicker({format: "dd-mm-yyyy"}) 9 | .focus(); // Activate for visibility checks 10 | this.dp = this.input.data('datepicker') 11 | this.picker = this.dp.picker; 12 | }, 13 | teardown: function(){ 14 | this.picker.remove(); 15 | } 16 | }); 17 | 18 | test('Selecting date from previous month while in January changes month and year displayed', function(){ 19 | var target; 20 | 21 | this.input.val('01-01-2011'); 22 | this.dp.update(); 23 | datesEqual(this.dp.viewDate, UTCDate(2011, 0, 1)) 24 | datesEqual(this.dp.date, UTCDate(2011, 0, 1)) 25 | 26 | // Rendered correctly 27 | equal(this.dp.viewMode, 0); 28 | target = this.picker.find('.datepicker-days tbody td:first'); 29 | equal(target.text(), '26'); // Should be Dec 26 30 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'January 2011'); 31 | 32 | // Updated internally on click 33 | target.click(); 34 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'December 2010'); 35 | datesEqual(this.dp.viewDate, UTCDate(2010, 11, 26)) 36 | datesEqual(this.dp.date, UTCDate(2010, 11, 26)) 37 | 38 | // Re-rendered on click 39 | target = this.picker.find('.datepicker-days tbody td:first'); 40 | equal(target.text(), '28'); // Should be Nov 28 41 | }); 42 | 43 | test('Selecting date from next month while in December changes month and year displayed', function(){ 44 | var target; 45 | 46 | this.input.val('01-12-2010'); 47 | this.dp.update(); 48 | datesEqual(this.dp.viewDate, UTCDate(2010, 11, 1)) 49 | datesEqual(this.dp.date, UTCDate(2010, 11, 1)) 50 | 51 | // Rendered correctly 52 | equal(this.dp.viewMode, 0); 53 | target = this.picker.find('.datepicker-days tbody td:last'); 54 | equal(target.text(), '8'); // Should be Jan 8 55 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'December 2010'); 56 | 57 | // Updated internally on click 58 | target.click(); 59 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'January 2011'); 60 | datesEqual(this.dp.viewDate, UTCDate(2011, 0, 8)) 61 | datesEqual(this.dp.date, UTCDate(2011, 0, 8)) 62 | 63 | // Re-rendered on click 64 | target = this.picker.find('.datepicker-days tbody td:first'); 65 | equal(target.text(), '26'); // Should be Dec 26 66 | }); 67 | -------------------------------------------------------------------------------- /tests/suites/events.js: -------------------------------------------------------------------------------- 1 | module('Events', { 2 | setup: function(){ 3 | this.input = $('') 4 | .appendTo('#qunit-fixture') 5 | .datepicker({format: "dd-mm-yyyy"}) 6 | .focus(); // Activate for visibility checks 7 | this.dp = this.input.data('datepicker') 8 | this.picker = this.dp.picker; 9 | }, 10 | teardown: function(){ 11 | this.picker.remove(); 12 | } 13 | }); 14 | 15 | test('Selecting a year from decade view triggers pickYear', function(){ 16 | var target, 17 | triggered = 0; 18 | 19 | this.input.on('changeYear', function(){ 20 | triggered++; 21 | }); 22 | 23 | equal(this.dp.viewMode, 0); 24 | target = this.picker.find('.datepicker-days thead th.switch'); 25 | ok(target.is(':visible'), 'View switcher is visible'); 26 | 27 | target.click(); 28 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 29 | equal(this.dp.viewMode, 1); 30 | // Not modified when switching modes 31 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 32 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 33 | 34 | target = this.picker.find('.datepicker-months thead th.switch'); 35 | ok(target.is(':visible'), 'View switcher is visible'); 36 | 37 | target.click(); 38 | ok(this.picker.find('.datepicker-years').is(':visible'), 'Year picker is visible'); 39 | equal(this.dp.viewMode, 2); 40 | // Not modified when switching modes 41 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 42 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 43 | 44 | // Change years to test internal state changes 45 | target = this.picker.find('.datepicker-years tbody span:contains(2010)'); 46 | target.click(); 47 | equal(this.dp.viewMode, 1); 48 | // Only viewDate modified 49 | datesEqual(this.dp.viewDate, UTCDate(2010, 2, 1)); 50 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 51 | equal(triggered, 1); 52 | }); 53 | 54 | test('Selecting a month from year view triggers pickMonth', function(){ 55 | var target, 56 | triggered = 0; 57 | 58 | this.input.on('changeMonth', function(){ 59 | triggered++; 60 | }); 61 | 62 | equal(this.dp.viewMode, 0); 63 | target = this.picker.find('.datepicker-days thead th.switch'); 64 | ok(target.is(':visible'), 'View switcher is visible'); 65 | 66 | target.click(); 67 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 68 | equal(this.dp.viewMode, 1); 69 | // Not modified when switching modes 70 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 71 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 72 | 73 | target = this.picker.find('.datepicker-months tbody span:contains(Apr)'); 74 | target.click(); 75 | equal(this.dp.viewMode, 0); 76 | // Only viewDate modified 77 | datesEqual(this.dp.viewDate, UTCDate(2011, 3, 1)); 78 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 79 | equal(triggered, 1); 80 | }); 81 | -------------------------------------------------------------------------------- /tests/suites/keyboard_navigation/2011.js: -------------------------------------------------------------------------------- 1 | module('Keyboard Navigation 2011', { 2 | setup: function(){ 3 | /* 4 | Tests start with picker on March 31, 2011. Fun facts: 5 | 6 | * March 1, 2011 was on a Tuesday 7 | * March 31, 2011 was on a Thursday 8 | */ 9 | this.input = $('') 10 | .appendTo('#qunit-fixture') 11 | .datepicker({format: "dd-mm-yyyy"}) 12 | .focus(); // Activate for visibility checks 13 | this.dp = this.input.data('datepicker') 14 | this.picker = this.dp.picker; 15 | }, 16 | teardown: function(){ 17 | this.picker.remove(); 18 | } 19 | }); 20 | 21 | test('Regression: by week (up/down arrows); up from Mar 6, 2011 should go to Feb 27, 2011', function(){ 22 | var target; 23 | 24 | this.input.val('06-03-2011').datepicker('update'); 25 | 26 | equal(this.dp.viewMode, 0); 27 | target = this.picker.find('.datepicker-days thead th.switch'); 28 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 29 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 6)); 30 | datesEqual(this.dp.date, UTCDate(2011, 2, 6)); 31 | 32 | // Navigation: -1 week, up arrow key 33 | this.input.trigger({ 34 | type: 'keydown', 35 | keyCode: 38 36 | }); 37 | datesEqual(this.dp.viewDate, UTCDate(2011, 1, 27)); 38 | datesEqual(this.dp.date, UTCDate(2011, 1, 27)); 39 | target = this.picker.find('.datepicker-days thead th.switch'); 40 | equal(target.text(), 'February 2011', 'Title is "February 2011"'); 41 | }); 42 | 43 | test('Regression: by day (left/right arrows); left from Mar 1, 2011 should go to Feb 28, 2011', function(){ 44 | var target; 45 | 46 | this.input.val('01-03-2011').datepicker('update'); 47 | 48 | equal(this.dp.viewMode, 0); 49 | target = this.picker.find('.datepicker-days thead th.switch'); 50 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 51 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 1)); 52 | datesEqual(this.dp.date, UTCDate(2011, 2, 1)); 53 | 54 | // Navigation: -1 day left arrow key 55 | this.input.trigger({ 56 | type: 'keydown', 57 | keyCode: 37 58 | }); 59 | datesEqual(this.dp.viewDate, UTCDate(2011, 1, 28)); 60 | datesEqual(this.dp.date, UTCDate(2011, 1, 28)); 61 | target = this.picker.find('.datepicker-days thead th.switch'); 62 | equal(target.text(), 'February 2011', 'Title is "February 2011"'); 63 | }); 64 | 65 | test('Regression: by month (shift + left/right arrows); left from Mar 15, 2011 should go to Feb 15, 2011', function(){ 66 | var target; 67 | 68 | this.input.val('15-03-2011').datepicker('update'); 69 | 70 | equal(this.dp.viewMode, 0); 71 | target = this.picker.find('.datepicker-days thead th.switch'); 72 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 73 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 15)); 74 | datesEqual(this.dp.date, UTCDate(2011, 2, 15)); 75 | 76 | // Navigation: -1 month, shift + left arrow key 77 | this.input.trigger({ 78 | type: 'keydown', 79 | keyCode: 37, 80 | shiftKey: true 81 | }); 82 | datesEqual(this.dp.viewDate, UTCDate(2011, 1, 15)); 83 | datesEqual(this.dp.date, UTCDate(2011, 1, 15)); 84 | target = this.picker.find('.datepicker-days thead th.switch'); 85 | equal(target.text(), 'February 2011', 'Title is "February 2011"'); 86 | }); 87 | -------------------------------------------------------------------------------- /less/datepicker.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Datepicker for Bootstrap 3 | * 4 | * Copyright 2012 Stefan Petre 5 | * Improvements by Andrew Rowls 6 | * Licensed under the Apache License v2.0 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | */ 10 | 11 | .datepicker { 12 | padding: 4px; 13 | margin-top: 1px; 14 | .border-radius(4px); 15 | &-inline { 16 | width: 220px; 17 | } 18 | direction: ltr; 19 | &&-rtl { 20 | direction: rtl; 21 | table tr td span { 22 | float: right; 23 | } 24 | } 25 | &-dropdown { 26 | top: 0; 27 | left: 0; 28 | &:before { 29 | content: ''; 30 | display: inline-block; 31 | border-left: 7px solid transparent; 32 | border-right: 7px solid transparent; 33 | border-bottom: 7px solid #ccc; 34 | border-bottom-color: rgba(0,0,0,.2); 35 | position: absolute; 36 | top: -7px; 37 | left: 6px; 38 | } 39 | &:after { 40 | content: ''; 41 | display: inline-block; 42 | border-left: 6px solid transparent; 43 | border-right: 6px solid transparent; 44 | border-bottom: 6px solid @white; 45 | position: absolute; 46 | top: -6px; 47 | left: 7px; 48 | } 49 | } 50 | >div { 51 | display: none; 52 | } 53 | &.days div.datepicker-days { 54 | display: block; 55 | } 56 | &.months div.datepicker-months { 57 | display: block; 58 | } 59 | &.years div.datepicker-years { 60 | display: block; 61 | } 62 | table{ 63 | margin: 0; 64 | } 65 | td, 66 | th{ 67 | text-align: center; 68 | width: 20px; 69 | height: 20px; 70 | .border-radius(4px); 71 | 72 | border: none; 73 | } 74 | // Inline display inside a table presents some problems with 75 | // border and background colors. 76 | .table-striped & table tr { 77 | td, th { 78 | background-color:transparent; 79 | } 80 | } 81 | table tr td { 82 | &.day:hover { 83 | background: @grayLighter; 84 | cursor: pointer; 85 | } 86 | &.old, 87 | &.new { 88 | color: @grayLight; 89 | } 90 | &.disabled, 91 | &.disabled:hover { 92 | background: none; 93 | color: @grayLight; 94 | cursor: default; 95 | } 96 | &.today, 97 | &.today:hover, 98 | &.today.disabled, 99 | &.today.disabled:hover { 100 | @todayBackground: lighten(@orange, 30%); 101 | .buttonBackground(@todayBackground, spin(@todayBackground, 20)); 102 | } 103 | &.active, 104 | &.active:hover, 105 | &.active.disabled, 106 | &.active.disabled:hover { 107 | .buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20)); 108 | color: #fff; 109 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 110 | } 111 | span { 112 | display: block; 113 | width: 23%; 114 | height: 54px; 115 | line-height: 54px; 116 | float: left; 117 | margin: 1%; 118 | cursor: pointer; 119 | .border-radius(4px); 120 | &:hover { 121 | background: @grayLighter; 122 | } 123 | &.disabled, 124 | &.disabled:hover { 125 | background:none; 126 | color: @grayLight; 127 | cursor: default; 128 | } 129 | &.active, 130 | &.active:hover, 131 | &.active.disabled, 132 | &.active.disabled:hover { 133 | .buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20)); 134 | color: #fff; 135 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 136 | } 137 | &.old { 138 | color: @grayLight; 139 | } 140 | } 141 | } 142 | 143 | th.switch { 144 | width: 145px; 145 | } 146 | 147 | thead tr:first-child th, 148 | tfoot tr:first-child th { 149 | cursor: pointer; 150 | &:hover{ 151 | background: @grayLighter; 152 | } 153 | } 154 | /*.dow { 155 | border-top: 1px solid #ddd !important; 156 | }*/ 157 | 158 | // Basic styling for calendar-week cells 159 | .cw { 160 | font-size: 10px; 161 | width: 12px; 162 | padding: 0 2px 0 5px; 163 | vertical-align: middle; 164 | } 165 | thead tr:first-child th.cw { 166 | cursor: default; 167 | background-color: transparent; 168 | } 169 | } 170 | .input-append, 171 | .input-prepend { 172 | &.date { 173 | .add-on i { 174 | display: block; 175 | cursor: pointer; 176 | width: 16px; 177 | height: 16px; 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /tests/assets/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.5.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 15px 15px 0 0; 42 | -moz-border-radius: 15px 15px 0 0; 43 | -webkit-border-top-right-radius: 15px; 44 | -webkit-border-top-left-radius: 15px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-header label { 58 | display: inline-block; 59 | } 60 | 61 | #qunit-banner { 62 | height: 5px; 63 | } 64 | 65 | #qunit-testrunner-toolbar { 66 | padding: 0.5em 0 0.5em 2em; 67 | color: #5E740B; 68 | background-color: #eee; 69 | } 70 | 71 | #qunit-userAgent { 72 | padding: 0.5em 0 0.5em 2.5em; 73 | background-color: #2b81af; 74 | color: #fff; 75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 76 | } 77 | 78 | 79 | /** Tests: Pass/Fail */ 80 | 81 | #qunit-tests { 82 | list-style-position: inside; 83 | } 84 | 85 | #qunit-tests li { 86 | padding: 0.4em 0.5em 0.4em 2.5em; 87 | border-bottom: 1px solid #fff; 88 | list-style-position: inside; 89 | } 90 | 91 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 92 | display: none; 93 | } 94 | 95 | #qunit-tests li strong { 96 | cursor: pointer; 97 | } 98 | 99 | #qunit-tests li a { 100 | padding: 0.5em; 101 | color: #c2ccd1; 102 | text-decoration: none; 103 | } 104 | #qunit-tests li a:hover, 105 | #qunit-tests li a:focus { 106 | color: #000; 107 | } 108 | 109 | #qunit-tests ol { 110 | margin-top: 0.5em; 111 | padding: 0.5em; 112 | 113 | background-color: #fff; 114 | 115 | border-radius: 15px; 116 | -moz-border-radius: 15px; 117 | -webkit-border-radius: 15px; 118 | 119 | box-shadow: inset 0px 2px 13px #999; 120 | -moz-box-shadow: inset 0px 2px 13px #999; 121 | -webkit-box-shadow: inset 0px 2px 13px #999; 122 | } 123 | 124 | #qunit-tests table { 125 | border-collapse: collapse; 126 | margin-top: .2em; 127 | } 128 | 129 | #qunit-tests th { 130 | text-align: right; 131 | vertical-align: top; 132 | padding: 0 .5em 0 0; 133 | } 134 | 135 | #qunit-tests td { 136 | vertical-align: top; 137 | } 138 | 139 | #qunit-tests pre { 140 | margin: 0; 141 | white-space: pre-wrap; 142 | word-wrap: break-word; 143 | } 144 | 145 | #qunit-tests del { 146 | background-color: #e0f2be; 147 | color: #374e0c; 148 | text-decoration: none; 149 | } 150 | 151 | #qunit-tests ins { 152 | background-color: #ffcaca; 153 | color: #500; 154 | text-decoration: none; 155 | } 156 | 157 | /*** Test Counts */ 158 | 159 | #qunit-tests b.counts { color: black; } 160 | #qunit-tests b.passed { color: #5E740B; } 161 | #qunit-tests b.failed { color: #710909; } 162 | 163 | #qunit-tests li li { 164 | margin: 0.5em; 165 | padding: 0.4em 0.5em 0.4em 0.5em; 166 | background-color: #fff; 167 | border-bottom: none; 168 | list-style-position: inside; 169 | } 170 | 171 | /*** Passing Styles */ 172 | 173 | #qunit-tests li li.pass { 174 | color: #5E740B; 175 | background-color: #fff; 176 | border-left: 26px solid #C6E746; 177 | } 178 | 179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 180 | #qunit-tests .pass .test-name { color: #366097; } 181 | 182 | #qunit-tests .pass .test-actual, 183 | #qunit-tests .pass .test-expected { color: #999999; } 184 | 185 | #qunit-banner.qunit-pass { background-color: #C6E746; } 186 | 187 | /*** Failing Styles */ 188 | 189 | #qunit-tests li li.fail { 190 | color: #710909; 191 | background-color: #fff; 192 | border-left: 26px solid #EE5757; 193 | white-space: pre; 194 | } 195 | 196 | #qunit-tests > li:last-child { 197 | border-radius: 0 0 15px 15px; 198 | -moz-border-radius: 0 0 15px 15px; 199 | -webkit-border-bottom-right-radius: 15px; 200 | -webkit-border-bottom-left-radius: 15px; 201 | } 202 | 203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 204 | #qunit-tests .fail .test-name, 205 | #qunit-tests .fail .module-name { color: #000000; } 206 | 207 | #qunit-tests .fail .test-actual { color: #EE5757; } 208 | #qunit-tests .fail .test-expected { color: green; } 209 | 210 | #qunit-banner.qunit-fail { background-color: #EE5757; } 211 | 212 | 213 | /** Result */ 214 | 215 | #qunit-testresult { 216 | padding: 0.5em 0.5em 0.5em 2.5em; 217 | 218 | color: #2b81af; 219 | background-color: #D2E0E6; 220 | 221 | border-bottom: 1px solid white; 222 | } 223 | #qunit-testresult .module-name { 224 | font-weight: bold; 225 | } 226 | 227 | /** Fixture */ 228 | 229 | #qunit-fixture { 230 | position: absolute; 231 | top: -10000px; 232 | left: -10000px; 233 | width: 1000px; 234 | height: 1000px; 235 | } 236 | -------------------------------------------------------------------------------- /tests/suites/component.js: -------------------------------------------------------------------------------- 1 | module('Component', { 2 | setup: function(){ 3 | this.component = $('
    '+ 4 | ''+ 5 | ''+ 6 | '
    ') 7 | .appendTo('#qunit-fixture') 8 | .datepicker({format: "dd-mm-yyyy"}); 9 | this.input = this.component.find('input'); 10 | this.addon = this.component.find('.add-on'); 11 | this.dp = this.component.data('datepicker') 12 | this.picker = this.dp.picker; 13 | }, 14 | teardown: function(){ 15 | this.picker.remove(); 16 | } 17 | }); 18 | 19 | 20 | test('Component gets date/viewDate from input value', function(){ 21 | datesEqual(this.dp.date, UTCDate(2012, 1, 12)); 22 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 12)); 23 | }); 24 | 25 | test('Activation by component', function(){ 26 | ok(!this.picker.is(':visible')); 27 | this.addon.click(); 28 | ok(this.picker.is(':visible')); 29 | }); 30 | 31 | test('simple keyboard nav test', function(){ 32 | var target; 33 | 34 | // Keyboard nav only works with non-readonly inputs 35 | this.input.removeAttr('readonly'); 36 | 37 | equal(this.dp.viewMode, 0); 38 | target = this.picker.find('.datepicker-days thead th.switch'); 39 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 40 | datesEqual(this.dp.date, UTCDate(2012, 1, 12)); 41 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 12)); 42 | 43 | // Focus/open 44 | this.addon.click(); 45 | 46 | // Navigation: -1 day, left arrow key 47 | this.input.trigger({ 48 | type: 'keydown', 49 | keyCode: 37 50 | }); 51 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 11)); 52 | datesEqual(this.dp.date, UTCDate(2012, 1, 11)); 53 | // Month not changed 54 | target = this.picker.find('.datepicker-days thead th.switch'); 55 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 56 | 57 | // Navigation: +1 month, shift + right arrow key 58 | this.input.trigger({ 59 | type: 'keydown', 60 | keyCode: 39, 61 | shiftKey: true 62 | }); 63 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 11)); 64 | datesEqual(this.dp.date, UTCDate(2012, 2, 11)); 65 | target = this.picker.find('.datepicker-days thead th.switch'); 66 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 67 | 68 | // Navigation: -1 year, ctrl + left arrow key 69 | this.input.trigger({ 70 | type: 'keydown', 71 | keyCode: 37, 72 | ctrlKey: true 73 | }); 74 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 11)); 75 | datesEqual(this.dp.date, UTCDate(2011, 2, 11)); 76 | target = this.picker.find('.datepicker-days thead th.switch'); 77 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 78 | }); 79 | 80 | test('setValue', function(){ 81 | this.dp.date = UTCDate(2012, 2, 13) 82 | this.dp.setValue() 83 | datesEqual(this.dp.date, UTCDate(2012, 2, 13)); 84 | equal(this.input.val(), '13-03-2012'); 85 | }); 86 | 87 | test('update', function(){ 88 | this.input.val('13-03-2012'); 89 | this.dp.update() 90 | datesEqual(this.dp.date, UTCDate(2012, 2, 13)); 91 | }); 92 | 93 | test('Navigating to/from decade view', function(){ 94 | var target; 95 | 96 | this.addon.click(); 97 | this.input.val('31-03-2012'); 98 | this.dp.update(); 99 | 100 | equal(this.dp.viewMode, 0); 101 | target = this.picker.find('.datepicker-days thead th.switch'); 102 | ok(target.is(':visible'), 'View switcher is visible'); 103 | 104 | target.click(); 105 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 106 | equal(this.dp.viewMode, 1); 107 | // Not modified when switching modes 108 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 109 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 110 | 111 | target = this.picker.find('.datepicker-months thead th.switch'); 112 | ok(target.is(':visible'), 'View switcher is visible'); 113 | 114 | target.click(); 115 | ok(this.picker.find('.datepicker-years').is(':visible'), 'Year picker is visible'); 116 | equal(this.dp.viewMode, 2); 117 | // Not modified when switching modes 118 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 119 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 120 | 121 | // Change years to test internal state changes 122 | target = this.picker.find('.datepicker-years tbody span:contains(2011)'); 123 | target.click(); 124 | equal(this.dp.viewMode, 1); 125 | // Only viewDate modified 126 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 1)); 127 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 128 | 129 | target = this.picker.find('.datepicker-months tbody span:contains(Apr)'); 130 | target.click(); 131 | equal(this.dp.viewMode, 0); 132 | // Only viewDate modified 133 | datesEqual(this.dp.viewDate, UTCDate(2011, 3, 1)); 134 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 135 | }); 136 | 137 | test('Selecting date resets viewDate and date', function(){ 138 | var target; 139 | 140 | this.addon.click(); 141 | this.input.val('31-03-2012'); 142 | this.dp.update(); 143 | 144 | // Rendered correctly 145 | equal(this.dp.viewMode, 0); 146 | target = this.picker.find('.datepicker-days tbody td:first'); 147 | equal(target.text(), '26'); // Should be Feb 26 148 | 149 | // Updated internally on click 150 | target.click(); 151 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 26)) 152 | datesEqual(this.dp.date, UTCDate(2012, 1, 26)) 153 | 154 | // Re-rendered on click 155 | target = this.picker.find('.datepicker-days tbody td:first'); 156 | equal(target.text(), '29'); // Should be Jan 29 157 | }); -------------------------------------------------------------------------------- /tests/run-qunit.js: -------------------------------------------------------------------------------- 1 | var system = require('system'); 2 | 3 | /** 4 | * Wait until the test condition is true or a timeout occurs. Useful for waiting 5 | * on a server response or for a ui change (fadeIn, etc.) to occur. 6 | * 7 | * @param testFx javascript condition that evaluates to a boolean, 8 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 9 | * as a callback function. 10 | * @param onReady what to do when testFx condition is fulfilled, 11 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 12 | * as a callback function. 13 | * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. 14 | */ 15 | function waitFor(testFx, onReady, timeOutMillis) { 16 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10001, //< Default Max Timout is 3s 17 | start = new Date().getTime(), 18 | condition = false, 19 | interval = setInterval(function() { 20 | if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { 21 | // If not time-out yet and condition not yet fulfilled 22 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code 23 | } else { 24 | if(!condition) { 25 | // If condition still not fulfilled (timeout but condition is 'false') 26 | console.log("'waitFor()' timeout"); 27 | phantom.exit(1); 28 | } else { 29 | // Condition fulfilled (timeout and/or condition is 'true') 30 | //console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); 31 | typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled 32 | clearInterval(interval); //< Stop this interval 33 | } 34 | } 35 | }, 100); //< repeat check every 100ms 36 | }; 37 | 38 | if (system.args.length !== 2) { 39 | console.log('Usage: run-qunit.js URL'); 40 | phantom.exit(1); 41 | } 42 | 43 | var fs = require('fs'); 44 | var page = require('webpage').create(); 45 | 46 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 47 | page.onConsoleMessage = function(msg) { 48 | console.log(msg); 49 | }; 50 | page.onError = function (msg, trace) { 51 | console.log(msg); 52 | trace.forEach(function(item) { 53 | console.log(' ', item.file, ':', item.line); 54 | }) 55 | } 56 | 57 | var _openPath = phantom.args[0].replace(/^.*(\\|\/)/, ''); 58 | var openPath = _openPath; 59 | var origdir = '../js/'; 60 | var basedir = '../instrumented/'; 61 | var coverageBase = fs.read('_coverage.html'); 62 | 63 | if (fs.exists(basedir)){ 64 | var script = /<\/script>/g, 65 | src = /src=(["'])(.*?)\1/, 66 | contents = fs.read(openPath), 67 | _contents = contents, 68 | srcs = [], 69 | s; 70 | while (script.exec(contents)){ 71 | s = src.exec(RegExp.lastMatch)[2]; 72 | if (s && s.indexOf(origdir) != -1) 73 | _contents = _contents.replace(s, s.replace(origdir, basedir)) 74 | } 75 | if (_contents != contents){ 76 | openPath += '.cov.html'; 77 | fs.write(openPath, _contents); 78 | } 79 | } 80 | 81 | page.open(openPath, function(status){ 82 | if (status !== "success") { 83 | console.log("Unable to access network"); 84 | phantom.exit(1); 85 | } else { 86 | // Inject instrumented sources if they exist 87 | if (fs.exists(basedir)) 88 | for (var i=0; i0 ? 'hit' : 'miss'); 116 | } else { 117 | hitmiss = ' ' + 'undef'; 118 | } 119 | var htmlLine = fileLines[idx] 120 | if (!source) 121 | htmlLine = htmlLine.replace('<', '<').replace('>', '>'); 122 | colorized += '
    ' + htmlLine + '
    \n'; 123 | }; 124 | colorized = coverageBase.replace('COLORIZED_LINE_HTML', colorized); 125 | 126 | fs.write('coverage.html', colorized, 'w'); 127 | 128 | console.log('Coverage for ' + coverageInfo.key + ' in coverage.html'); 129 | } 130 | if (_openPath != openPath) 131 | fs.remove(openPath); 132 | 133 | var failedNum = page.evaluate(function(){ 134 | var el = document.getElementById('qunit-testresult'); 135 | console.log(el.innerText); 136 | try { 137 | return el.getElementsByClassName('failed')[0].innerHTML; 138 | } catch (e) { } 139 | return 10000; 140 | }); 141 | phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0); 142 | }); 143 | } 144 | }); 145 | 146 | function readFileLines(filename) { 147 | var stream = fs.open(filename, 'r'); 148 | var lines = []; 149 | var line; 150 | while (!stream.atEnd()) { 151 | lines.push(stream.readLine()); 152 | } 153 | stream.close(); 154 | 155 | return lines; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /tests/suites/formats.js: -------------------------------------------------------------------------------- 1 | module('Formats', { 2 | setup: function(){ 3 | this.input = $('').appendTo('#qunit-fixture'); 4 | this.date = UTCDate(2012, 2, 15, 0, 0, 0, 0); // March 15, 2012 5 | }, 6 | teardown: function(){ 7 | this.input.data('datepicker').picker.remove(); 8 | } 9 | }); 10 | 11 | test('d: Day of month, no leading zero.', function(){ 12 | this.input 13 | .val('2012-03-05') 14 | .datepicker({format: 'yyyy-mm-d'}) 15 | .datepicker('setValue'); 16 | equal(this.input.val().split('-')[2], '5'); 17 | }); 18 | 19 | test('dd: Day of month, leading zero.', function(){ 20 | this.input 21 | .val('2012-03-5') 22 | .datepicker({format: 'yyyy-mm-dd'}) 23 | .datepicker('setValue'); 24 | equal(this.input.val().split('-')[2], '05'); 25 | }); 26 | 27 | test('D: Day of week, short.', function(){ 28 | this.input 29 | .val('2012-03-05') 30 | .datepicker({format: 'yyyy-mm-dd-D'}) 31 | .datepicker('setValue'); 32 | equal(this.input.val().split('-')[3], 'Mon'); 33 | }); 34 | 35 | test('DD: Day of week, long.', function(){ 36 | this.input 37 | .val('2012-03-05') 38 | .datepicker({format: 'yyyy-mm-dd-DD'}) 39 | .datepicker('setValue'); 40 | equal(this.input.val().split('-')[3], 'Monday'); 41 | }); 42 | 43 | test('m: Month, no leading zero.', function(){ 44 | this.input 45 | .val('2012-03-05') 46 | .datepicker({format: 'yyyy-m-dd'}) 47 | .datepicker('setValue'); 48 | equal(this.input.val().split('-')[1], '3'); 49 | }); 50 | 51 | test('mm: Month, leading zero.', function(){ 52 | this.input 53 | .val('2012-3-5') 54 | .datepicker({format: 'yyyy-mm-dd'}) 55 | .datepicker('setValue'); 56 | equal(this.input.val().split('-')[1], '03'); 57 | }); 58 | 59 | test('M: Month shortname.', function(){ 60 | this.input 61 | .val('2012-Mar-05') 62 | .datepicker({format: 'yyyy-M-dd'}) 63 | .datepicker('setValue'); 64 | equal(this.input.val().split('-')[1], 'Mar'); 65 | }); 66 | 67 | test('MM: Month full name.', function(){ 68 | this.input 69 | .val('2012-March-5') 70 | .datepicker({format: 'yyyy-MM-dd'}) 71 | .datepicker('setValue'); 72 | equal(this.input.val().split('-')[1], 'March'); 73 | }); 74 | 75 | test('yy: Year, two-digit.', function(){ 76 | this.input 77 | .val('2012-03-05') 78 | .datepicker({format: 'yy-mm-dd'}) 79 | .datepicker('setValue'); 80 | equal(this.input.val().split('-')[0], '12'); 81 | }); 82 | 83 | test('yyyy: Year, four-digit.', function(){ 84 | this.input 85 | .val('2012-03-5') 86 | .datepicker({format: 'yyyy-mm-dd'}) 87 | .datepicker('setValue'); 88 | equal(this.input.val().split('-')[0], '2012'); 89 | }); 90 | 91 | test('dd-mm-yyyy: Regression: Prevent potential month overflow in small-to-large formats (Mar 31, 2012 -> Mar 01, 2012)', function(){ 92 | this.input 93 | .val('31-03-2012') 94 | .datepicker({format: 'dd-mm-yyyy'}) 95 | .datepicker('setValue'); 96 | equal(this.input.val(), '31-03-2012'); 97 | }); 98 | 99 | test('dd-mm-yyyy: Leap day', function(){ 100 | this.input 101 | .val('29-02-2012') 102 | .datepicker({format: 'dd-mm-yyyy'}) 103 | .datepicker('setValue'); 104 | equal(this.input.val(), '29-02-2012'); 105 | }); 106 | 107 | test('yyyy-mm-dd: Alternative format', function(){ 108 | this.input 109 | .val('2012-02-12') 110 | .datepicker({format: 'yyyy-mm-dd'}) 111 | .datepicker('setValue'); 112 | equal(this.input.val(), '2012-02-12'); 113 | }); 114 | 115 | test('yyyy-MM-dd: Regression: Infinite loop when numbers used for month', function(){ 116 | this.input 117 | .val('2012-02-12') 118 | .datepicker({format: 'yyyy-MM-dd'}) 119 | .datepicker('setValue'); 120 | equal(this.input.val(), '2012-February-12'); 121 | }); 122 | 123 | test('+1d: Tomorrow', patch_date(function(Date){ 124 | Date.now = UTCDate(2012, 2, 15); 125 | this.input 126 | .val('+1d') 127 | .datepicker({format: 'dd-mm-yyyy'}) 128 | .datepicker('setValue'); 129 | equal(this.input.val(), '16-03-2012'); 130 | })); 131 | 132 | test('-1d: Yesterday', patch_date(function(Date){ 133 | Date.now = UTCDate(2012, 2, 15); 134 | this.input 135 | .val('-1d') 136 | .datepicker({format: 'dd-mm-yyyy'}) 137 | .datepicker('setValue'); 138 | equal(this.input.val(), '14-03-2012'); 139 | })); 140 | 141 | test('+1w: Next week', patch_date(function(Date){ 142 | Date.now = UTCDate(2012, 2, 15); 143 | this.input 144 | .val('+1w') 145 | .datepicker({format: 'dd-mm-yyyy'}) 146 | .datepicker('setValue'); 147 | equal(this.input.val(), '22-03-2012'); 148 | })); 149 | 150 | test('-1w: Last week', patch_date(function(Date){ 151 | Date.now = UTCDate(2012, 2, 15); 152 | this.input 153 | .val('-1w') 154 | .datepicker({format: 'dd-mm-yyyy'}) 155 | .datepicker('setValue'); 156 | equal(this.input.val(), '08-03-2012'); 157 | })); 158 | 159 | test('+1m: Next month', patch_date(function(Date){ 160 | Date.now = UTCDate(2012, 2, 15); 161 | this.input 162 | .val('+1m') 163 | .datepicker({format: 'dd-mm-yyyy'}) 164 | .datepicker('setValue'); 165 | equal(this.input.val(), '15-04-2012'); 166 | })); 167 | 168 | test('-1m: Last month', patch_date(function(Date){ 169 | Date.now = UTCDate(2012, 2, 15); 170 | this.input 171 | .val('-1m') 172 | .datepicker({format: 'dd-mm-yyyy'}) 173 | .datepicker('setValue'); 174 | equal(this.input.val(), '15-02-2012'); 175 | })); 176 | 177 | test('+1y: Next year', patch_date(function(Date){ 178 | Date.now = UTCDate(2012, 2, 15); 179 | this.input 180 | .val('+1y') 181 | .datepicker({format: 'dd-mm-yyyy'}) 182 | .datepicker('setValue'); 183 | equal(this.input.val(), '15-03-2013'); 184 | })); 185 | 186 | test('-1y: Last year', patch_date(function(Date){ 187 | Date.now = UTCDate(2012, 2, 15); 188 | this.input 189 | .val('-1y') 190 | .datepicker({format: 'dd-mm-yyyy'}) 191 | .datepicker('setValue'); 192 | equal(this.input.val(), '15-03-2011'); 193 | })); 194 | 195 | test('-1y +2m: Multiformat', patch_date(function(Date){ 196 | Date.now = UTCDate(2012, 2, 15); 197 | this.input 198 | .val('-1y +2m') 199 | .datepicker({format: 'dd-mm-yyyy'}) 200 | .datepicker('setValue'); 201 | equal(this.input.val(), '15-05-2011'); 202 | })); 203 | 204 | test('Regression: End-of-month bug', patch_date(function(Date){ 205 | Date.now = UTCDate(2012, 4, 31); 206 | this.input 207 | .val('29-02-2012') 208 | .datepicker({format: 'dd-mm-yyyy'}) 209 | .datepicker('setValue'); 210 | equal(this.input.val(), '29-02-2012'); 211 | })); 212 | 213 | test('Invalid formats are force-parsed into a valid date on tab', patch_date(function(Date){ 214 | Date.now = UTCDate(2012, 4, 31); 215 | this.input 216 | .val('44-44-4444') 217 | .datepicker({format: 'yyyy-MM-dd'}) 218 | .focus(); 219 | 220 | this.input.trigger({ 221 | type: 'keydown', 222 | keyCode: 9 223 | }); 224 | 225 | equal(this.input.val(), '56-September-30'); 226 | })); 227 | -------------------------------------------------------------------------------- /tests/suites/mouse_navigation/2012.js: -------------------------------------------------------------------------------- 1 | module('Mouse Navigation 2012', { 2 | setup: function(){ 3 | /* 4 | Tests start with picker on March 31, 2012. Fun facts: 5 | 6 | * February 1, 2012 was on a Wednesday 7 | * February 29, 2012 was on a Wednesday 8 | * March 1, 2012 was on a Thursday 9 | * March 31, 2012 was on a Saturday 10 | */ 11 | this.input = $('') 12 | .appendTo('#qunit-fixture') 13 | .datepicker({format: "dd-mm-yyyy"}) 14 | .focus(); // Activate for visibility checks 15 | this.dp = this.input.data('datepicker') 16 | this.picker = this.dp.picker; 17 | }, 18 | teardown: function(){ 19 | this.picker.remove(); 20 | } 21 | }); 22 | 23 | test('Selecting date resets viewDate and date', function(){ 24 | var target; 25 | 26 | // Rendered correctly 27 | equal(this.dp.viewMode, 0); 28 | target = this.picker.find('.datepicker-days tbody td:nth(7)'); 29 | equal(target.text(), '4'); // Should be Mar 4 30 | 31 | // Updated internally on click 32 | target.click(); 33 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 4)) 34 | datesEqual(this.dp.date, UTCDate(2012, 2, 4)) 35 | 36 | // Re-rendered on click 37 | target = this.picker.find('.datepicker-days tbody td:first'); 38 | equal(target.text(), '26'); // Should be Feb 29 39 | }); 40 | 41 | test('Navigating next/prev by month', function(){ 42 | var target; 43 | 44 | equal(this.dp.viewMode, 0); 45 | target = this.picker.find('.datepicker-days thead th.prev'); 46 | ok(target.is(':visible'), 'Month:prev nav is visible'); 47 | 48 | // Updated internally on click 49 | target.click(); 50 | // Should handle month-length changes gracefully 51 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 29)); 52 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 53 | 54 | // Re-rendered on click 55 | target = this.picker.find('.datepicker-days tbody td:first'); 56 | equal(target.text(), '29'); // Should be Jan 29 57 | 58 | target = this.picker.find('.datepicker-days thead th.next'); 59 | ok(target.is(':visible'), 'Month:next nav is visible'); 60 | 61 | // Updated internally on click 62 | target.click().click(); 63 | // Graceful moonth-end handling carries over 64 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 29)); 65 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 66 | 67 | // Re-rendered on click 68 | target = this.picker.find('.datepicker-days tbody td:first'); 69 | equal(target.text(), '25'); // Should be Mar 25 70 | // (includes "old" days at start of month, even if that's all the first week-row consists of) 71 | }); 72 | 73 | test('Navigating to/from year view', function(){ 74 | var target; 75 | 76 | equal(this.dp.viewMode, 0); 77 | target = this.picker.find('.datepicker-days thead th.switch'); 78 | ok(target.is(':visible'), 'View switcher is visible'); 79 | 80 | target.click(); 81 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 82 | equal(this.dp.viewMode, 1); 83 | // Not modified when switching modes 84 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 85 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 86 | 87 | // Change months to test internal state 88 | target = this.picker.find('.datepicker-months tbody span:contains(Apr)'); 89 | target.click(); 90 | equal(this.dp.viewMode, 0); 91 | // Only viewDate modified 92 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 1)); // Apr 30 93 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 94 | }); 95 | 96 | test('Navigating to/from decade view', function(){ 97 | var target; 98 | 99 | equal(this.dp.viewMode, 0); 100 | target = this.picker.find('.datepicker-days thead th.switch'); 101 | ok(target.is(':visible'), 'View switcher is visible'); 102 | 103 | target.click(); 104 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 105 | equal(this.dp.viewMode, 1); 106 | // Not modified when switching modes 107 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 108 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 109 | 110 | target = this.picker.find('.datepicker-months thead th.switch'); 111 | ok(target.is(':visible'), 'View switcher is visible'); 112 | 113 | target.click(); 114 | ok(this.picker.find('.datepicker-years').is(':visible'), 'Year picker is visible'); 115 | equal(this.dp.viewMode, 2); 116 | // Not modified when switching modes 117 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 118 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 119 | 120 | // Change years to test internal state changes 121 | target = this.picker.find('.datepicker-years tbody span:contains(2011)'); 122 | target.click(); 123 | equal(this.dp.viewMode, 1); 124 | // Only viewDate modified 125 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 1)); 126 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 127 | 128 | target = this.picker.find('.datepicker-months tbody span:contains(Apr)'); 129 | target.click(); 130 | equal(this.dp.viewMode, 0); 131 | // Only viewDate modified 132 | datesEqual(this.dp.viewDate, UTCDate(2011, 3, 1)); 133 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 134 | }); 135 | 136 | test('Navigating prev/next in year view', function(){ 137 | var target; 138 | 139 | equal(this.dp.viewMode, 0); 140 | target = this.picker.find('.datepicker-days thead th.switch'); 141 | ok(target.is(':visible'), 'View switcher is visible'); 142 | 143 | target.click(); 144 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 145 | equal(this.dp.viewMode, 1); 146 | equal(this.picker.find('.datepicker-months thead th.switch').text(), '2012'); 147 | // Not modified when switching modes 148 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 149 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 150 | 151 | // Go to next year (2013) 152 | target = this.picker.find('.datepicker-months thead th.next'); 153 | target.click(); 154 | equal(this.picker.find('.datepicker-months thead th.switch').text(), '2013'); 155 | // Only viewDate modified 156 | datesEqual(this.dp.viewDate, UTCDate(2013, 2, 31)); 157 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 158 | 159 | // Go to prev year (x2 == 2011) 160 | target = this.picker.find('.datepicker-months thead th.prev'); 161 | target.click().click(); 162 | equal(this.picker.find('.datepicker-months thead th.switch').text(), '2011'); 163 | // Only viewDate modified 164 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 165 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 166 | }); 167 | 168 | test('Navigating prev/next in decade view', function(){ 169 | var target; 170 | 171 | equal(this.dp.viewMode, 0); 172 | target = this.picker.find('.datepicker-days thead th.switch'); 173 | ok(target.is(':visible'), 'View switcher is visible'); 174 | 175 | target.click(); 176 | ok(this.picker.find('.datepicker-months').is(':visible'), 'Month picker is visible'); 177 | equal(this.dp.viewMode, 1); 178 | // Not modified when switching modes 179 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 180 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 181 | 182 | target = this.picker.find('.datepicker-months thead th.switch'); 183 | ok(target.is(':visible'), 'View switcher is visible'); 184 | 185 | target.click(); 186 | ok(this.picker.find('.datepicker-years').is(':visible'), 'Year picker is visible'); 187 | equal(this.dp.viewMode, 2); 188 | equal(this.picker.find('.datepicker-years thead th.switch').text(), '2010-2019'); 189 | // Not modified when switching modes 190 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 191 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 192 | 193 | // Go to next decade (2020-29) 194 | target = this.picker.find('.datepicker-years thead th.next'); 195 | target.click(); 196 | equal(this.picker.find('.datepicker-years thead th.switch').text(), '2020-2029'); 197 | // Only viewDate modified 198 | datesEqual(this.dp.viewDate, UTCDate(2022, 2, 31)); 199 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 200 | 201 | // Go to prev year (x2 == 2000-09) 202 | target = this.picker.find('.datepicker-years thead th.prev'); 203 | target.click().click(); 204 | equal(this.picker.find('.datepicker-years thead th.switch').text(), '2000-2009'); 205 | // Only viewDate modified 206 | datesEqual(this.dp.viewDate, UTCDate(2002, 2, 31)); 207 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 208 | }); 209 | 210 | test('Selecting date from previous month resets viewDate and date, changing month displayed', function(){ 211 | var target; 212 | 213 | // Rendered correctly 214 | equal(this.dp.viewMode, 0); 215 | target = this.picker.find('.datepicker-days tbody td:first'); 216 | equal(target.text(), '26'); // Should be Feb 26 217 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'March 2012'); 218 | 219 | // Updated internally on click 220 | target.click(); 221 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'February 2012'); 222 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 26)) 223 | datesEqual(this.dp.date, UTCDate(2012, 1, 26)) 224 | 225 | // Re-rendered on click 226 | target = this.picker.find('.datepicker-days tbody td:first'); 227 | equal(target.text(), '29'); // Should be Jan 29 228 | }); 229 | 230 | test('Selecting date from next month resets viewDate and date, changing month displayed', function(){ 231 | var target; 232 | 233 | this.input.val('01-04-2012'); 234 | this.dp.update(); 235 | 236 | // Rendered correctly 237 | equal(this.dp.viewMode, 0); 238 | target = this.picker.find('.datepicker-days tbody td:last'); 239 | equal(target.text(), '5'); // Should be May 5 240 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'April 2012'); 241 | 242 | // Updated internally on click 243 | target.click(); 244 | equal(this.picker.find('.datepicker-days thead th.switch').text(), 'May 2012'); 245 | datesEqual(this.dp.viewDate, UTCDate(2012, 4, 5)) 246 | datesEqual(this.dp.date, UTCDate(2012, 4, 5)) 247 | 248 | // Re-rendered on click 249 | target = this.picker.find('.datepicker-days tbody td:first'); 250 | equal(target.text(), '29'); // Should be Apr 29 251 | }); 252 | -------------------------------------------------------------------------------- /css/datepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Datepicker for Bootstrap 3 | * 4 | * Copyright 2012 Stefan Petre 5 | * Improvements by Andrew Rowls 6 | * Licensed under the Apache License v2.0 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | */ 10 | .datepicker { 11 | padding: 4px; 12 | margin-top: 1px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | border-radius: 4px; 16 | direction: ltr; 17 | /*.dow { 18 | border-top: 1px solid #ddd !important; 19 | }*/ 20 | 21 | } 22 | .datepicker-inline { 23 | width: 220px; 24 | } 25 | .datepicker.datepicker-rtl { 26 | direction: rtl; 27 | } 28 | .datepicker.datepicker-rtl table tr td span { 29 | float: right; 30 | } 31 | .datepicker-dropdown { 32 | top: 0; 33 | left: 0; 34 | } 35 | .datepicker-dropdown:before { 36 | content: ''; 37 | display: inline-block; 38 | border-left: 7px solid transparent; 39 | border-right: 7px solid transparent; 40 | border-bottom: 7px solid #ccc; 41 | border-bottom-color: rgba(0, 0, 0, 0.2); 42 | position: absolute; 43 | top: -7px; 44 | left: 6px; 45 | } 46 | .datepicker-dropdown:after { 47 | content: ''; 48 | display: inline-block; 49 | border-left: 6px solid transparent; 50 | border-right: 6px solid transparent; 51 | border-bottom: 6px solid #ffffff; 52 | position: absolute; 53 | top: -6px; 54 | left: 7px; 55 | } 56 | .datepicker > div { 57 | display: none; 58 | } 59 | .datepicker.days div.datepicker-days { 60 | display: block; 61 | } 62 | .datepicker.months div.datepicker-months { 63 | display: block; 64 | } 65 | .datepicker.years div.datepicker-years { 66 | display: block; 67 | } 68 | .datepicker table { 69 | margin: 0; 70 | } 71 | .datepicker td, 72 | .datepicker th { 73 | text-align: center; 74 | width: 20px; 75 | height: 20px; 76 | -webkit-border-radius: 4px; 77 | -moz-border-radius: 4px; 78 | border-radius: 4px; 79 | border: none; 80 | } 81 | .table-striped .datepicker table tr td, 82 | .table-striped .datepicker table tr th { 83 | background-color: transparent; 84 | } 85 | .datepicker table tr td.day:hover { 86 | background: #eeeeee; 87 | cursor: pointer; 88 | } 89 | .datepicker table tr td.old, 90 | .datepicker table tr td.new { 91 | color: #999999; 92 | } 93 | .datepicker table tr td.disabled, 94 | .datepicker table tr td.disabled:hover { 95 | background: none; 96 | color: #999999; 97 | cursor: default; 98 | } 99 | .datepicker table tr td.today, 100 | .datepicker table tr td.today:hover, 101 | .datepicker table tr td.today.disabled, 102 | .datepicker table tr td.today.disabled:hover { 103 | background-color: #fde19a; 104 | background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); 105 | background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); 106 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); 107 | background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); 108 | background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); 109 | background-image: linear-gradient(top, #fdd49a, #fdf59a); 110 | background-repeat: repeat-x; 111 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); 112 | border-color: #fdf59a #fdf59a #fbed50; 113 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 114 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 115 | } 116 | .datepicker table tr td.today:hover, 117 | .datepicker table tr td.today:hover:hover, 118 | .datepicker table tr td.today.disabled:hover, 119 | .datepicker table tr td.today.disabled:hover:hover, 120 | .datepicker table tr td.today:active, 121 | .datepicker table tr td.today:hover:active, 122 | .datepicker table tr td.today.disabled:active, 123 | .datepicker table tr td.today.disabled:hover:active, 124 | .datepicker table tr td.today.active, 125 | .datepicker table tr td.today:hover.active, 126 | .datepicker table tr td.today.disabled.active, 127 | .datepicker table tr td.today.disabled:hover.active, 128 | .datepicker table tr td.today.disabled, 129 | .datepicker table tr td.today:hover.disabled, 130 | .datepicker table tr td.today.disabled.disabled, 131 | .datepicker table tr td.today.disabled:hover.disabled, 132 | .datepicker table tr td.today[disabled], 133 | .datepicker table tr td.today:hover[disabled], 134 | .datepicker table tr td.today.disabled[disabled], 135 | .datepicker table tr td.today.disabled:hover[disabled] { 136 | background-color: #fdf59a; 137 | } 138 | .datepicker table tr td.today:active, 139 | .datepicker table tr td.today:hover:active, 140 | .datepicker table tr td.today.disabled:active, 141 | .datepicker table tr td.today.disabled:hover:active, 142 | .datepicker table tr td.today.active, 143 | .datepicker table tr td.today:hover.active, 144 | .datepicker table tr td.today.disabled.active, 145 | .datepicker table tr td.today.disabled:hover.active { 146 | background-color: #fbf069 \9; 147 | } 148 | .datepicker table tr td.active, 149 | .datepicker table tr td.active:hover, 150 | .datepicker table tr td.active.disabled, 151 | .datepicker table tr td.active.disabled:hover { 152 | background-color: #006dcc; 153 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 154 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 155 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 156 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 157 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 158 | background-image: linear-gradient(top, #0088cc, #0044cc); 159 | background-repeat: repeat-x; 160 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 161 | border-color: #0044cc #0044cc #002a80; 162 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 163 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 164 | color: #fff; 165 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 166 | } 167 | .datepicker table tr td.active:hover, 168 | .datepicker table tr td.active:hover:hover, 169 | .datepicker table tr td.active.disabled:hover, 170 | .datepicker table tr td.active.disabled:hover:hover, 171 | .datepicker table tr td.active:active, 172 | .datepicker table tr td.active:hover:active, 173 | .datepicker table tr td.active.disabled:active, 174 | .datepicker table tr td.active.disabled:hover:active, 175 | .datepicker table tr td.active.active, 176 | .datepicker table tr td.active:hover.active, 177 | .datepicker table tr td.active.disabled.active, 178 | .datepicker table tr td.active.disabled:hover.active, 179 | .datepicker table tr td.active.disabled, 180 | .datepicker table tr td.active:hover.disabled, 181 | .datepicker table tr td.active.disabled.disabled, 182 | .datepicker table tr td.active.disabled:hover.disabled, 183 | .datepicker table tr td.active[disabled], 184 | .datepicker table tr td.active:hover[disabled], 185 | .datepicker table tr td.active.disabled[disabled], 186 | .datepicker table tr td.active.disabled:hover[disabled] { 187 | background-color: #0044cc; 188 | } 189 | .datepicker table tr td.active:active, 190 | .datepicker table tr td.active:hover:active, 191 | .datepicker table tr td.active.disabled:active, 192 | .datepicker table tr td.active.disabled:hover:active, 193 | .datepicker table tr td.active.active, 194 | .datepicker table tr td.active:hover.active, 195 | .datepicker table tr td.active.disabled.active, 196 | .datepicker table tr td.active.disabled:hover.active { 197 | background-color: #003399 \9; 198 | } 199 | .datepicker table tr td span { 200 | display: block; 201 | width: 23%; 202 | height: 54px; 203 | line-height: 54px; 204 | float: left; 205 | margin: 1%; 206 | cursor: pointer; 207 | -webkit-border-radius: 4px; 208 | -moz-border-radius: 4px; 209 | border-radius: 4px; 210 | } 211 | .datepicker table tr td span:hover { 212 | background: #eeeeee; 213 | } 214 | .datepicker table tr td span.disabled, 215 | .datepicker table tr td span.disabled:hover { 216 | background: none; 217 | color: #999999; 218 | cursor: default; 219 | } 220 | .datepicker table tr td span.active, 221 | .datepicker table tr td span.active:hover, 222 | .datepicker table tr td span.active.disabled, 223 | .datepicker table tr td span.active.disabled:hover { 224 | background-color: #006dcc; 225 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 226 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 227 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 228 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 229 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 230 | background-image: linear-gradient(top, #0088cc, #0044cc); 231 | background-repeat: repeat-x; 232 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 233 | border-color: #0044cc #0044cc #002a80; 234 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 235 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 236 | color: #fff; 237 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 238 | } 239 | .datepicker table tr td span.active:hover, 240 | .datepicker table tr td span.active:hover:hover, 241 | .datepicker table tr td span.active.disabled:hover, 242 | .datepicker table tr td span.active.disabled:hover:hover, 243 | .datepicker table tr td span.active:active, 244 | .datepicker table tr td span.active:hover:active, 245 | .datepicker table tr td span.active.disabled:active, 246 | .datepicker table tr td span.active.disabled:hover:active, 247 | .datepicker table tr td span.active.active, 248 | .datepicker table tr td span.active:hover.active, 249 | .datepicker table tr td span.active.disabled.active, 250 | .datepicker table tr td span.active.disabled:hover.active, 251 | .datepicker table tr td span.active.disabled, 252 | .datepicker table tr td span.active:hover.disabled, 253 | .datepicker table tr td span.active.disabled.disabled, 254 | .datepicker table tr td span.active.disabled:hover.disabled, 255 | .datepicker table tr td span.active[disabled], 256 | .datepicker table tr td span.active:hover[disabled], 257 | .datepicker table tr td span.active.disabled[disabled], 258 | .datepicker table tr td span.active.disabled:hover[disabled] { 259 | background-color: #0044cc; 260 | } 261 | .datepicker table tr td span.active:active, 262 | .datepicker table tr td span.active:hover:active, 263 | .datepicker table tr td span.active.disabled:active, 264 | .datepicker table tr td span.active.disabled:hover:active, 265 | .datepicker table tr td span.active.active, 266 | .datepicker table tr td span.active:hover.active, 267 | .datepicker table tr td span.active.disabled.active, 268 | .datepicker table tr td span.active.disabled:hover.active { 269 | background-color: #003399 \9; 270 | } 271 | .datepicker table tr td span.old { 272 | color: #999999; 273 | } 274 | .datepicker th.switch { 275 | width: 145px; 276 | } 277 | .datepicker thead tr:first-child th, 278 | .datepicker tfoot tr:first-child th { 279 | cursor: pointer; 280 | } 281 | .datepicker thead tr:first-child th:hover, 282 | .datepicker tfoot tr:first-child th:hover { 283 | background: #eeeeee; 284 | } 285 | .datepicker .cw { 286 | font-size: 10px; 287 | width: 12px; 288 | padding: 0 2px 0 5px; 289 | vertical-align: middle; 290 | } 291 | .datepicker thead tr:first-child th.cw { 292 | cursor: default; 293 | background-color: transparent; 294 | } 295 | .input-append.date .add-on i, 296 | .input-prepend.date .add-on i { 297 | display: block; 298 | cursor: pointer; 299 | width: 16px; 300 | height: 16px; 301 | } 302 | -------------------------------------------------------------------------------- /tests/suites/options.js: -------------------------------------------------------------------------------- 1 | module('Options', { 2 | setup: function(){}, 3 | teardown: function(){ 4 | return 5 | $('#qunit-fixture *').each(function(){ 6 | var t = $(this); 7 | if ('datepicker' in t.data()) 8 | t.data('datepicker').picker.remove(); 9 | }); 10 | } 11 | }); 12 | 13 | test('Autoclose', function(){ 14 | var input = $('') 15 | .appendTo('#qunit-fixture') 16 | .val('2012-03-05') 17 | .datepicker({ 18 | format: 'yyyy-mm-dd', 19 | autoclose: true 20 | }), 21 | dp = input.data('datepicker'), 22 | picker = dp.picker, 23 | target; 24 | 25 | 26 | input.focus(); 27 | ok(picker.is(':visible'), 'Picker is visible'); 28 | target = picker.find('.datepicker-days tbody td:nth(7)'); 29 | equal(target.text(), '4'); // Mar 4 30 | 31 | target.click(); 32 | ok(picker.is(':not(:visible)'), 'Picker is hidden'); 33 | datesEqual(dp.date, UTCDate(2012, 2, 4)); 34 | datesEqual(dp.viewDate, UTCDate(2012, 2, 4)); 35 | }); 36 | 37 | test('Startview: year view (integer)', function(){ 38 | var input = $('') 39 | .appendTo('#qunit-fixture') 40 | .val('2012-03-05') 41 | .datepicker({ 42 | format: 'yyyy-mm-dd', 43 | startView: 1 44 | }), 45 | dp = input.data('datepicker'), 46 | picker = dp.picker, 47 | target; 48 | 49 | input.focus(); 50 | ok(picker.find('.datepicker-days').is(':not(:visible)'), 'Days view hidden'); 51 | ok(picker.find('.datepicker-months').is(':visible'), 'Months view visible'); 52 | ok(picker.find('.datepicker-years').is(':not(:visible)'), 'Years view hidden'); 53 | }); 54 | 55 | test('Startview: year view (string)', function(){ 56 | var input = $('') 57 | .appendTo('#qunit-fixture') 58 | .val('2012-03-05') 59 | .datepicker({ 60 | format: 'yyyy-mm-dd', 61 | startView: 'year' 62 | }), 63 | dp = input.data('datepicker'), 64 | picker = dp.picker, 65 | target; 66 | 67 | input.focus(); 68 | ok(picker.find('.datepicker-days').is(':not(:visible)'), 'Days view hidden'); 69 | ok(picker.find('.datepicker-months').is(':visible'), 'Months view visible'); 70 | ok(picker.find('.datepicker-years').is(':not(:visible)'), 'Years view hidden'); 71 | }); 72 | 73 | test('Startview: decade view (integer)', function(){ 74 | var input = $('') 75 | .appendTo('#qunit-fixture') 76 | .val('2012-03-05') 77 | .datepicker({ 78 | format: 'yyyy-mm-dd', 79 | startView: 2 80 | }), 81 | dp = input.data('datepicker'), 82 | picker = dp.picker, 83 | target; 84 | 85 | input.focus(); 86 | ok(picker.find('.datepicker-days').is(':not(:visible)'), 'Days view hidden'); 87 | ok(picker.find('.datepicker-months').is(':not(:visible)'), 'Months view hidden'); 88 | ok(picker.find('.datepicker-years').is(':visible'), 'Years view visible'); 89 | }); 90 | 91 | test('Startview: decade view (string)', function(){ 92 | var input = $('') 93 | .appendTo('#qunit-fixture') 94 | .val('2012-03-05') 95 | .datepicker({ 96 | format: 'yyyy-mm-dd', 97 | startView: 'decade' 98 | }), 99 | dp = input.data('datepicker'), 100 | picker = dp.picker, 101 | target; 102 | 103 | input.focus(); 104 | ok(picker.find('.datepicker-days').is(':not(:visible)'), 'Days view hidden'); 105 | ok(picker.find('.datepicker-months').is(':not(:visible)'), 'Months view hidden'); 106 | ok(picker.find('.datepicker-years').is(':visible'), 'Years view visible'); 107 | }); 108 | 109 | test('Today Button: today button not default', function(){ 110 | var input = $('') 111 | .appendTo('#qunit-fixture') 112 | .val('2012-03-05') 113 | .datepicker({ 114 | format: 'yyyy-mm-dd' 115 | }), 116 | dp = input.data('datepicker'), 117 | picker = dp.picker, 118 | target; 119 | 120 | input.focus(); 121 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 122 | ok(picker.find('.datepicker-days tfoot .today').is(':not(:visible)'), 'Today button not visible'); 123 | }); 124 | 125 | test('Today Button: today visibility when enabled', function(){ 126 | var input = $('') 127 | .appendTo('#qunit-fixture') 128 | .val('2012-03-05') 129 | .datepicker({ 130 | format: 'yyyy-mm-dd', 131 | todayBtn: true 132 | }), 133 | dp = input.data('datepicker'), 134 | picker = dp.picker, 135 | target; 136 | 137 | input.focus(); 138 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 139 | ok(picker.find('.datepicker-days tfoot .today').is(':visible'), 'Today button visible'); 140 | 141 | picker.find('.datepicker-days thead th.switch').click(); 142 | ok(picker.find('.datepicker-months').is(':visible'), 'Months view visible'); 143 | ok(picker.find('.datepicker-months tfoot .today').is(':visible'), 'Today button visible'); 144 | 145 | picker.find('.datepicker-months thead th.switch').click(); 146 | ok(picker.find('.datepicker-years').is(':visible'), 'Years view visible'); 147 | ok(picker.find('.datepicker-years tfoot .today').is(':visible'), 'Today button visible'); 148 | }); 149 | 150 | test('Today Button: data-api', function(){ 151 | var input = $('') 152 | .appendTo('#qunit-fixture') 153 | .val('2012-03-05') 154 | .datepicker({ 155 | format: 'yyyy-mm-dd' 156 | }), 157 | dp = input.data('datepicker'), 158 | picker = dp.picker, 159 | target; 160 | 161 | input.focus(); 162 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 163 | ok(picker.find('.datepicker-days tfoot .today').is(':visible'), 'Today button visible'); 164 | }); 165 | 166 | test('Today Button: moves to today\'s date', function(){ 167 | var input = $('') 168 | .appendTo('#qunit-fixture') 169 | .val('2012-03-05') 170 | .datepicker({ 171 | format: 'yyyy-mm-dd', 172 | todayBtn: true 173 | }), 174 | dp = input.data('datepicker'), 175 | picker = dp.picker, 176 | target; 177 | 178 | input.focus(); 179 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 180 | ok(picker.find('.datepicker-days tfoot .today').is(':visible'), 'Today button visible'); 181 | 182 | target = picker.find('.datepicker-days tfoot .today'); 183 | target.click(); 184 | 185 | var d = new Date(), 186 | today = UTCDate(d.getFullYear(), d.getMonth(), d.getDate()); 187 | datesEqual(dp.viewDate, today); 188 | datesEqual(dp.date, UTCDate(2012, 2, 5)); 189 | }); 190 | 191 | test('Today Button: "linked" selects today\'s date', function(){ 192 | var input = $('') 193 | .appendTo('#qunit-fixture') 194 | .val('2012-03-05') 195 | .datepicker({ 196 | format: 'yyyy-mm-dd', 197 | todayBtn: "linked" 198 | }), 199 | dp = input.data('datepicker'), 200 | picker = dp.picker, 201 | target; 202 | 203 | input.focus(); 204 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 205 | ok(picker.find('.datepicker-days tfoot .today').is(':visible'), 'Today button visible'); 206 | 207 | target = picker.find('.datepicker-days tfoot .today'); 208 | target.click(); 209 | 210 | var d = new Date(), 211 | today = UTCDate(d.getFullYear(), d.getMonth(), d.getDate()); 212 | datesEqual(dp.viewDate, today); 213 | datesEqual(dp.date, today); 214 | }); 215 | 216 | test('Today Highlight: today\'s date is not highlighted by default', patch_date(function(Date){ 217 | Date.now = UTCDate(2012, 2, 15); 218 | var input = $('') 219 | .appendTo('#qunit-fixture') 220 | .val('2012-03-05') 221 | .datepicker({ 222 | format: 'yyyy-mm-dd' 223 | }), 224 | dp = input.data('datepicker'), 225 | picker = dp.picker, 226 | target; 227 | 228 | input.focus(); 229 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 230 | equal(picker.find('.datepicker-days thead .switch').text(), 'March 2012', 'Title is "March 2012"'); 231 | 232 | target = picker.find('.datepicker-days tbody td:contains(15)'); 233 | ok(!target.hasClass('today'), 'Today is not marked with "today" class'); 234 | target = picker.find('.datepicker-days tbody td:contains(14)'); 235 | ok(!target.hasClass('today'), 'Yesterday is not marked with "today" class'); 236 | target = picker.find('.datepicker-days tbody td:contains(16)'); 237 | ok(!target.hasClass('today'), 'Tomorrow is not marked with "today" class'); 238 | })); 239 | 240 | test('Today Highlight: today\'s date is highlighted when not active', patch_date(function(Date){ 241 | Date.now = new Date(2012, 2, 15); 242 | var input = $('') 243 | .appendTo('#qunit-fixture') 244 | .val('2012-03-05') 245 | .datepicker({ 246 | format: 'yyyy-mm-dd', 247 | todayHighlight: true 248 | }), 249 | dp = input.data('datepicker'), 250 | picker = dp.picker, 251 | target; 252 | 253 | input.focus(); 254 | ok(picker.find('.datepicker-days').is(':visible'), 'Days view visible'); 255 | equal(picker.find('.datepicker-days thead .switch').text(), 'March 2012', 'Title is "March 2012"'); 256 | 257 | target = picker.find('.datepicker-days tbody td:contains(15)'); 258 | ok(target.hasClass('today'), 'Today is marked with "today" class'); 259 | target = picker.find('.datepicker-days tbody td:contains(14)'); 260 | ok(!target.hasClass('today'), 'Yesterday is not marked with "today" class'); 261 | target = picker.find('.datepicker-days tbody td:contains(16)'); 262 | ok(!target.hasClass('today'), 'Tomorrow is not marked with "today" class'); 263 | })); 264 | 265 | test('DaysOfWeekDisabled', function(){ 266 | var input = $('') 267 | .appendTo('#qunit-fixture') 268 | .val('2012-10-26') 269 | .datepicker({ 270 | format: 'yyyy-mm-dd', 271 | daysOfWeekDisabled: '1,5' 272 | }), 273 | dp = input.data('datepicker'), 274 | picker = dp.picker, 275 | target; 276 | 277 | 278 | input.focus(); 279 | target = picker.find('.datepicker-days tbody td:nth(22)'); 280 | ok(target.hasClass('disabled'), 'Day of week is disabled'); 281 | target = picker.find('.datepicker-days tbody td:nth(24)'); 282 | ok(!target.hasClass('disabled'), 'Day of week is enabled'); 283 | target = picker.find('.datepicker-days tbody td:nth(26)'); 284 | ok(target.hasClass('disabled'), 'Day of week is disabled'); 285 | }); 286 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bootstrap-datepicker [![Build Status](https://travis-ci.org/eternicode/bootstrap-datepicker.png?branch=master)](https://travis-ci.org/eternicode/bootstrap-datepicker) 2 | 3 | This is a fork of Stefan Petre's [original code](http://www.eyecon.ro/bootstrap-datepicker/); 4 | thanks go to him for getting this thing started! 5 | 6 | Please note that this fork is not used on Stefan's page at this time, nor is it maintained or 7 | contributed to by him (yet?) 8 | 9 | Versions are incremented according to [semver](http://semver.org/). 10 | 11 | # Requirements 12 | 13 | * [Bootstrap](http://twitter.github.com/bootstrap/) [2.0.4](https://github.com/twitter/bootstrap/tree/v2.0.4) 14 | * [jQuery](http://jquery.com/) [1.7.1](https://github.com/jquery/jquery/tree/1.7.1) 15 | 16 | These are the specific versions bootstrap-datpicker is tested against (`js` files) and built against (`css` files). Use other versions at your own risk. 17 | 18 | # Example 19 | 20 | Attached to a field with the format specified via options: 21 | 22 | ```html 23 | 24 | ``` 25 | ```javascript 26 | $('#datepicker').datepicker({ 27 | format: 'mm-dd-yyyy' 28 | }); 29 | ``` 30 | 31 | Attached to a field with the format specified via data tag: 32 | 33 | ```html 34 | 35 | ``` 36 | ```javascript 37 | $('#datepicker').datepicker(); 38 | ``` 39 | 40 | As component: 41 | 42 | ```html 43 |
    44 | 45 | 46 |
    47 | ``` 48 | ```javascript 49 | $('#datepicker').datepicker(); 50 | ``` 51 | 52 | Attached to non-field element, using events to work with the date values. 53 | 54 | ```html 55 |
    56 | Oh snap! 57 |
    58 | 59 | 60 | 61 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
    62 | Start date 63 | Change 64 | 66 | End date 67 | Change 68 |
    2012-02-202012-02-25
    78 | ``` 79 | ```javascript 80 | var startDate = new Date(2012,1,20); 81 | var endDate = new Date(2012,1,25); 82 | $('#date-start') 83 | .datepicker() 84 | .on('changeDate', function(ev){ 85 | if (ev.date.valueOf() > endDate.valueOf()){ 86 | $('#alert').show().find('strong').text('The start date must be before the end date.'); 87 | } else { 88 | $('#alert').hide(); 89 | startDate = new Date(ev.date); 90 | $('#date-start-display').text($('#date-start').data('date')); 91 | } 92 | $('#date-start').datepicker('hide'); 93 | }); 94 | $('#date-end') 95 | .datepicker() 96 | .on('changeDate', function(ev){ 97 | if (ev.date.valueOf() < startDate.valueOf()){ 98 | $('#alert').show().find('strong').text('The end date must be after the start date.'); 99 | } else { 100 | $('#alert').hide(); 101 | endDate = new Date(ev.date); 102 | $('#date-end-display').text($('#date-end').data('date')); 103 | } 104 | $('#date-end').datepicker('hide'); 105 | }); 106 | ``` 107 | 108 | As inline datepicker: 109 | 110 | ```html 111 |
    112 | ``` 113 | ```javascript 114 | $('#datepicker').datepicker(); 115 | ``` 116 | 117 | 118 | # Using bootstrap-datepicker.js 119 | 120 | Call the datepicker via javascript: 121 | 122 | ```javascript 123 | $('#datepicker').datepicker() 124 | ``` 125 | 126 | ## Dependencies 127 | 128 | Requires bootstrap's dropdown component (`dropdowns.less`) for some styles, and bootstrap's sprites (`sprites.less` and associated images) for arrows. 129 | 130 | A standalone .css file (including necessary dropdown styles and alternative, text-based arrows) can be generated by running `build/build_standalone.less` through the `lessc` compiler: 131 | 132 | ```bash 133 | $ lessc build/build_standalone.less datepicker.css 134 | ``` 135 | 136 | ## Options 137 | 138 | All options that take a "Date" can handle a `Date` object; a String formatted according to the given `format`; or a timedelta relative to today, eg '-1d', '+6m +1y', etc, where valid units are 'd' (day), 'w' (week), 'm' (month), and 'y' (year). 139 | 140 | ### format 141 | 142 | String. Default: 'mm/dd/yyyy' 143 | 144 | The date format, combination of d, dd, D, DD, m, mm, M, MM, yy, yyyy. 145 | 146 | * d, dd: Numeric date, no leading zero and leading zero, respectively. Eg, 5, 05. 147 | * D, DD: Abbreviated and full weekday names, respectively. Eg, Mon, Monday. 148 | * m, mm: Numeric month, no leading zero and leading zero, respectively. Eg, 7, 07. 149 | * M, MM: Abbreviated and full month names, respectively. Eg, Jan, January 150 | * yy, yyyy: 2- and 4-digit years, respectively. Eg, 12, 2012. 151 | 152 | ### weekStart 153 | 154 | Integer. Default: 0 155 | 156 | Day of the week start. 0 (Sunday) to 6 (Saturday) 157 | 158 | ### calendarWeeks 159 | 160 | Boolean. Default: false 161 | 162 | Whether or not to show week numbers to the left of week rows. 163 | 164 | ### startDate 165 | 166 | Date. Default: Beginning of time 167 | 168 | The earliest date that may be selected; all earlier dates will be disabled. 169 | 170 | ### endDate 171 | 172 | Date. Default: End of time 173 | 174 | The latest date that may be selected; all later dates will be disabled. 175 | 176 | ### daysOfWeekDisabled 177 | 178 | String, Array. Default: '', [] 179 | 180 | Days of the week that should be disabled. Values are 0 (Sunday) to 6 (Saturday). Multiple values should be comma-separated. Example: disable weekends: `'0,6'` or `[0,6]`. 181 | 182 | ### autoclose 183 | 184 | Boolean. Default: false 185 | 186 | Whether or not to close the datepicker immediately when a date is selected. 187 | 188 | ### startView 189 | 190 | Number, String. Default: 0, 'month' 191 | 192 | The view that the datepicker should show when it is opened. Accepts values of 0 or 'month' for month view (the default), 1 or 'year' for the 12-month overview, and 2 or 'decade' for the 10-year overview. Useful for date-of-birth datepickers. 193 | 194 | ### minViewMode 195 | 196 | Number, String. Default: 0, 'days' 197 | 198 | Set a limit for the view mode. Accepts: 'days' or 0, 'months' or 1, and 'years' or 2. 199 | Gives the ability to pick only a month or an year. The day is set to the 1st for 'months', and the month is set to January for 'years'. 200 | 201 | ### todayBtn 202 | 203 | Boolean, "linked". Default: false 204 | 205 | If true or "linked", displays a "Today" button at the bottom of the datepicker to select the current date. If true, the "Today" button will only move the current date into view; if "linked", the current date will also be selected. 206 | 207 | ### todayHighlight 208 | 209 | Boolean. Default: false 210 | 211 | If true, highlights the current date. 212 | 213 | ### keyboardNavigation 214 | 215 | Boolean. Default: true 216 | 217 | Whether or not to allow date navigation by arrow keys. 218 | 219 | ### language 220 | 221 | String. Default: 'en' 222 | 223 | The IETF code (eg "en" for English, "pt-BR" for Brazilian Portuguese) of the language to use for month and day names. These will also be used as the input's value (and subsequently sent to the server in the case of form submissions). If a full code (eg "de-DE") is supplied the picker will first check for an "de-DE" language and if not found will fallback and check for a "de" language. If an unknown language code is given, English will be used. See I18N below. 224 | 225 | ### forceParse 226 | 227 | Boolean. Default: true 228 | 229 | Whether or not to force parsing of the input value when the picker is closed. That is, when an invalid date is left in the input field by the user, the picker will forcibly parse that value, and set the input's value to the new, valid date, conforming to the given `format`. 230 | 231 | ## Markup 232 | 233 | Format a component. 234 | 235 | ```html 236 |
    237 | 238 | 239 |
    240 | ``` 241 | 242 | ## Methods 243 | 244 | ### .datepicker(options) 245 | 246 | Initializes an datepicker. 247 | 248 | ### remove 249 | 250 | Arguments: None 251 | 252 | Remove the datepicker. Removes attached events, internal attached objects, and 253 | added HTML elements. 254 | 255 | ```javascript 256 | $('#datepicker').datepicker('remove'); 257 | ``` 258 | 259 | ### show 260 | 261 | Arguments: None 262 | 263 | Show the datepicker. 264 | 265 | ```javascript 266 | $('#datepicker').datepicker('show'); 267 | ``` 268 | 269 | ### hide 270 | 271 | Arguments: None 272 | 273 | Hide the datepicker. 274 | 275 | ```javascript 276 | $('#datepicker').datepicker('hide'); 277 | ``` 278 | 279 | ### update 280 | 281 | Arguments: None 282 | 283 | Update the datepicker with the current input value. 284 | 285 | ```javascript 286 | $('#datepicker').datepicker('update'); 287 | ``` 288 | 289 | ### setStartDate 290 | 291 | Arguments: 292 | 293 | * startDate (String) 294 | 295 | Sets a new lower date limit on the datepicker. 296 | 297 | ```javascript 298 | $('#datepicker').datepicker('setStartDate', '2012-01-01'); 299 | ``` 300 | 301 | Omit startDate (or provide an otherwise falsey value) to unset the limit. 302 | 303 | ```javascript 304 | $('#datepicker').datepicker('setStartDate'); 305 | $('#datepicker').datepicker('setStartDate', null); 306 | ``` 307 | 308 | ### setEndDate 309 | 310 | Arguments: 311 | 312 | * endDate (String) 313 | 314 | Sets a new upper date limit on the datepicker. 315 | 316 | ```javascript 317 | $('#datepicker').datepicker('setEndDate', '2012-12-31'); 318 | ``` 319 | 320 | Omit endDate (or provide an otherwise falsey value) to unset the limit. 321 | 322 | ```javascript 323 | $('#datepicker').datepicker('setEndDate'); 324 | $('#datepicker').datepicker('setEndDate', null); 325 | ``` 326 | 327 | ### setDaysOfWeekDisabled 328 | 329 | Arguments: 330 | 331 | * daysOfWeekDisabled (String|Array) 332 | 333 | Sets the days of week that should be disabled. 334 | 335 | ```javascript 336 | $('#datepicker').datepicker('setDaysOfWeekDisabled', [0,6]); 337 | ``` 338 | 339 | Omit daysOfWeekDisabled (or provide an otherwise falsey value) to unset the disabled days. 340 | 341 | ```javascript 342 | $('#datepicker').datepicker('setDaysOfWeekDisabled'); 343 | $('#datepicker').datepicker('setDaysOfWeekDisabled', null); 344 | ``` 345 | 346 | ## Events 347 | 348 | Datepicker class exposes a few events for manipulating the dates. 349 | 350 | ### show 351 | 352 | Fired when the date picker is displayed. 353 | 354 | ### hide 355 | 356 | Fired when the date picker is hidden. 357 | 358 | ### changeDate 359 | 360 | Fired when the date is changed. 361 | 362 | ```javascript 363 | $('#date-end') 364 | .datepicker() 365 | .on('changeDate', function(ev){ 366 | if (ev.date.valueOf() < date-start-display.valueOf()){ 367 | .... 368 | } 369 | }); 370 | ``` 371 | 372 | ### changeYear 373 | 374 | Fired when the *view* year is changed from decade view. 375 | 376 | ### changeMonth 377 | 378 | Fired when the *view* month is changed from year view. 379 | 380 | ## Keyboard support 381 | 382 | The datepicker includes some keyboard navigation: 383 | 384 | ### up, down, left, right arrow keys 385 | 386 | By themselves, left/right will move backward/forward one day, up/down will move back/forward one week. 387 | 388 | With the shift key, up/left will move backward one month, down/right will move forward one month. 389 | 390 | With the ctrl key, up/left will move backward one year, down/right will move forward oone year. 391 | 392 | Shift+ctrl behaves the same as ctrl -- that is, it does not change both month and year simultaneously, only the year. 393 | 394 | ### escape 395 | 396 | The escape key can be used to hide and re-show the datepicker; this is necessary if the user wants to manually edit the value. 397 | 398 | ### enter 399 | 400 | When the picker is visible, enter will simply hide it. When the picker is not visible, enter will have normal effects -- submitting the current form, etc. 401 | 402 | ## I18N 403 | 404 | The plugin supports i18n for the month and weekday names and the `weekStart` option. The default is English ('en'); other available translations are avilable in the `js/locales/` directory, simply include your desired locale after the plugin. To add more languages, simply add a key to `$.fn.datepicker.dates`, before calling `.datepicker()`. Example: 405 | 406 | ```javascript 407 | $.fn.datepicker.dates['en'] = { 408 | days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 409 | daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 410 | daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 411 | months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], 412 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 413 | today: "Today" 414 | }; 415 | ``` 416 | 417 | Right-to-left languages may also include `rtl: true` to make the calendar display appropriately. 418 | 419 | If your browser (or those of your users) is displaying characters wrong, chances are the browser is loading the javascript file with a non-unicode encoding. Simply add `charset="UTF-8"` to your `script` tag: 420 | 421 | ```html 422 | 423 | ``` 424 | -------------------------------------------------------------------------------- /tests/suites/keyboard_navigation/2012.js: -------------------------------------------------------------------------------- 1 | module('Keyboard Navigation 2012', { 2 | setup: function(){ 3 | /* 4 | Tests start with picker on March 31, 2012. Fun facts: 5 | 6 | * February 1, 2012 was on a Wednesday 7 | * February 29, 2012 was on a Wednesday 8 | * March 1, 2012 was on a Thursday 9 | * March 31, 2012 was on a Saturday 10 | */ 11 | this.input = $('') 12 | .appendTo('#qunit-fixture') 13 | .datepicker({format: "dd-mm-yyyy"}) 14 | .focus(); // Activate for visibility checks 15 | this.dp = this.input.data('datepicker') 16 | this.picker = this.dp.picker; 17 | }, 18 | teardown: function(){ 19 | this.picker.remove(); 20 | } 21 | }); 22 | 23 | 24 | test('by day (right/left arrows)', function(){ 25 | var target; 26 | 27 | equal(this.dp.viewMode, 0); 28 | target = this.picker.find('.datepicker-days thead th.switch'); 29 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 30 | 31 | // Navigation: -1 day, left arrow key 32 | this.input.trigger({ 33 | type: 'keydown', 34 | keyCode: 37 35 | }); 36 | // Both updated on keyboard navigation 37 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 30)); 38 | datesEqual(this.dp.date, UTCDate(2012, 2, 30)); 39 | // Month not changed 40 | target = this.picker.find('.datepicker-days thead th.switch'); 41 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 42 | 43 | // Navigation: +1 day, right arrow key 44 | for (var i=0; i<2; i++) 45 | this.input.trigger({ 46 | type: 'keydown', 47 | keyCode: 39 48 | }); 49 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 1)); 50 | datesEqual(this.dp.date, UTCDate(2012, 3, 1)); 51 | // Month changed: April 1 (this is not a joke!) 52 | target = this.picker.find('.datepicker-days thead th.switch'); 53 | equal(target.text(), 'April 2012', 'Title is "April 2012"'); 54 | }); 55 | 56 | test('by week (up/down arrows)', function(){ 57 | var target; 58 | 59 | equal(this.dp.viewMode, 0); 60 | target = this.picker.find('.datepicker-days thead th.switch'); 61 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 62 | 63 | // Navigation: -1 week, up arrow key 64 | this.input.trigger({ 65 | type: 'keydown', 66 | keyCode: 38 67 | }); 68 | // Both updated on keyboard navigation 69 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 24)); 70 | datesEqual(this.dp.date, UTCDate(2012, 2, 24)); 71 | // Month not changed 72 | target = this.picker.find('.datepicker-days thead th.switch'); 73 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 74 | 75 | // Navigation: +1 week, down arrow key 76 | for (var i=0; i<2; i++) 77 | this.input.trigger({ 78 | type: 'keydown', 79 | keyCode: 40 80 | }); 81 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 7)); 82 | datesEqual(this.dp.date, UTCDate(2012, 3, 7)); 83 | target = this.picker.find('.datepicker-days thead th.switch'); 84 | equal(target.text(), 'April 2012', 'Title is "April 2012"'); 85 | }); 86 | 87 | test('by month, v1 (shift + left/right arrows)', function(){ 88 | var target; 89 | 90 | equal(this.dp.viewMode, 0); 91 | target = this.picker.find('.datepicker-days thead th.switch'); 92 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 93 | 94 | // Navigation: -1 month, shift + left arrow key 95 | this.input.trigger({ 96 | type: 'keydown', 97 | keyCode: 37, 98 | shiftKey: true 99 | }); 100 | // Both updated on keyboard navigation, w/ graceful date ends 101 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 29)); 102 | datesEqual(this.dp.date, UTCDate(2012, 1, 29)); 103 | // Month not changed 104 | target = this.picker.find('.datepicker-days thead th.switch'); 105 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 106 | 107 | // Navigation: +1 month, shift + right arrow key 108 | for (var i=0; i<2; i++) 109 | this.input.trigger({ 110 | type: 'keydown', 111 | keyCode: 39, 112 | shiftKey: true 113 | }); 114 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 29)); 115 | datesEqual(this.dp.date, UTCDate(2012, 3, 29)); 116 | target = this.picker.find('.datepicker-days thead th.switch'); 117 | equal(target.text(), 'April 2012', 'Title is "April 2012"'); 118 | }); 119 | 120 | test('by month, v2 (shift + up/down arrows)', function(){ 121 | var target; 122 | 123 | equal(this.dp.viewMode, 0); 124 | target = this.picker.find('.datepicker-days thead th.switch'); 125 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 126 | 127 | // Navigation: -1 month, shift + up arrow key 128 | this.input.trigger({ 129 | type: 'keydown', 130 | keyCode: 38, 131 | shiftKey: true 132 | }); 133 | // Both updated on keyboard navigation, w/ graceful date ends 134 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 29)); 135 | datesEqual(this.dp.date, UTCDate(2012, 1, 29)); 136 | // Month not changed 137 | target = this.picker.find('.datepicker-days thead th.switch'); 138 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 139 | 140 | // Navigation: +1 month, shift + down arrow key 141 | for (var i=0; i<2; i++) 142 | this.input.trigger({ 143 | type: 'keydown', 144 | keyCode: 40, 145 | shiftKey: true 146 | }); 147 | datesEqual(this.dp.viewDate, UTCDate(2012, 3, 29)); 148 | datesEqual(this.dp.date, UTCDate(2012, 3, 29)); 149 | target = this.picker.find('.datepicker-days thead th.switch'); 150 | equal(target.text(), 'April 2012', 'Title is "April 2012"'); 151 | }); 152 | 153 | test('by year, v1 (ctrl + left/right arrows)', function(){ 154 | var target; 155 | 156 | equal(this.dp.viewMode, 0); 157 | target = this.picker.find('.datepicker-days thead th.switch'); 158 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 159 | 160 | // Navigation: -1 year, ctrl + left arrow key 161 | this.input.trigger({ 162 | type: 'keydown', 163 | keyCode: 37, 164 | ctrlKey: true 165 | }); 166 | // Both updated on keyboard navigation 167 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 168 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 169 | // Month not changed 170 | target = this.picker.find('.datepicker-days thead th.switch'); 171 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 172 | 173 | // Navigation: +1 year, ctrl + right arrow key 174 | for (var i=0; i<2; i++) 175 | this.input.trigger({ 176 | type: 'keydown', 177 | keyCode: 39, 178 | ctrlKey: true 179 | }); 180 | datesEqual(this.dp.viewDate, UTCDate(2013, 2, 31)); 181 | datesEqual(this.dp.date, UTCDate(2013, 2, 31)); 182 | target = this.picker.find('.datepicker-days thead th.switch'); 183 | equal(target.text(), 'March 2013', 'Title is "March 2013"'); 184 | }); 185 | 186 | test('by year, v2 (ctrl + up/down arrows)', function(){ 187 | var target; 188 | 189 | equal(this.dp.viewMode, 0); 190 | target = this.picker.find('.datepicker-days thead th.switch'); 191 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 192 | 193 | // Navigation: -1 year, ctrl + up arrow key 194 | this.input.trigger({ 195 | type: 'keydown', 196 | keyCode: 38, 197 | ctrlKey: true 198 | }); 199 | // Both updated on keyboard navigation 200 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 201 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 202 | // Month not changed 203 | target = this.picker.find('.datepicker-days thead th.switch'); 204 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 205 | 206 | // Navigation: +1 year, ctrl + down arrow key 207 | for (var i=0; i<2; i++) 208 | this.input.trigger({ 209 | type: 'keydown', 210 | keyCode: 40, 211 | ctrlKey: true 212 | }); 213 | datesEqual(this.dp.viewDate, UTCDate(2013, 2, 31)); 214 | datesEqual(this.dp.date, UTCDate(2013, 2, 31)); 215 | target = this.picker.find('.datepicker-days thead th.switch'); 216 | equal(target.text(), 'March 2013', 'Title is "March 2013"'); 217 | }); 218 | 219 | test('by year, v3 (ctrl + shift + left/right arrows)', function(){ 220 | var target; 221 | 222 | equal(this.dp.viewMode, 0); 223 | target = this.picker.find('.datepicker-days thead th.switch'); 224 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 225 | 226 | // Navigation: -1 year, ctrl + left arrow key 227 | this.input.trigger({ 228 | type: 'keydown', 229 | keyCode: 37, 230 | ctrlKey: true, 231 | shiftKey: true 232 | }); 233 | // Both updated on keyboard navigation 234 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 235 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 236 | // Month not changed 237 | target = this.picker.find('.datepicker-days thead th.switch'); 238 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 239 | 240 | // Navigation: +1 year, ctrl + right arrow key 241 | for (var i=0; i<2; i++) 242 | this.input.trigger({ 243 | type: 'keydown', 244 | keyCode: 39, 245 | ctrlKey: true, 246 | shiftKey: true 247 | }); 248 | datesEqual(this.dp.viewDate, UTCDate(2013, 2, 31)); 249 | datesEqual(this.dp.date, UTCDate(2013, 2, 31)); 250 | target = this.picker.find('.datepicker-days thead th.switch'); 251 | equal(target.text(), 'March 2013', 'Title is "March 2013"'); 252 | }); 253 | 254 | test('by year, v4 (ctrl + shift + up/down arrows)', function(){ 255 | var target; 256 | 257 | equal(this.dp.viewMode, 0); 258 | target = this.picker.find('.datepicker-days thead th.switch'); 259 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 260 | 261 | // Navigation: -1 year, ctrl + up arrow key 262 | this.input.trigger({ 263 | type: 'keydown', 264 | keyCode: 38, 265 | ctrlKey: true, 266 | shiftKey: true 267 | }); 268 | // Both updated on keyboard navigation 269 | datesEqual(this.dp.viewDate, UTCDate(2011, 2, 31)); 270 | datesEqual(this.dp.date, UTCDate(2011, 2, 31)); 271 | // Month not changed 272 | target = this.picker.find('.datepicker-days thead th.switch'); 273 | equal(target.text(), 'March 2011', 'Title is "March 2011"'); 274 | 275 | // Navigation: +1 year, ctrl + down arrow key 276 | for (var i=0; i<2; i++) 277 | this.input.trigger({ 278 | type: 'keydown', 279 | keyCode: 40, 280 | ctrlKey: true, 281 | shiftKey: true 282 | }); 283 | datesEqual(this.dp.viewDate, UTCDate(2013, 2, 31)); 284 | datesEqual(this.dp.date, UTCDate(2013, 2, 31)); 285 | target = this.picker.find('.datepicker-days thead th.switch'); 286 | equal(target.text(), 'March 2013', 'Title is "March 2013"'); 287 | }); 288 | 289 | test('by year, from leap day', function(){ 290 | var target; 291 | 292 | equal(this.dp.viewMode, 0); 293 | target = this.picker.find('.datepicker-days thead th.switch'); 294 | 295 | this.input.val('29-02-2012').datepicker('update'); 296 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 29)); 297 | datesEqual(this.dp.date, UTCDate(2012, 1, 29)); 298 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 299 | 300 | // Navigation: -1 year 301 | this.input.trigger({ 302 | type: 'keydown', 303 | keyCode: 37, 304 | ctrlKey: true 305 | }); 306 | // Both updated on keyboard navigation; graceful month-end 307 | datesEqual(this.dp.viewDate, UTCDate(2011, 1, 28)); 308 | datesEqual(this.dp.date, UTCDate(2011, 1, 28)); 309 | // Month not changed 310 | target = this.picker.find('.datepicker-days thead th.switch'); 311 | equal(target.text(), 'February 2011', 'Title is "February 2011"'); 312 | 313 | // Navigation: +1 year, back to leap year 314 | this.input.trigger({ 315 | type: 'keydown', 316 | keyCode: 39, 317 | ctrlKey: true 318 | }); 319 | // Both updated on keyboard navigation; graceful month-end 320 | datesEqual(this.dp.viewDate, UTCDate(2012, 1, 28)); 321 | datesEqual(this.dp.date, UTCDate(2012, 1, 28)); 322 | target = this.picker.find('.datepicker-days thead th.switch'); 323 | equal(target.text(), 'February 2012', 'Title is "February 2012"'); 324 | 325 | // Navigation: +1 year 326 | this.input.trigger({ 327 | type: 'keydown', 328 | keyCode: 39, 329 | ctrlKey: true 330 | }); 331 | // Both updated on keyboard navigation; graceful month-end 332 | datesEqual(this.dp.viewDate, UTCDate(2013, 1, 28)); 333 | datesEqual(this.dp.date, UTCDate(2013, 1, 28)); 334 | target = this.picker.find('.datepicker-days thead th.switch'); 335 | equal(target.text(), 'February 2013', 'Title is "February 2013"'); 336 | }); 337 | 338 | test('Selection (enter)', function(){ 339 | var target; 340 | 341 | equal(this.dp.viewMode, 0); 342 | target = this.picker.find('.datepicker-days thead th.switch'); 343 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 344 | 345 | // Navigation: -1 day, left arrow key 346 | this.input.trigger({ 347 | type: 'keydown', 348 | keyCode: 37 349 | }); 350 | // Both updated on keyboard navigation 351 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 30)); 352 | datesEqual(this.dp.date, UTCDate(2012, 2, 30)); 353 | // Month not changed 354 | target = this.picker.find('.datepicker-days thead th.switch'); 355 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 356 | 357 | // Selection: Enter 358 | this.input.trigger({ 359 | type: 'keydown', 360 | keyCode: 13 361 | }); 362 | // Both updated on keyboard navigation 363 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 30)); 364 | datesEqual(this.dp.date, UTCDate(2012, 2, 30)); 365 | // Month not changed 366 | target = this.picker.find('.datepicker-days thead th.switch'); 367 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 368 | 369 | ok(this.picker.is(':not(:visible)'), 'Picker is hidden'); 370 | }); 371 | 372 | test('Toggle hide/show (escape); navigation while hidden is suppressed', function(){ 373 | var target; 374 | 375 | equal(this.dp.viewMode, 0); 376 | target = this.picker.find('.datepicker-days thead th.switch'); 377 | equal(target.text(), 'March 2012', 'Title is "March 2012"'); 378 | 379 | ok(this.picker.is(':visible'), 'Picker is visible'); 380 | 381 | // Hide 382 | this.input.trigger({ 383 | type: 'keydown', 384 | keyCode: 27 385 | }); 386 | 387 | ok(this.picker.is(':not(:visible)'), 'Picker is hidden'); 388 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 389 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 390 | 391 | // left arrow key, *doesn't* navigate 392 | this.input.trigger({ 393 | type: 'keydown', 394 | keyCode: 37 395 | }); 396 | 397 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 398 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 399 | 400 | // Show 401 | this.input.trigger({ 402 | type: 'keydown', 403 | keyCode: 27 404 | }); 405 | 406 | ok(this.picker.is(':visible'), 'Picker is visible'); 407 | datesEqual(this.dp.viewDate, UTCDate(2012, 2, 31)); 408 | datesEqual(this.dp.date, UTCDate(2012, 2, 31)); 409 | }); 410 | 411 | -------------------------------------------------------------------------------- /js/bootstrap-datepicker.js: -------------------------------------------------------------------------------- 1 | /* ========================================================= 2 | * bootstrap-datepicker.js 3 | * http://www.eyecon.ro/bootstrap-datepicker 4 | * ========================================================= 5 | * Copyright 2012 Stefan Petre 6 | * Improvements by Andrew Rowls 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ========================================================= */ 20 | 21 | !function( $ ) { 22 | 23 | function UTCDate(){ 24 | return new Date(Date.UTC.apply(Date, arguments)); 25 | } 26 | function UTCToday(){ 27 | var today = new Date(); 28 | return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); 29 | } 30 | 31 | // Picker object 32 | 33 | var Datepicker = function(element, options) { 34 | var that = this; 35 | 36 | this.element = $(element); 37 | this.language = options.language||this.element.data('date-language')||"en"; 38 | this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de" 39 | this.language = this.language in dates ? this.language : "en"; 40 | this.isRTL = dates[this.language].rtl||false; 41 | this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy'); 42 | this.isInline = false; 43 | this.isInput = this.element.is('input'); 44 | this.component = this.element.is('.date') ? this.element.find('.add-on') : false; 45 | this.hasInput = this.component && this.element.find('input').length; 46 | if(this.component && this.component.length === 0) 47 | this.component = false; 48 | 49 | this._attachEvents(); 50 | 51 | this.forceParse = true; 52 | if ('forceParse' in options) { 53 | this.forceParse = options.forceParse; 54 | } else if ('dateForceParse' in this.element.data()) { 55 | this.forceParse = this.element.data('date-force-parse'); 56 | } 57 | 58 | 59 | this.picker = $(DPGlobal.template) 60 | .appendTo(this.isInline ? this.element : 'body') 61 | .on({ 62 | click: $.proxy(this.click, this), 63 | mousedown: $.proxy(this.mousedown, this) 64 | }); 65 | 66 | if(this.isInline) { 67 | this.picker.addClass('datepicker-inline'); 68 | } else { 69 | this.picker.addClass('datepicker-dropdown dropdown-menu'); 70 | } 71 | if (this.isRTL){ 72 | this.picker.addClass('datepicker-rtl'); 73 | this.picker.find('.prev i, .next i') 74 | .toggleClass('icon-arrow-left icon-arrow-right'); 75 | } 76 | $(document).on('mousedown', function (e) { 77 | // Clicked outside the datepicker, hide it 78 | if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) { 79 | that.hide(); 80 | } 81 | }); 82 | 83 | this.autoclose = false; 84 | if ('autoclose' in options) { 85 | this.autoclose = options.autoclose; 86 | } else if ('dateAutoclose' in this.element.data()) { 87 | this.autoclose = this.element.data('date-autoclose'); 88 | } 89 | 90 | this.keyboardNavigation = true; 91 | if ('keyboardNavigation' in options) { 92 | this.keyboardNavigation = options.keyboardNavigation; 93 | } else if ('dateKeyboardNavigation' in this.element.data()) { 94 | this.keyboardNavigation = this.element.data('date-keyboard-navigation'); 95 | } 96 | 97 | this.viewMode = this.startViewMode = 0; 98 | switch(options.startView || this.element.data('date-start-view')){ 99 | case 2: 100 | case 'decade': 101 | this.viewMode = this.startViewMode = 2; 102 | break; 103 | case 1: 104 | case 'year': 105 | this.viewMode = this.startViewMode = 1; 106 | break; 107 | } 108 | 109 | this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0; 110 | if (typeof this.minViewMode === 'string') { 111 | switch (this.minViewMode) { 112 | case 'months': 113 | this.minViewMode = 1; 114 | break; 115 | case 'years': 116 | this.minViewMode = 2; 117 | break; 118 | default: 119 | this.minViewMode = 0; 120 | break; 121 | } 122 | } 123 | 124 | this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode); 125 | 126 | this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); 127 | this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); 128 | 129 | this.calendarWeeks = false; 130 | if ('calendarWeeks' in options) { 131 | this.calendarWeeks = options.calendarWeeks; 132 | } else if ('dateCalendarWeeks' in this.element.data()) { 133 | this.calendarWeeks = this.element.data('date-calendar-weeks'); 134 | } 135 | if (this.calendarWeeks) 136 | this.picker.find('tfoot th.today') 137 | .attr('colspan', function(i, val){ 138 | return parseInt(val) + 1; 139 | }); 140 | 141 | this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); 142 | this.weekEnd = ((this.weekStart + 6) % 7); 143 | this.startDate = -Infinity; 144 | this.endDate = Infinity; 145 | this.daysOfWeekDisabled = []; 146 | this.setStartDate(options.startDate||this.element.data('date-startdate')); 147 | this.setEndDate(options.endDate||this.element.data('date-enddate')); 148 | this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled')); 149 | this.fillDow(); 150 | this.fillMonths(); 151 | this.update(); 152 | this.showMode(); 153 | 154 | if(this.isInline) { 155 | this.show(); 156 | } 157 | }; 158 | 159 | Datepicker.prototype = { 160 | constructor: Datepicker, 161 | 162 | _events: [], 163 | _attachEvents: function(){ 164 | this._detachEvents(); 165 | if (this.isInput) { // single input 166 | this._events = [ 167 | [this.element, { 168 | focus: $.proxy(this.show, this), 169 | keyup: $.proxy(this.update, this), 170 | keydown: $.proxy(this.keydown, this) 171 | }] 172 | ]; 173 | } 174 | else if (this.component && this.hasInput){ // component: input + button 175 | this._events = [ 176 | // For components that are not readonly, allow keyboard nav 177 | [this.element.find('input'), { 178 | focus: $.proxy(this.show, this), 179 | keyup: $.proxy(this.update, this), 180 | keydown: $.proxy(this.keydown, this) 181 | }], 182 | [this.component, { 183 | click: $.proxy(this.show, this) 184 | }] 185 | ]; 186 | } 187 | else if (this.element.is('div')) { // inline datepicker 188 | this.isInline = true; 189 | } 190 | else { 191 | this._events = [ 192 | [this.element, { 193 | click: $.proxy(this.show, this) 194 | }] 195 | ]; 196 | } 197 | for (var i=0, el, ev; i this.endDate) { 355 | this.viewDate = new Date(this.endDate); 356 | } else { 357 | this.viewDate = new Date(this.date); 358 | } 359 | this.fill(); 360 | }, 361 | 362 | fillDow: function(){ 363 | var dowCnt = this.weekStart, 364 | html = ''; 365 | if(this.calendarWeeks){ 366 | var cell = ' '; 367 | html += cell; 368 | this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); 369 | } 370 | while (dowCnt < this.weekStart + 7) { 371 | html += ''+dates[this.language].daysMin[(dowCnt++)%7]+''; 372 | } 373 | html += ''; 374 | this.picker.find('.datepicker-days thead').append(html); 375 | }, 376 | 377 | fillMonths: function(){ 378 | var html = '', 379 | i = 0; 380 | while (i < 12) { 381 | html += ''+dates[this.language].monthsShort[i++]+''; 382 | } 383 | this.picker.find('.datepicker-months td').html(html); 384 | }, 385 | 386 | fill: function() { 387 | var d = new Date(this.viewDate), 388 | year = d.getUTCFullYear(), 389 | month = d.getUTCMonth(), 390 | startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity, 391 | startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, 392 | endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, 393 | endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, 394 | currentDate = this.date && this.date.valueOf(), 395 | today = new Date(); 396 | this.picker.find('.datepicker-days thead th.switch') 397 | .text(dates[this.language].months[month]+' '+year); 398 | this.picker.find('tfoot th.today') 399 | .text(dates[this.language].today) 400 | .toggle(this.todayBtn !== false); 401 | this.updateNavArrows(); 402 | this.fillMonths(); 403 | var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), 404 | day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); 405 | prevMonth.setUTCDate(day); 406 | prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7); 407 | var nextMonth = new Date(prevMonth); 408 | nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); 409 | nextMonth = nextMonth.valueOf(); 410 | var html = []; 411 | var clsName; 412 | while(prevMonth.valueOf() < nextMonth) { 413 | if (prevMonth.getUTCDay() == this.weekStart) { 414 | html.push(''); 415 | if(this.calendarWeeks){ 416 | // ISO 8601: First week contains first thursday. 417 | // ISO also states week starts on Monday, but we can be more abstract here. 418 | var 419 | // Start of current week: based on weekstart/current date 420 | ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), 421 | // Thursday of this week 422 | th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), 423 | // First Thursday of year, year from thursday 424 | yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), 425 | // Calendar week: ms between thursdays, div ms per day, div 7 days 426 | calWeek = (th - yth) / 864e5 / 7 + 1; 427 | html.push(''+ calWeek +''); 428 | 429 | } 430 | } 431 | clsName = ''; 432 | if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { 433 | clsName += ' old'; 434 | } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) { 435 | clsName += ' new'; 436 | } 437 | // Compare internal UTC date with local today, not UTC today 438 | if (this.todayHighlight && 439 | prevMonth.getUTCFullYear() == today.getFullYear() && 440 | prevMonth.getUTCMonth() == today.getMonth() && 441 | prevMonth.getUTCDate() == today.getDate()) { 442 | clsName += ' today'; 443 | } 444 | if (currentDate && prevMonth.valueOf() == currentDate) { 445 | clsName += ' active'; 446 | } 447 | if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate || 448 | $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) { 449 | clsName += ' disabled'; 450 | } 451 | html.push(''+prevMonth.getUTCDate() + ''); 452 | if (prevMonth.getUTCDay() == this.weekEnd) { 453 | html.push(''); 454 | } 455 | prevMonth.setUTCDate(prevMonth.getUTCDate()+1); 456 | } 457 | this.picker.find('.datepicker-days tbody').empty().append(html.join('')); 458 | var currentYear = this.date && this.date.getUTCFullYear(); 459 | 460 | var months = this.picker.find('.datepicker-months') 461 | .find('th:eq(1)') 462 | .text(year) 463 | .end() 464 | .find('span').removeClass('active'); 465 | if (currentYear && currentYear == year) { 466 | months.eq(this.date.getUTCMonth()).addClass('active'); 467 | } 468 | if (year < startYear || year > endYear) { 469 | months.addClass('disabled'); 470 | } 471 | if (year == startYear) { 472 | months.slice(0, startMonth).addClass('disabled'); 473 | } 474 | if (year == endYear) { 475 | months.slice(endMonth+1).addClass('disabled'); 476 | } 477 | 478 | html = ''; 479 | year = parseInt(year/10, 10) * 10; 480 | var yearCont = this.picker.find('.datepicker-years') 481 | .find('th:eq(1)') 482 | .text(year + '-' + (year + 9)) 483 | .end() 484 | .find('td'); 485 | year -= 1; 486 | for (var i = -1; i < 11; i++) { 487 | html += ''+year+''; 488 | year += 1; 489 | } 490 | yearCont.html(html); 491 | }, 492 | 493 | updateNavArrows: function() { 494 | var d = new Date(this.viewDate), 495 | year = d.getUTCFullYear(), 496 | month = d.getUTCMonth(); 497 | switch (this.viewMode) { 498 | case 0: 499 | if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) { 500 | this.picker.find('.prev').css({visibility: 'hidden'}); 501 | } else { 502 | this.picker.find('.prev').css({visibility: 'visible'}); 503 | } 504 | if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) { 505 | this.picker.find('.next').css({visibility: 'hidden'}); 506 | } else { 507 | this.picker.find('.next').css({visibility: 'visible'}); 508 | } 509 | break; 510 | case 1: 511 | case 2: 512 | if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) { 513 | this.picker.find('.prev').css({visibility: 'hidden'}); 514 | } else { 515 | this.picker.find('.prev').css({visibility: 'visible'}); 516 | } 517 | if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) { 518 | this.picker.find('.next').css({visibility: 'hidden'}); 519 | } else { 520 | this.picker.find('.next').css({visibility: 'visible'}); 521 | } 522 | break; 523 | } 524 | }, 525 | 526 | click: function(e) { 527 | e.stopPropagation(); 528 | e.preventDefault(); 529 | var target = $(e.target).closest('span, td, th'); 530 | if (target.length == 1) { 531 | switch(target[0].nodeName.toLowerCase()) { 532 | case 'th': 533 | switch(target[0].className) { 534 | case 'switch': 535 | this.showMode(1); 536 | break; 537 | case 'prev': 538 | case 'next': 539 | var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); 540 | switch(this.viewMode){ 541 | case 0: 542 | this.viewDate = this.moveMonth(this.viewDate, dir); 543 | break; 544 | case 1: 545 | case 2: 546 | this.viewDate = this.moveYear(this.viewDate, dir); 547 | break; 548 | } 549 | this.fill(); 550 | break; 551 | case 'today': 552 | var date = new Date(); 553 | date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); 554 | 555 | this.showMode(-2); 556 | var which = this.todayBtn == 'linked' ? null : 'view'; 557 | this._setDate(date, which); 558 | break; 559 | } 560 | break; 561 | case 'span': 562 | if (!target.is('.disabled')) { 563 | this.viewDate.setUTCDate(1); 564 | if (target.is('.month')) { 565 | var day = 1; 566 | var month = target.parent().find('span').index(target); 567 | var year = this.viewDate.getUTCFullYear(); 568 | this.viewDate.setUTCMonth(month); 569 | this.element.trigger({ 570 | type: 'changeMonth', 571 | date: this.viewDate 572 | }); 573 | if ( this.minViewMode == 1 ) { 574 | this._setDate(UTCDate(year, month, day,0,0,0,0)); 575 | } 576 | } else { 577 | var year = parseInt(target.text(), 10)||0; 578 | var day = 1; 579 | var month = 0; 580 | this.viewDate.setUTCFullYear(year); 581 | this.element.trigger({ 582 | type: 'changeYear', 583 | date: this.viewDate 584 | }); 585 | if ( this.minViewMode == 2 ) { 586 | this._setDate(UTCDate(year, month, day,0,0,0,0)); 587 | } 588 | } 589 | this.showMode(-1); 590 | this.fill(); 591 | } 592 | break; 593 | case 'td': 594 | if (target.is('.day') && !target.is('.disabled')){ 595 | var day = parseInt(target.text(), 10)||1; 596 | var year = this.viewDate.getUTCFullYear(), 597 | month = this.viewDate.getUTCMonth(); 598 | if (target.is('.old')) { 599 | if (month === 0) { 600 | month = 11; 601 | year -= 1; 602 | } else { 603 | month -= 1; 604 | } 605 | } else if (target.is('.new')) { 606 | if (month == 11) { 607 | month = 0; 608 | year += 1; 609 | } else { 610 | month += 1; 611 | } 612 | } 613 | this._setDate(UTCDate(year, month, day,0,0,0,0)); 614 | } 615 | break; 616 | } 617 | } 618 | }, 619 | 620 | _setDate: function(date, which){ 621 | if (!which || which == 'date') 622 | this.date = date; 623 | if (!which || which == 'view') 624 | this.viewDate = date; 625 | this.fill(); 626 | this.setValue(); 627 | this.element.trigger({ 628 | type: 'changeDate', 629 | date: this.date 630 | }); 631 | var element; 632 | if (this.isInput) { 633 | element = this.element; 634 | } else if (this.component){ 635 | element = this.element.find('input'); 636 | } 637 | if (element) { 638 | element.change(); 639 | if (this.autoclose && (!which || which == 'date')) { 640 | this.hide(); 641 | } 642 | } 643 | }, 644 | 645 | moveMonth: function(date, dir){ 646 | if (!dir) return date; 647 | var new_date = new Date(date.valueOf()), 648 | day = new_date.getUTCDate(), 649 | month = new_date.getUTCMonth(), 650 | mag = Math.abs(dir), 651 | new_month, test; 652 | dir = dir > 0 ? 1 : -1; 653 | if (mag == 1){ 654 | test = dir == -1 655 | // If going back one month, make sure month is not current month 656 | // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) 657 | ? function(){ return new_date.getUTCMonth() == month; } 658 | // If going forward one month, make sure month is as expected 659 | // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) 660 | : function(){ return new_date.getUTCMonth() != new_month; }; 661 | new_month = month + dir; 662 | new_date.setUTCMonth(new_month); 663 | // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 664 | if (new_month < 0 || new_month > 11) 665 | new_month = (new_month + 12) % 12; 666 | } else { 667 | // For magnitudes >1, move one month at a time... 668 | for (var i=0; i= this.startDate && date <= this.endDate; 691 | }, 692 | 693 | keydown: function(e){ 694 | if (this.picker.is(':not(:visible)')){ 695 | if (e.keyCode == 27) // allow escape to hide and re-show picker 696 | this.show(); 697 | return; 698 | } 699 | var dateChanged = false, 700 | dir, day, month, 701 | newDate, newViewDate; 702 | switch(e.keyCode){ 703 | case 27: // escape 704 | this.hide(); 705 | e.preventDefault(); 706 | break; 707 | case 37: // left 708 | case 39: // right 709 | if (!this.keyboardNavigation) break; 710 | dir = e.keyCode == 37 ? -1 : 1; 711 | if (e.ctrlKey){ 712 | newDate = this.moveYear(this.date, dir); 713 | newViewDate = this.moveYear(this.viewDate, dir); 714 | } else if (e.shiftKey){ 715 | newDate = this.moveMonth(this.date, dir); 716 | newViewDate = this.moveMonth(this.viewDate, dir); 717 | } else { 718 | newDate = new Date(this.date); 719 | newDate.setUTCDate(this.date.getUTCDate() + dir); 720 | newViewDate = new Date(this.viewDate); 721 | newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir); 722 | } 723 | if (this.dateWithinRange(newDate)){ 724 | this.date = newDate; 725 | this.viewDate = newViewDate; 726 | this.setValue(); 727 | this.update(); 728 | e.preventDefault(); 729 | dateChanged = true; 730 | } 731 | break; 732 | case 38: // up 733 | case 40: // down 734 | if (!this.keyboardNavigation) break; 735 | dir = e.keyCode == 38 ? -1 : 1; 736 | if (e.ctrlKey){ 737 | newDate = this.moveYear(this.date, dir); 738 | newViewDate = this.moveYear(this.viewDate, dir); 739 | } else if (e.shiftKey){ 740 | newDate = this.moveMonth(this.date, dir); 741 | newViewDate = this.moveMonth(this.viewDate, dir); 742 | } else { 743 | newDate = new Date(this.date); 744 | newDate.setUTCDate(this.date.getUTCDate() + dir * 7); 745 | newViewDate = new Date(this.viewDate); 746 | newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7); 747 | } 748 | if (this.dateWithinRange(newDate)){ 749 | this.date = newDate; 750 | this.viewDate = newViewDate; 751 | this.setValue(); 752 | this.update(); 753 | e.preventDefault(); 754 | dateChanged = true; 755 | } 756 | break; 757 | case 13: // enter 758 | this.hide(); 759 | e.preventDefault(); 760 | break; 761 | case 9: // tab 762 | this.hide(); 763 | break; 764 | } 765 | if (dateChanged){ 766 | this.element.trigger({ 767 | type: 'changeDate', 768 | date: this.date 769 | }); 770 | var element; 771 | if (this.isInput) { 772 | element = this.element; 773 | } else if (this.component){ 774 | element = this.element.find('input'); 775 | } 776 | if (element) { 777 | element.change(); 778 | } 779 | } 780 | }, 781 | 782 | showMode: function(dir) { 783 | if (dir) { 784 | this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); 785 | } 786 | /* 787 | vitalets: fixing bug of very special conditions: 788 | jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover. 789 | Method show() does not set display css correctly and datepicker is not shown. 790 | Changed to .css('display', 'block') solve the problem. 791 | See https://github.com/vitalets/x-editable/issues/37 792 | 793 | In jquery 1.7.2+ everything works fine. 794 | */ 795 | //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); 796 | this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); 797 | this.updateNavArrows(); 798 | } 799 | }; 800 | 801 | $.fn.datepicker = function ( option ) { 802 | var args = Array.apply(null, arguments); 803 | args.shift(); 804 | return this.each(function () { 805 | var $this = $(this), 806 | data = $this.data('datepicker'), 807 | options = typeof option == 'object' && option; 808 | if (!data) { 809 | $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); 810 | } 811 | if (typeof option == 'string' && typeof data[option] == 'function') { 812 | data[option].apply(data, args); 813 | } 814 | }); 815 | }; 816 | 817 | $.fn.datepicker.defaults = { 818 | }; 819 | $.fn.datepicker.Constructor = Datepicker; 820 | var dates = $.fn.datepicker.dates = { 821 | en: { 822 | days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], 823 | daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 824 | daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 825 | months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], 826 | monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 827 | today: "Today" 828 | } 829 | }; 830 | 831 | var DPGlobal = { 832 | modes: [ 833 | { 834 | clsName: 'days', 835 | navFnc: 'Month', 836 | navStep: 1 837 | }, 838 | { 839 | clsName: 'months', 840 | navFnc: 'FullYear', 841 | navStep: 1 842 | }, 843 | { 844 | clsName: 'years', 845 | navFnc: 'FullYear', 846 | navStep: 10 847 | }], 848 | isLeapYear: function (year) { 849 | return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 850 | }, 851 | getDaysInMonth: function (year, month) { 852 | return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; 853 | }, 854 | validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, 855 | nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, 856 | parseFormat: function(format){ 857 | // IE treats \0 as a string end in inputs (truncating the value), 858 | // so it's a bad format delimiter, anyway 859 | var separators = format.replace(this.validParts, '\0').split('\0'), 860 | parts = format.match(this.validParts); 861 | if (!separators || !separators.length || !parts || parts.length === 0){ 862 | throw new Error("Invalid date format."); 863 | } 864 | return {separators: separators, parts: parts}; 865 | }, 866 | parseDate: function(date, format, language) { 867 | if (date instanceof Date) return date; 868 | if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { 869 | var part_re = /([\-+]\d+)([dmwy])/, 870 | parts = date.match(/([\-+]\d+)([dmwy])/g), 871 | part, dir; 872 | date = new Date(); 873 | for (var i=0; i'+ 980 | ''+ 981 | ''+ 982 | ''+ 983 | ''+ 984 | ''+ 985 | '', 986 | contTemplate: '', 987 | footTemplate: '' 988 | }; 989 | DPGlobal.template = '
    '+ 990 | '
    '+ 991 | ''+ 992 | DPGlobal.headTemplate+ 993 | ''+ 994 | DPGlobal.footTemplate+ 995 | '
    '+ 996 | '
    '+ 997 | '
    '+ 998 | ''+ 999 | DPGlobal.headTemplate+ 1000 | DPGlobal.contTemplate+ 1001 | DPGlobal.footTemplate+ 1002 | '
    '+ 1003 | '
    '+ 1004 | '
    '+ 1005 | ''+ 1006 | DPGlobal.headTemplate+ 1007 | DPGlobal.contTemplate+ 1008 | DPGlobal.footTemplate+ 1009 | '
    '+ 1010 | '
    '+ 1011 | '
    '; 1012 | 1013 | $.fn.datepicker.DPGlobal = DPGlobal; 1014 | 1015 | }( window.jQuery ); 1016 | --------------------------------------------------------------------------------