diff --git a/unicalendar/class.unicalendar.php b/unicalendar/class.unicalendar.php
new file mode 100644
index 0000000..32529bb
--- /dev/null
+++ b/unicalendar/class.unicalendar.php
@@ -0,0 +1,186 @@
+Query("
+ SELECT *
+ FROM " . PREFIX . "_module_unicalendar
+ WHERE id = '" . $id . "'
+ ");
+ $unicalendars = array();
+ while ($row = $sql->FetchAssocArray())
+ {
+ array_push($unicalendars, $row);
+ }
+ foreach ( $unicalendars as $k=>$v )
+ {
+ $uevents = $v['uca_events'];
+ $urubric_id = $v['uca_rubric_id'];
+ $udoc_id = $v['uca_doc_id'];
+ }
+ // Если выбрали вывести все документы из заданной рубрики - документы с Id=1 и Id=2 - выводиться не будут!
+ if ($uevents !='' && $uevents == '1'){
+ $sql = $AVE_DB->Query("
+ SELECT Id, document_alias, document_title, document_published, document_meta_description
+ FROM " . PREFIX . "_documents
+ WHERE rubric_id = '" . $urubric_id . "' AND Id > 2
+ ");
+ $results = array();
+ while ($row = $sql->FetchAssocArray())
+ {
+ array_push($results, $row);
+ }
+
+ }
+
+ // Если выбрали вывести выбранные документы из заданной рубрики
+ if ($uevents !='' && $uevents == '2'){
+ $sql = $AVE_DB->Query("
+ SELECT Id, document_alias, document_title, document_published, document_meta_description
+ FROM " . PREFIX . "_documents
+ WHERE rubric_id = '" . $urubric_id . "' AND Id > 2 AND $udoc_id
+ ");
+ $results = array();
+ while ($row = $sql->FetchAssocArray())
+ {
+ array_push($results, $row);
+ }
+
+ }
+ $AVE_Template->assign('unicalendars', $unicalendars);
+ $AVE_Template->assign('results', $results);
+ $AVE_Template->display($tpl_dir . 'unicalendar.tpl');
+ }
+
+ /**
+ * ФУНКЦИИ АДМИНИСТРАТИВНОЙ ЧАСТИ
+ */
+
+
+ /**
+ * Вывод списка календарей
+ *
+ * @param string $tpl_dir - путь к папке с шаблонами модуля
+ */
+ function unicalendarList($tpl_dir)
+ {
+ global $AVE_DB, $AVE_Template;
+
+ $unicalendars = array();
+
+ $limit = 20;
+ $start = get_current_page() * $limit - $limit;
+
+ $sql = $AVE_DB->Query("
+ SELECT SQL_CALC_FOUND_ROWS
+ u.*,
+ COUNT(u.id) AS uca_count
+ FROM
+ " . PREFIX . "_module_unicalendar AS u
+ GROUP BY u.id
+ ORDER BY u.id ASC
+ LIMIT " . $start . "," . $limit . "
+ ");
+
+ $sql_num = $AVE_DB->Query("SELECT FOUND_ROWS()");
+ $num = $sql_num->GetCell();
+
+ while($row = $sql->FetchAssocArray())
+ {
+ array_push($unicalendars, $row);
+ }
+
+ if ($num > $limit)
+ {
+ $page_nav = "
{t} ";
+ $page_nav = get_pagination(ceil($num / $limit), 'page', $page_nav);
+ }
+ else
+ {
+ $page_nav = '';
+ }
+
+ $AVE_Template->assign('page_nav', $page_nav);
+
+ if (!empty($_REQUEST['alert']))
+ {
+ $AVE_Template->assign('alert', htmlspecialchars(stripslashes($_REQUEST['alert'])));
+ }
+ $AVE_Template->assign('unicalendars', $unicalendars);
+ $AVE_Template->assign('formaction', 'index.php?do=modules&action=modedit&mod=unicalendar&moduleaction=new&sub=save&cp=' . SESSION);
+ $AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . 'admin_unicalendar_list.tpl'));
+ }
+
+ /**
+ * Создание календаря
+ *
+ */
+ function unicalendarNew()
+ {
+ if (isset($_REQUEST['sub']) && $_REQUEST['sub'] == 'save')
+ {
+ global $AVE_DB;
+ $cont = true;
+ $alert = '';
+ if (empty($_POST['uca_title']))
+ {
+ $alert = '&alert=empty_uca_title';
+ $cont = false;
+ }
+ if ($cont)
+ {
+ $AVE_DB->Query("
+ INSERT
+ INTO " . PREFIX . "_module_unicalendar
+ SET
+ id = '',
+ uca_title = '" . $_POST['uca_title'] . "',
+ uca_events = '" . $_POST['uca_events'] . "',
+ uca_rubric_id = '" . $_POST['uca_rubric_id'] . "',
+ uca_doc_id = '" . $_POST['uca_doc_id'] . "'
+ ");
+ }
+ header('Location:index.php?do=modules&action=modedit&mod=unicalendar&moduleaction=1'. $alert);
+ exit;
+ }
+ }
+
+ /**
+ * Удаление календаря
+ *
+ * @param int $unicalendar_id - идентификатор календаря
+ */
+ function unicalendarDelete($unicalendar_id)
+ {
+ global $AVE_DB;
+ $AVE_DB->Query("DELETE FROM " . PREFIX . "_module_unicalendar WHERE id = '" . $unicalendar_id . "'");
+ header('Location:index.php?do=modules&action=modedit&mod=unicalendar&moduleaction=1&cp=' . SESSION);
+ exit;
+ }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/unicalendar/css/eventCalendar.css b/unicalendar/css/eventCalendar.css
new file mode 100644
index 0000000..a24865f
--- /dev/null
+++ b/unicalendar/css/eventCalendar.css
@@ -0,0 +1,99 @@
+/*= CORE CSS */
+
+.eventCalendar-hidden {
+ display: none;
+}
+
+.eventCalendar-wrap {
+ position: relative;
+ overflow: hidden;
+}
+
+.eventCalendar-arrow {
+ position: absolute;
+ z-index: 5;
+ top: 3px;
+}
+
+.eventCalendar-prev {
+ left: 3px;
+}
+
+.eventCalendar-next {
+ right: 3px;
+}
+
+.eventCalendar-monthTitle {
+ display: block;
+ text-align: center;
+}
+
+.eventCalendar-monthWrap {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+}
+
+.eventCalendar-currentMonth {
+ z-index: 1;
+}
+
+.eventCalendar-daysList {
+ display: table;
+ width: 100%;
+}
+
+.eventCalendar-showAsWeek {
+ display: block;
+}
+
+.eventCalendar-daysList li {
+ display: table-cell;
+}
+
+.eventCalendar-dayWithEvents {
+ background: rgba(0, 0, 0, 0.15);
+}
+
+.eventCalendar-current {
+ background: rgba(0, 0, 0, 0.35);
+}
+
+.eventCalendar-showAsWeek li {
+ display: block;
+ float: left;
+ width: 14.28%;
+ height: 20px;
+}
+
+.eventCalendar-daysList a {
+ padding: 0;
+ display: block;
+ text-align: center;
+ font-size: 8px;
+ min-width: 7px;
+}
+
+.eventCalendar-loading {
+ display: block;
+ min-width: 100px;
+ height: 40px;
+ line-height: 40px;
+}
+
+.eventCalendar-list {
+ position: relative;
+ z-index: 1;
+}
+
+.eventCalendar-oldEventList {
+ z-index: 2;
+}
+
+.eventCalendar-subtitle {
+ padding-left: 5px;
+ font-weight: bold;
+ font-size: 120%;
+}
+/* end of core CSS */
\ No newline at end of file
diff --git a/unicalendar/css/eventCalendar_theme_responsive.css b/unicalendar/css/eventCalendar_theme_responsive.css
new file mode 100644
index 0000000..81e7bf0
--- /dev/null
+++ b/unicalendar/css/eventCalendar_theme_responsive.css
@@ -0,0 +1,371 @@
+ /* Theme CSS */
+ /*html {
+ background-color:#eee;
+ }
+ body {
+ font-family: Arial, "Lucida Grande", sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ color: #555;
+ background-color:#fff;
+ }*/
+.eventCalendar-wrap {
+ border:solid 1px #d3d3d3;
+ box-shadow:0 0 15px #999;
+ border-radius:5px;
+ margin-bottom:20px;
+ background-color:#fff;
+ color:#807E7E;
+}
+ .eventCalendar-wrap .eventCalendar-arrow {
+ text-decoration:none;
+ color:#fff;
+ padding:0 5px;
+ line-height:28px;
+ top:9px;
+ padding:8px 10px;
+ }
+ .eventCalendar-wrap .eventCalendar-arrow.prev {
+
+ }
+ .eventCalendar-wrap .eventCalendar-arrow:hover { opacity:0.7;}
+ .eventCalendar-wrap .eventCalendar-arrow span {
+ height: 0;
+ width: 0;
+ font-size: 0;
+ line-height: 0;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-left: 6px solid #fff;
+ float:left;
+ text-indent:-5000px;
+ }
+ .eventCalendar-wrap .eventCalendar-arrow.eventCalendar-prev span {
+ border-left-width:0;
+ border-right: 6px solid #fff;
+ }
+ .eventCalendar-slider { height:80px;}
+
+ .eventCalendar-monthWrap {
+ border-radius:5px;
+ top:10px;
+ left:0px;
+ }
+ .eventCalendar-currentTitle {
+ line-height:25px;
+ background-color:#138DA5;
+ outline:1px solid #138DA5;
+ border:1px solid #E3E3E3;
+ border-width:1px 0;
+ }
+ .eventCalendar-currentTitle .eventCalendar-monthTitle {
+ font-size:110%;
+ text-decoration:none;
+ font-weight:bold;
+ color:#fff;
+ }
+
+ .eventCalendar-daysList {
+ zoom: 1;
+ padding:0;
+ width:100%;
+
+ }
+ .eventCalendar-daysList.eventCalendar-showAsWeek {
+ margin:10px 5px;
+ width:auto;
+
+ border:solid 1px #BCBCBC;
+ border-bottom-width:0;
+ border-radius:0;
+ background-color:#CCCCCC;
+ background-image: linear-gradient(top, #EEEEEE 42%, #CCCCCC 71%);
+ background-image: -o-linear-gradient(top, #EEEEEE 42%, #CCCCCC 71%);
+ background-image: -moz-linear-gradient(top, #EEEEEE 42%, #CCCCCC 71%);
+ background-image: -webkit-linear-gradient(top, #EEEEEE 42%, #CCCCCC 71%);
+ background-image: -ms-linear-gradient(top, #EEEEEE 42%, #CCCCCC 71%);
+
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left bottom,
+ color-stop(0.42, #EEEEEE),
+ color-stop(0.71, #CCCCCC)
+ );
+
+ }
+ .eventCalendar-daysList.showDayNames.eventCalendar-showAsWeek {
+
+ border-radius:5px 5px 0 0;
+ }
+ .eventCalendar-daysList:before, .eventCalendar-daysList:after { content:""; display:table; }
+ .eventCalendar-daysList:after { clear: both; }
+ .eventCalendar-day-header {
+ text-transform:lowercase;
+ text-align:center;
+ font-size:12px;
+ border-bottom:solid 1px #BCBCBC;
+ }
+ .eventCalendar-daysList.eventCalendar-showAsWeek li {
+ height:auto; margin:0;
+ }
+ .eventCalendar-daysList.eventCalendar-showAsWeek li.eventCalendar-empty {
+ background-color: #ccc;
+ min-height:27px;
+ border-top: solid 1px #ccc;
+ }
+ .eventCalendar-day a {
+ text-decoration:none;
+ font-size:10px;
+ color:#424242;
+ }
+ .eventCalendar-day {
+ border-left:solid 1px #BCBCBC;
+ }
+ .eventCalendar-day a {
+ border:solid 1px #BCBCBC;
+ border-width:0 1px 1px 0;
+ }
+ .eventCalendar-showAsWeek .eventCalendar-day { border-left-width:0;}
+ .eventCalendar-showAsWeek .eventCalendar-day a {
+ border:solid 1px red;
+ border-color:#fff #BCBCBC #BCBCBC #eee;
+ line-height:27px;
+ font-size:11px;
+
+ }
+ .eventCalendar-day a:hover {
+ background-color:#E4E4E4;
+ /* box-shadow:inset 5px 5px 10px #C1C1C1;
+ text-shadow: 2px 2px 2px #C1C1C1;*/
+ }
+ .eventCalendar-daysList li.today a {
+ color:#fff;
+ background:#aaa;
+ /* box-shadow:inset 5px 5px 10px #777;
+ text-shadow: 2px 2px 2px #777;*/
+ }
+ li.eventCalendar-day.today a:hover {
+ background-color:#ccc;
+ /*box-shadow:inset 5px 5px 10px #999;*/
+ }
+
+ .eventCalendar-daysList li.eventCalendar-dayWithEvents a {
+ background:#89B814;
+ /*box-shadow:inset 5px 5px 10px #698B10;
+ text-shadow: 2px 2px 2px #698B10;*/
+ color:#fff;
+ }
+ li.eventCalendar-day.eventCalendar-dayWithEvents a:hover {
+ background-color:#C2D374;
+ /*box-shadow:inset 5px 5px 10px #89B814;
+ text-shadow: 2px 2px 2px #89B814;*/
+ }
+
+
+ .eventCalendar-daysList li.current a {
+ color:#fff;
+ background:#449FB2;
+ box-shadow:inset 5px 5px 10px #216B7A;
+ text-shadow: 2px 2px 2px #216B7A;
+ }
+ li.eventCalendar-day.current a:hover {
+ background-color:#79BDCC;
+ box-shadow:inset 5px 5px 10px #449FB2;
+ text-shadow: 2px 2px 2px #449FB2;
+ }
+ .eventCalendar-loading {
+ border-radius:4px;
+ margin:5px auto;
+ padding:0 10px;
+ background-color:#ccc;
+ color:#fff;
+ text-align:center;
+ font-weight:bold;
+ box-shadow:0 0 10px #ccc;
+ text-shadow:0 0 3px #aaa;
+ position:absolute;
+ z-index:4;
+ top:25px;
+ left:5px;
+ }
+ .eventCalendar-loading.error {
+ background-color:red;
+ }
+
+.eventCalendar-subtitle { padding-top:10px;}
+.eventCalendar-list-wrap {
+ min-height:100px;
+ position:relative;
+}
+ .eventCalendar-list-content.scrollable {
+
+ height:100px;
+ overflow-y:auto;
+ margin:0 5px 5px 0;
+ }
+ .eventCalendar-list {
+ margin:0; padding:0; list-style-type:none;
+ }
+ .eventCalendar-list li {
+ padding:0 5px 15px;
+ margin:0;
+ clear:both;
+ }
+ .eventCalendar-list li time {
+ font-size:10px;
+ line-height:13px;
+ }
+ .eventCalendar-list li time em {
+ float:left;
+ font-style:normal;
+ }
+ .eventCalendar-list li time small {
+ font-size:10px;
+ float:left;
+ background-color:#807E7E;
+ color:#fff;
+ padding:0 5px 0 4px;
+ margin:0 0 0 3px;
+ }
+ .eventCalendar-list li .eventCalendar-eventTitle {
+ display:block;
+ clear:both;
+
+ font-weight:bold;
+ text-decoration:none;
+ }
+ .eventCalendar-list li a.eventCalendar-eventTitle {
+ color:#0E8EAB;
+ }
+ .eventCalendar-list li a.eventCalendar-eventTitle:hover { text-decoration:underline;}
+ .eventCalendar-list li .eventDesc {
+ clear: both;
+ margin:0 0 5px 0;
+ font-size:80%;
+ line-height:1.2em;
+
+ }
+ .eventCalendar-list .eventCalendar-noEvents {
+ font-size:120%;
+ border-radius:4px;
+ margin:5px;
+ padding:5px;
+
+ background-color:#ccc;
+ color:#fff;
+ text-align:center;
+ font-weight:bold;
+ box-shadow:0 0 10px #ccc;
+ text-shadow:0 0 3px #aaa;
+ }
+
+.bt {
+ font-size:12px;
+ display:block;
+ clear:both;
+ text-align: center;
+ margin-top:10px;
+ padding: 9px 34px 11px;
+ text-decoration: none;
+ font-weight: bold;
+ line-height: 1;
+
+ color: #ffffff;
+ background-color: #698B10;
+ background-repeat: repeat-x;
+
+ background-image: -khtml-gradient(linear, left top, left bottom, from(#89B814), to(#698B10));
+ background-image: -moz-linear-gradient(top, #89B814, #698B10);
+ background-image: -ms-linear-gradient(top, #89B814, #698B10);
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #89B814), color-stop(100%, #698B10));
+ background-image: -webkit-linear-gradient(top, #89B814, #698B10);
+ background-image: -o-linear-gradient(top, #89B814, #698B10);
+ background-image: linear-gradient(top, #89B814, #698B10);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#89B814', endColorstr='#698B10', GradientType=0);
+
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+
+ border: 1px solid #698B10;
+ border-color: #698B10 #698B10 #465F05;
+
+ border-radius: 4px;
+
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+
+ -webkit-transition: 0.1s linear all;
+ -moz-transition: 0.1s linear all;
+ -ms-transition: 0.1s linear all;
+ -o-transition: 0.1s linear all;
+ transition: 0.1s linear all;
+}
+.bt:hover {
+ background-position: 0 -15px;
+ text-decoration: none;
+ color: #E4E4E4;
+ }
+/* end of theme css */
+
+
+/*= ONLY FOR DEMO PAGE */
+body {
+/*
+ width:978px;
+ box-shadow:0 0 10px #777;
+ padding: 20px 40px;
+ margin:0 auto;
+ */
+}
+a { color: #0E8EAB}
+/*
+.eventCalendar-wrap {
+ width:265px;
+}
+*/
+.features li { margin-bottom:3px;}
+#thanksPanel li { margin-bottom:0;}
+#introPanel { padding-top:20px;}
+
+.poweredBy img {
+ float:left;
+ margin-right:3px;
+}
+ .poweredBy .data {
+ float:left;
+ }
+ .poweredBy .name {
+ font-weight:bold;
+ color:#555;
+ text-decoration:none;
+ display:block;
+ margin-top:28px;
+ }
+ .poweredBy .twitter {
+ text-decoration:none;
+ display:block;
+ float:left;
+ }
+.features {
+ padding-left:20px;
+ float:left;
+}
+#appLogo {
+ margin-right:30px;
+}
+pre {
+ clear:both;
+ background-color:#FFFFCC;
+ padding:5px;
+ border:solid 1px #FED17E;
+ overflow:auto;
+}
+#license {
+ width:300px;
+ float:right;
+ font-size:10px;
+ line-height:12px;
+ text-align:center;
+}
+
+
+/* end of demo page */
\ No newline at end of file
diff --git a/unicalendar/css/index.php b/unicalendar/css/index.php
new file mode 100644
index 0000000..4ca25aa
--- /dev/null
+++ b/unicalendar/css/index.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/images/index.php b/unicalendar/images/index.php
new file mode 100644
index 0000000..4ca25aa
--- /dev/null
+++ b/unicalendar/images/index.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/js/index.php b/unicalendar/js/index.php
new file mode 100644
index 0000000..4ca25aa
--- /dev/null
+++ b/unicalendar/js/index.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/js/jquery.eventCalendar.js b/unicalendar/js/jquery.eventCalendar.js
new file mode 100644
index 0000000..2d7de42
--- /dev/null
+++ b/unicalendar/js/jquery.eventCalendar.js
@@ -0,0 +1,495 @@
+/* =
+ jquery.eventCalendar.js
+ version: 0.7
+ date: 13-08-2015
+ author:
+ Jaime Fernandez (@vissit)
+ company:
+ Paradigma Tecnologico (@paradigmate)
+ url:
+ http://www.vissit.com/projects/eventCalendar/
+*/
+;(function( $ ) {
+ $.fn.eventCalendar = function(options){
+ var calendar = this;
+
+ if ( options.locales && typeof(options.locales) == 'string' ) {
+ $.getJSON(options.locales, function(data) {
+ options.locales = $.extend({}, $.fn.eventCalendar.defaults.locales, data);
+ moment.locale(data.locale, options.locales.moment);
+ moment.locale(data.locale);
+
+ initEventCalendar(calendar, options);
+ }).error(function() {
+ showError("error getting locale json", $(this));
+ });
+ } else {
+ if ( options.locales && options.locales.locale ) {
+ options.locales = $.extend({}, $.fn.eventCalendar.defaults.locales, options.locales);
+ moment.locale(options.locales.locale, options.locales.moment);
+ moment.locale(options.locales.locale);
+ }
+ initEventCalendar(calendar, options);
+ }
+
+
+ };
+
+
+ // define the parameters with the default values of the function
+ $.fn.eventCalendar.defaults = {
+ eventsjson: 'js/events.json',
+ eventsLimit: 4,
+ locales: {
+ locale: "en",
+ txt_noEvents: "There are no events in this period",
+ txt_SpecificEvents_prev: "",
+ txt_SpecificEvents_after: "events:",
+ txt_next: "next",
+ txt_prev: "prev",
+ txt_NextEvents: "Next events:",
+ txt_GoToEventUrl: "See the event",
+ txt_loading: "loading..."
+ },
+ showDayAsWeeks: true,
+ startWeekOnMonday: true,
+ showDayNameInCalendar: true,
+ showDescription: false,
+ onlyOneDescription: true,
+ openEventInNewWindow: false,
+ eventsScrollable: false,
+ dateFormat: "D/MM/YYYY",
+ jsonDateFormat: 'timestamp', // you can use also "human" 'YYYY-MM-DD HH:MM:SS'
+ moveSpeed: 500, // speed of month move when you clic on a new date
+ moveOpacity: 0.15, // month and events fadeOut to this opacity
+ jsonData: "", // to load and inline json (not ajax calls)
+ cacheJson: true // if true plugin get a json only first time and after plugin filter events
+ // if false plugin get a new json on each date change
+ };
+
+ function initEventCalendar(that, options) {
+ var eventsOpts = $.extend({}, $.fn.eventCalendar.defaults, options);
+
+ // define global vars for the function
+ var flags = {
+ wrap: "",
+ directionLeftMove: "300",
+ eventsJson: {}
+ };
+
+ // each eventCalendar will execute this function
+ that.each(function(){
+
+ flags.wrap = $(this);
+ flags.wrap.addClass('eventCalendar-wrap').append("
"+eventsOpts.locales.txt_loading+" ");
+
+ if (eventsOpts.eventsScrollable) {
+ flags.wrap.find('.eventCalendar-list-content').addClass('scrollable');
+ }
+
+ setCalendarWidth(flags);
+ $(window).resize(function(){
+ setCalendarWidth(flags);
+ });
+ //flags.directionLeftMove = flags.wrap.width();
+
+ // show current month
+ dateSlider("current", flags, eventsOpts);
+
+ getEvents(flags, eventsOpts, eventsOpts.eventsLimit,false,false,false,false);
+
+ changeMonth(flags, eventsOpts);
+
+ flags.wrap.on('click','.eventCalendar-day a',function(e){
+ //flags.wrap.find('.eventCalendar-day a').live('click',function(e){
+ e.preventDefault();
+ var year = flags.wrap.attr('data-current-year'),
+ month = flags.wrap.attr('data-current-month'),
+ day = $(this).parent().attr('rel');
+
+ getEvents(flags, eventsOpts, false, year, month,day, "day");
+ });
+ flags.wrap.on('click','.eventCalendar-monthTitle', function(e){
+ //flags.wrap.find('.eventCalendar-monthTitle').live('click',function(e){
+ e.preventDefault();
+ var year = flags.wrap.attr('data-current-year'),
+ month = flags.wrap.attr('data-current-month');
+
+ getEvents(flags, eventsOpts, eventsOpts.eventsLimit, year, month,false, "month");
+ });
+ });
+
+ // show event description
+ flags.wrap.find('.eventCalendar-list').on('click','.eventCalendar-eventTitle',function(e){
+ //flags.wrap.find('.eventCalendar-list .eventCalendar-eventTitle').live('click',function(e){
+ if(!eventsOpts.showDescription) {
+ e.preventDefault();
+ var desc = $(this).parent().find('.eventCalendar-eventDesc');
+
+ if (!desc.find('a').size()) {
+ var eventUrl = $(this).attr('href');
+ var eventTarget = $(this).attr('target');
+
+ // create a button to go to event url
+ if(eventUrl){
+ desc.append(''+eventsOpts.locales.txt_GoToEventUrl+' ');
+ }
+
+ }
+
+ if ( desc.is(':visible') ) {
+ desc.slideUp();
+ } else {
+ if(eventsOpts.onlyOneDescription) {
+ flags.wrap.find('.eventCalendar-eventDesc').slideUp();
+ }
+ desc.slideDown();
+ }
+
+ }
+ });
+ }
+
+ function sortJson(a, b){
+ if ( typeof a.date === 'string' ) {
+ return a.date.toLowerCase() > b.date.toLowerCase() ? 1 : -1;
+ }
+ return a.date > b.date ? 1 : -1;
+ }
+
+ function dateSlider(show, flags, eventsOpts) {
+ var $eventsCalendarSlider = $("
"),
+ $eventsCalendarMonthWrap = $("
"),
+ $eventsCalendarTitle = $(""),
+ $eventsCalendarArrows = $("" + eventsOpts.locales.txt_prev + " " + eventsOpts.locales.txt_next + " ");
+ $eventsCalendarDaysList = $(""),
+ date = new Date();
+
+ if ( !flags.wrap.find('.eventCalendar-slider').length ) {
+ flags.wrap.prepend($eventsCalendarSlider);
+ $eventsCalendarSlider.append($eventsCalendarMonthWrap);
+ } else {
+ flags.wrap.find('.eventCalendar-slider').append($eventsCalendarMonthWrap);
+ }
+
+ flags.wrap.find('.eventCalendar-monthWrap.eventCalendar-currentMonth').removeClass('eventCalendar-currentMonth').addClass('eventCalendar-oldMonth');
+ $eventsCalendarMonthWrap.addClass('eventCalendar-currentMonth').append($eventsCalendarTitle, $eventsCalendarDaysList);
+
+
+
+ // if current show current month & day
+ if (show === "current") {
+ day = date.getDate();
+ $eventsCalendarSlider.append($eventsCalendarArrows);
+
+ } else {
+ date = new Date(flags.wrap.attr('data-current-year'),flags.wrap.attr('data-current-month'),1,0,0,0); // current visible month
+ day = 0; // not show current day in days list
+
+ moveOfMonth = 1;
+ if (show === "prev") {
+ moveOfMonth = -1;
+ }
+ date.setMonth( date.getMonth() + moveOfMonth );
+
+ var tmpDate = new Date();
+ if (date.getMonth() === tmpDate.getMonth()) {
+ day = tmpDate.getDate();
+ }
+
+ }
+
+ // get date portions
+ var year = date.getFullYear(), // year of the events
+ currentYear = new Date().getFullYear(), // current year
+ month = date.getMonth(), // 0-11
+ monthToShow = month + 1;
+
+ if (show != "current") {
+ // month change
+ getEvents(flags, eventsOpts, eventsOpts.eventsLimit, year, month,false, show);
+ }
+
+ flags.wrap.attr('data-current-month',month)
+ .attr('data-current-year',year);
+
+ // add current date info
+ moment.locale(eventsOpts.locales.locale);
+
+ var formatedDate = moment(year+" "+monthToShow, "YYYY MM").format("MMMM YYYY");
+ $eventsCalendarTitle.find('.eventCalendar-monthTitle').html(formatedDate);
+
+ // print all month days
+ var daysOnTheMonth = 32 - new Date(year, month, 32).getDate();
+ var daysList = [],
+ i;
+ if (eventsOpts.showDayAsWeeks) {
+ $eventsCalendarDaysList.addClass('eventCalendar-showAsWeek');
+
+ // show day name in top of calendar
+ if (eventsOpts.showDayNameInCalendar) {
+ $eventsCalendarDaysList.addClass('eventCalendar-showDayNames');
+
+ i = 0;
+ // if week start on monday
+ if (eventsOpts.startWeekOnMonday) {
+ i = 1;
+ }
+
+ for (; i < 7; i++) {
+ daysList.push('');
+
+ if (i === 6 && eventsOpts.startWeekOnMonday) {
+ // print sunday header
+ daysList.push('');
+ }
+
+ }
+ }
+
+ dt=new Date(year, month, 01);
+ var weekDay = dt.getDay(); // day of the week where month starts
+
+ if (eventsOpts.startWeekOnMonday) {
+ weekDay = dt.getDay() - 1;
+ }
+ if (weekDay < 0) { weekDay = 6; } // if -1 is because day starts on sunday(0) and week starts on monday
+
+ for (i = weekDay; i > 0; i--) {
+ daysList.push(' ');
+ }
+ }
+ for (dayCount = 1; dayCount <= daysOnTheMonth; dayCount++) {
+ var dayClass = "";
+
+ if (day > 0 && dayCount === day && year === currentYear) {
+ dayClass = "today";
+ }
+ daysList.push('' + dayCount + ' ');
+ }
+ $eventsCalendarDaysList.append(daysList.join(''));
+
+ $eventsCalendarSlider.css('height',$eventsCalendarMonthWrap.height()+'px');
+ }
+
+ function getEvents(flags, eventsOpts, limit, year, month, day, direction) {
+ limit = limit || 0;
+ year = year || '';
+ day = day || '';
+
+ // to avoid problem with january (month = 0)
+
+ if (typeof month != 'undefined') {
+ month = month;
+ } else {
+ month = '';
+ }
+
+ //var month = month || '';
+ flags.wrap.find('.eventCalendar-loading').fadeIn();
+
+ if (eventsOpts.jsonData) {
+ // user send a json in the plugin params
+ eventsOpts.cacheJson = true;
+
+ flags.eventsJson = eventsOpts.jsonData;
+ getEventsData(flags, eventsOpts, flags.eventsJson, limit, year, month, day, direction);
+
+ } else if (!eventsOpts.cacheJson || !direction) {
+ // first load: load json and save it to future filters
+ $.getJSON(eventsOpts.eventsjson + "?limit="+limit+"&year="+year+"&month="+month+"&day="+day, function(data) {
+ flags.eventsJson = data; // save data to future filters
+ getEventsData(flags, eventsOpts, flags.eventsJson, limit, year, month, day, direction);
+ }).error(function() {
+ showError("error getting json: ", flags.wrap);
+ });
+ } else {
+ // filter previus saved json
+ getEventsData(flags, eventsOpts, flags.eventsJson, limit, year, month, day, direction);
+ }
+
+ if (day > '') {
+ flags.wrap.find('.eventCalendar-current').removeClass('eventCalendar-current');
+ flags.wrap.find('#dayList_'+day).addClass('eventCalendar-current');
+ }
+ }
+
+ function getEventsData(flags, eventsOpts, data, limit, year, month, day, direction){
+ directionLeftMove = "-=" + flags.directionLeftMove;
+ eventContentHeight = "auto";
+
+ subtitle = flags.wrap.find('.eventCalendar-list-wrap .eventCalendar-subtitle');
+
+ if ( !direction ) {
+ // first load
+ subtitle.html(eventsOpts.locales.txt_NextEvents);
+ eventContentHeight = "auto";
+ directionLeftMove = "-=0";
+ } else {
+ var jsMonth = parseInt(month) + 1,
+ formatedDate;
+ moment.locale(eventsOpts.locales.locale);
+
+ if (day !== '') {
+ formatedDate = moment(year+" "+jsMonth+" "+day, "YYYY MM DD").format("LL");
+ subtitle.html(eventsOpts.locales.txt_SpecificEvents_prev + formatedDate + " " + eventsOpts.locales.txt_SpecificEvents_after);
+ //eventStringDate = moment(eventDate).format(eventsOpts.dateFormat);
+ } else {
+ formatedDate = moment(year+" "+jsMonth, "YYYY MM").format("MMMM");
+ subtitle.html(eventsOpts.locales.txt_SpecificEvents_prev + formatedDate + " " + eventsOpts.locales.txt_SpecificEvents_after);
+ }
+
+ if (direction === 'eventCalendar-prev') {
+ directionLeftMove = "+=" + flags.directionLeftMove;
+ } else if (direction === 'day' || direction === 'month') {
+ directionLeftMove = "+=0";
+ eventContentHeight = 0;
+ }
+ }
+
+ flags.wrap.find('.eventCalendar-list').animate({
+ opacity: eventsOpts.moveOpacity,
+ left: directionLeftMove,
+ height: eventContentHeight
+ }, eventsOpts.moveSpeed, function() {
+ flags.wrap.find('.eventCalendar-list').css({'left':0, 'height': 'auto'}).hide();
+ //wrap.find('.eventCalendar-list li').fadeIn();
+
+ var events = [];
+
+ data = $(data).sort(sortJson); // sort event by dates
+ // each event
+ if ( data.length ) {
+
+ // show or hide event description
+ var eventDescClass = '';
+ if(!eventsOpts.showDescription) {
+ eventDescClass = 'eventCalendar-hidden';
+ }
+ var eventLinkTarget = "_self";
+ if(eventsOpts.openEventInNewWindow) {
+ eventLinkTarget = '_target';
+ }
+
+ var i = 0;
+ $.each(data, function(key, event) {
+ var eventDateTime, eventDate, eventTime, eventYear, eventMonth, eventDay,
+ eventMonthToShow, eventHour, eventMinute, eventSeconds;
+ if (eventsOpts.jsonDateFormat == 'human') {
+ eventDateTime = event.date.split(" ");
+ eventDate = eventDateTime[0].split("-");
+ eventTime = eventDateTime[1].split(":");
+ eventYear = eventDate[0];
+ eventMonth = parseInt(eventDate[1]) - 1;
+ eventDay = parseInt(eventDate[2]);
+ //eventMonthToShow = eventMonth;
+ eventMonthToShow = parseInt(eventMonth) + 1;
+ eventHour = eventTime[0];
+ eventMinute = eventTime[1];
+ eventSeconds = eventTime[2];
+ eventDate = new Date(eventYear, eventMonth, eventDay, eventHour, eventMinute, eventSeconds);
+ } else {
+ eventDate = new Date(parseInt(event.date));
+ eventYear = eventDate.getFullYear();
+ eventMonth = eventDate.getMonth();
+ eventDay = eventDate.getDate();
+ eventMonthToShow = eventMonth + 1;
+ eventHour = eventDate.getHours();
+ eventMinute = eventDate.getMinutes();
+
+ }
+
+ if (parseInt(eventMinute) <= 9) {
+ eventMinute = "0" + parseInt(eventMinute);
+ }
+
+
+ if (limit === 0 || limit > i) {
+ // if month or day exist then only show matched events
+
+ if ((month === false || month == eventMonth) && (day === '' || day == eventDay) && (year === '' || year == eventYear) ) {
+ // if initial load then load only future events
+ if (month === false && eventDate < new Date()) {
+ } else {
+ moment.locale(eventsOpts.locales.locale);
+ //eventStringDate = eventDay + "/" + eventMonthToShow + "/" + eventYear;
+ eventStringDate = moment(eventDate).format(eventsOpts.dateFormat);
+ var eventTitle;
+
+ if (event.url) {
+ eventTitle = '' + event.title + ' ';
+ } else {
+ eventTitle = ''+event.title+' ';
+ }
+ events.push('' + eventStringDate + ' '+eventHour+":"+eventMinute+' '+eventTitle+'' + event.description + '
');
+ i++;
+ }
+ }
+ }
+
+ // add mark in the dayList to the days with events
+ if (eventYear == flags.wrap.attr('data-current-year') && eventMonth == flags.wrap.attr('data-current-month')) {
+ flags.wrap.find('.eventCalendar-currentMonth .eventCalendar-daysList #dayList_' + parseInt(eventDay)).addClass('eventCalendar-dayWithEvents');
+ }
+
+ });
+ }
+
+ // there is no events on this period
+ if (!events.length) {
+ events.push('' + eventsOpts.locales.txt_noEvents + '
');
+ }
+ flags.wrap.find('.eventCalendar-loading').hide();
+
+ flags.wrap.find('.eventCalendar-list')
+ .html(events.join(''));
+
+ flags.wrap.find('.eventCalendar-list').animate({
+ opacity: 1,
+ height: "toggle"
+ }, eventsOpts.moveSpeed);
+
+
+ });
+ setCalendarWidth(flags);
+ }
+
+ function changeMonth(flags, eventsOpts) {
+ flags.wrap.find('.eventCalendar-arrow').click(function(e){
+ e.preventDefault();
+ var lastMonthMove;
+
+ if ($(this).hasClass('eventCalendar-next')) {
+ dateSlider("next", flags, eventsOpts);
+ lastMonthMove = '-=' + flags.directionLeftMove;
+
+ } else {
+ dateSlider("prev", flags, eventsOpts);
+ lastMonthMove = '+=' + flags.directionLeftMove;
+ }
+
+ flags.wrap.find('.eventCalendar-monthWrap.eventCalendar-oldMonth').animate({
+ opacity: eventsOpts.moveOpacity,
+ left: lastMonthMove
+ }, eventsOpts.moveSpeed, function() {
+ flags.wrap.find('.eventCalendar-monthWrap.eventCalendar-oldMonth').remove();
+ });
+ });
+ }
+
+ function showError(msg, wrap) {
+ wrap.find('.eventCalendar-list-wrap').html(""+msg+" ");
+ }
+
+ function setCalendarWidth(flags){
+ // resize calendar width on window resize
+ flags.directionLeftMove = flags.wrap.width();
+ flags.wrap.find('.eventCalendar-monthWrap').width(flags.wrap.width() + 'px');
+
+ flags.wrap.find('.eventCalendar-list-wrap').width(flags.wrap.width() + 'px');
+
+ }
+
+
+})( jQuery );
+
diff --git a/unicalendar/js/jquery.eventCalendar.min.js b/unicalendar/js/jquery.eventCalendar.min.js
new file mode 100644
index 0000000..44d1d86
--- /dev/null
+++ b/unicalendar/js/jquery.eventCalendar.min.js
@@ -0,0 +1,13 @@
+/* =
+ jquery.eventCalendar.js
+ version: 0.68
+ date: 17-07-2015
+ author:
+ Jaime Fernandez (@vissit)
+ company:
+ Paradigma Tecnologico (@paradigmate)
+ url:
+ http://www.vissit.com/projects/eventCalendar/
+*/
+
+;!function(e){function t(t,a){var l=e.extend({},e.fn.eventCalendar.defaults,a),o={wrap:"",directionLeftMove:"300",eventsJson:{}};t.each(function(){o.wrap=e(this),o.wrap.addClass("eventCalendar-wrap").append("
"+l.locales.txt_loading+" "),l.eventsScrollable&&o.wrap.find(".eventCalendar-list-content").addClass("scrollable"),d(o),e(window).resize(function(){d(o)}),n("current",o,l),r(o,l,l.eventsLimit,!1,!1,!1,!1),s(o,l),o.wrap.on("click",".eventCalendar-day a",function(t){t.preventDefault();var a=o.wrap.attr("data-current-year"),n=o.wrap.attr("data-current-month"),s=e(this).parent().attr("rel");r(o,l,!1,a,n,s,"day")}),o.wrap.on("click",".eventCalendar-monthTitle",function(e){e.preventDefault();var t=o.wrap.attr("data-current-year"),a=o.wrap.attr("data-current-month");r(o,l,l.eventsLimit,t,a,!1,"month")})}),o.wrap.find(".eventCalendar-list").on("click",".eventCalendar-eventTitle",function(t){if(!l.showDescription){t.preventDefault();var a=e(this).parent().find(".eventCalendar-eventDesc");if(!a.find("a").size()){var n=e(this).attr("href"),r=e(this).attr("target");a.append(''+l.locales.txt_GoToEventUrl+" ")}a.is(":visible")?a.slideUp():(l.onlyOneDescription&&o.wrap.find(".eventCalendar-eventDesc").slideUp(),a.slideDown())}})}function a(e,t){return"string"==typeof e.date?e.date.toLowerCase()>t.date.toLowerCase()?1:-1:e.date>t.date?1:-1}function n(t,a,n){var l=e("
"),s=e("
"),o=e(""),d=e(""+n.locales.txt_prev+" "+n.locales.txt_next+" ");if($eventsCalendarDaysList=e(""),date=new Date,a.wrap.find(".eventCalendar-slider").length?a.wrap.find(".eventCalendar-slider").append(s):(a.wrap.prepend(l),l.append(s)),a.wrap.find(".eventCalendar-monthWrap.eventCalendar-currentMonth").removeClass("eventCalendar-currentMonth").addClass("eventCalendar-oldMonth"),s.addClass("eventCalendar-currentMonth").append(o,$eventsCalendarDaysList),"current"===t)day=date.getDate(),l.append(d);else{date=new Date(a.wrap.attr("data-current-year"),a.wrap.attr("data-current-month"),1,0,0,0),day=0,moveOfMonth=1,"prev"===t&&(moveOfMonth=-1),date.setMonth(date.getMonth()+moveOfMonth);var i=new Date;date.getMonth()===i.getMonth()&&(day=i.getDate())}var v=date.getFullYear(),c=(new Date).getFullYear(),p=date.getMonth(),f=p+1;"current"!=t&&r(a,n,n.eventsLimit,v,p,!1,t),a.wrap.attr("data-current-month",p).attr("data-current-year",v),moment.locale(n.locales.locale);var h=moment(v+" "+f,"YYYY MM").format("MMMM YYYY");o.find(".eventCalendar-monthTitle").html(h);var C,m=32-new Date(v,p,32).getDate(),u=[];if(n.showDayAsWeeks){if($eventsCalendarDaysList.addClass("eventCalendar-showAsWeek"),n.showDayNameInCalendar)for($eventsCalendarDaysList.addClass("eventCalendar-showDayNames"),C=0,n.startWeekOnMonday&&(C=1);7>C;C++)u.push('"),6===C&&n.startWeekOnMonday&&u.push('");dt=new Date(v,p,1);var w=dt.getDay();for(n.startWeekOnMonday&&(w=dt.getDay()-1),0>w&&(w=6),C=w;C>0;C--)u.push(' ')}for(dayCount=1;m>=dayCount;dayCount++){var y="";day>0&&dayCount===day&&v===c&&(y="today"),u.push(''+dayCount+" ")}$eventsCalendarDaysList.append(u.join("")),l.css("height",s.height()+"px")}function r(t,a,n,r,s,d,i){n=n||0,r=r||"",d=d||"",s="undefined"!=typeof s?s:"",t.wrap.find(".eventCalendar-loading").fadeIn(),a.jsonData?(a.cacheJson=!0,t.eventsJson=a.jsonData,l(t,a,t.eventsJson,n,r,s,d,i)):a.cacheJson&&i?l(t,a,t.eventsJson,n,r,s,d,i):e.getJSON(a.eventsjson+"?limit="+n+"&year="+r+"&month="+s+"&day="+d,function(e){t.eventsJson=e,l(t,a,t.eventsJson,n,r,s,d,i)}).error(function(){o("error getting json: ",t.wrap)}),d>""&&(t.wrap.find(".eventCalendar-current").removeClass("eventCalendar-current"),t.wrap.find("#dayList_"+d).addClass("eventCalendar-current"))}function l(t,n,r,l,s,o,i,v){if(directionLeftMove="-="+t.directionLeftMove,eventContentHeight="auto",subtitle=t.wrap.find(".eventCalendar-list-wrap .eventCalendar-subtitle"),v){var c,p=parseInt(o)+1;moment.locale(n.locales.locale),""!==i?(c=moment(s+" "+p+" "+i,"YYYY MM DD").format("LL"),subtitle.html(n.locales.txt_SpecificEvents_prev+c+" "+n.locales.txt_SpecificEvents_after)):(c=moment(s+" "+p,"YYYY MM").format("MMMM"),subtitle.html(n.locales.txt_SpecificEvents_prev+c+" "+n.locales.txt_SpecificEvents_after)),"eventCalendar-prev"===v?directionLeftMove="+="+t.directionLeftMove:("day"===v||"month"===v)&&(directionLeftMove="+=0",eventContentHeight=0)}else subtitle.html(n.locales.txt_NextEvents),eventContentHeight="auto",directionLeftMove="-=0";t.wrap.find(".eventCalendar-list").animate({opacity:n.moveOpacity,left:directionLeftMove,height:eventContentHeight},n.moveSpeed,function(){t.wrap.find(".eventCalendar-list").css({left:0,height:"auto"}).hide();var d=[];if(r=e(r).sort(a),r.length){var v="";n.showDescription||(v="eventCalendar-hidden");var c="_self";n.openEventInNewWindow&&(c="_target");var p=0;e.each(r,function(e,a){var r,f,h,C,m,u,w,y,g,D;if("human"==n.jsonDateFormat?(r=a.date.split(" "),f=r[0].split("-"),h=r[1].split(":"),C=f[0],m=parseInt(f[1])-1,u=parseInt(f[2]),w=parseInt(m)+1,y=h[0],g=h[1],D=h[2],f=new Date(C,m,u,y,g,D)):(f=new Date(parseInt(a.date)),C=f.getFullYear(),m=f.getMonth(),u=f.getDate(),w=m+1,y=f.getHours(),g=f.getMinutes()),parseInt(g)<=9&&(g="0"+parseInt(g)),(0===l||l>p)&&!(o!==!1&&o!=m||""!==i&&i!=u||""!==s&&s!=C))if(o===!1&&f'+a.title+"":''+a.title+" ",d.push(''+eventStringDate+" "+y+":"+g+" "+M+''+a.description+"
"),p++}C==t.wrap.attr("data-current-year")&&m==t.wrap.attr("data-current-month")&&t.wrap.find(".eventCalendar-currentMonth .eventCalendar-daysList #dayList_"+parseInt(u)).addClass("eventCalendar-dayWithEvents")})}d.length||d.push(''+n.locales.txt_noEvents+"
"),t.wrap.find(".eventCalendar-loading").hide(),t.wrap.find(".eventCalendar-list").html(d.join("")),t.wrap.find(".eventCalendar-list").animate({opacity:1,height:"toggle"},n.moveSpeed)}),d(t)}function s(t,a){t.wrap.find(".eventCalendar-arrow").click(function(r){r.preventDefault();var l;e(this).hasClass("eventCalendar-next")?(n("next",t,a),l="-="+t.directionLeftMove):(n("prev",t,a),l="+="+t.directionLeftMove),t.wrap.find(".eventCalendar-monthWrap.eventCalendar-oldMonth").animate({opacity:a.moveOpacity,left:l},a.moveSpeed,function(){t.wrap.find(".eventCalendar-monthWrap.eventCalendar-oldMonth").remove()})})}function o(e,t){t.find(".eventCalendar-list-wrap").html(""+e+" ")}function d(e){e.directionLeftMove=e.wrap.width(),e.wrap.find(".eventCalendar-monthWrap").width(e.wrap.width()+"px"),e.wrap.find(".eventCalendar-list-wrap").width(e.wrap.width()+"px")}e.fn.eventCalendar=function(a){var n=this;a.locales&&"string"==typeof a.locales?e.getJSON(a.locales,function(r){a.locales=e.extend({},e.fn.eventCalendar.defaults.locales,r),moment.locale(r.locale,a.locales.moment),moment.locale(r.locale),t(n,a)}).error(function(){o("error getting locale json",e(this))}):(a.locales&&a.locales.locale&&(a.locales=e.extend({},e.fn.eventCalendar.defaults.locales,a.locales),moment.locale(a.locales.locale,a.locales.moment),moment.locale(a.locales.locale)),t(n,a))},e.fn.eventCalendar.defaults={eventsjson:"js/events.json",eventsLimit:4,locales:{locale:"en",txt_noEvents:"There are no events in this period",txt_SpecificEvents_prev:"",txt_SpecificEvents_after:"events:",txt_next:"next",txt_prev:"prev",txt_NextEvents:"Next events:",txt_GoToEventUrl:"See the event",txt_loading:"loading..."},showDayAsWeeks:!0,startWeekOnMonday:!0,showDayNameInCalendar:!0,showDescription:!1,onlyOneDescription:!0,openEventInNewWindow:!1,eventsScrollable:!1,dateFormat:"D/MM/YYYY",jsonDateFormat:"timestamp",moveSpeed:500,moveOpacity:.15,jsonData:"",cacheJson:!0}}(jQuery);
\ No newline at end of file
diff --git a/unicalendar/js/moment.js b/unicalendar/js/moment.js
new file mode 100644
index 0000000..96922ff
--- /dev/null
+++ b/unicalendar/js/moment.js
@@ -0,0 +1,2856 @@
+//! moment.js
+//! version : 2.8.3
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
+//! license : MIT
+//! momentjs.com
+
+(function (undefined) {
+ /************************************
+ Constants
+ ************************************/
+
+ var moment,
+ VERSION = '2.8.3',
+ // the global-scope this is NOT the global object in Node.js
+ globalScope = typeof global !== 'undefined' ? global : this,
+ oldGlobalMoment,
+ round = Math.round,
+ hasOwnProperty = Object.prototype.hasOwnProperty,
+ i,
+
+ YEAR = 0,
+ MONTH = 1,
+ DATE = 2,
+ HOUR = 3,
+ MINUTE = 4,
+ SECOND = 5,
+ MILLISECOND = 6,
+
+ // internal storage for locale config files
+ locales = {},
+
+ // extra moment internal properties (plugins register props here)
+ momentProperties = [],
+
+ // check for nodeJS
+ hasModule = (typeof module !== 'undefined' && module.exports),
+
+ // ASP.NET json date format regex
+ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
+ aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
+
+ // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+ // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+ isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
+
+ // format tokens
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
+ localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
+
+ // parsing token regexes
+ parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
+ parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
+ parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
+ parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
+ parseTokenDigits = /\d+/, // nonzero number of digits
+ parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
+ parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
+ parseTokenT = /T/i, // T (ISO separator)
+ parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
+ parseTokenOrdinal = /\d{1,2}/,
+
+ //strict parsing regexes
+ parseTokenOneDigit = /\d/, // 0 - 9
+ parseTokenTwoDigits = /\d\d/, // 00 - 99
+ parseTokenThreeDigits = /\d{3}/, // 000 - 999
+ parseTokenFourDigits = /\d{4}/, // 0000 - 9999
+ parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
+ parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
+
+ // iso 8601 regex
+ // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
+ isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
+
+ isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
+
+ isoDates = [
+ ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
+ ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
+ ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
+ ['GGGG-[W]WW', /\d{4}-W\d{2}/],
+ ['YYYY-DDD', /\d{4}-\d{3}/]
+ ],
+
+ // iso time formats and regexes
+ isoTimes = [
+ ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
+ ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
+ ['HH:mm', /(T| )\d\d:\d\d/],
+ ['HH', /(T| )\d\d/]
+ ],
+
+ // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-15', '30']
+ parseTimezoneChunker = /([\+\-]|\d\d)/gi,
+
+ // getter and setter names
+ proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
+ unitMillisecondFactors = {
+ 'Milliseconds' : 1,
+ 'Seconds' : 1e3,
+ 'Minutes' : 6e4,
+ 'Hours' : 36e5,
+ 'Days' : 864e5,
+ 'Months' : 2592e6,
+ 'Years' : 31536e6
+ },
+
+ unitAliases = {
+ ms : 'millisecond',
+ s : 'second',
+ m : 'minute',
+ h : 'hour',
+ d : 'day',
+ D : 'date',
+ w : 'week',
+ W : 'isoWeek',
+ M : 'month',
+ Q : 'quarter',
+ y : 'year',
+ DDD : 'dayOfYear',
+ e : 'weekday',
+ E : 'isoWeekday',
+ gg: 'weekYear',
+ GG: 'isoWeekYear'
+ },
+
+ camelFunctions = {
+ dayofyear : 'dayOfYear',
+ isoweekday : 'isoWeekday',
+ isoweek : 'isoWeek',
+ weekyear : 'weekYear',
+ isoweekyear : 'isoWeekYear'
+ },
+
+ // format function strings
+ formatFunctions = {},
+
+ // default relative time thresholds
+ relativeTimeThresholds = {
+ s: 45, // seconds to minute
+ m: 45, // minutes to hour
+ h: 22, // hours to day
+ d: 26, // days to month
+ M: 11 // months to year
+ },
+
+ // tokens to ordinalize and pad
+ ordinalizeTokens = 'DDD w W M D d'.split(' '),
+ paddedTokens = 'M D H h m s w W'.split(' '),
+
+ formatTokenFunctions = {
+ M : function () {
+ return this.month() + 1;
+ },
+ MMM : function (format) {
+ return this.localeData().monthsShort(this, format);
+ },
+ MMMM : function (format) {
+ return this.localeData().months(this, format);
+ },
+ D : function () {
+ return this.date();
+ },
+ DDD : function () {
+ return this.dayOfYear();
+ },
+ d : function () {
+ return this.day();
+ },
+ dd : function (format) {
+ return this.localeData().weekdaysMin(this, format);
+ },
+ ddd : function (format) {
+ return this.localeData().weekdaysShort(this, format);
+ },
+ dddd : function (format) {
+ return this.localeData().weekdays(this, format);
+ },
+ w : function () {
+ return this.week();
+ },
+ W : function () {
+ return this.isoWeek();
+ },
+ YY : function () {
+ return leftZeroFill(this.year() % 100, 2);
+ },
+ YYYY : function () {
+ return leftZeroFill(this.year(), 4);
+ },
+ YYYYY : function () {
+ return leftZeroFill(this.year(), 5);
+ },
+ YYYYYY : function () {
+ var y = this.year(), sign = y >= 0 ? '+' : '-';
+ return sign + leftZeroFill(Math.abs(y), 6);
+ },
+ gg : function () {
+ return leftZeroFill(this.weekYear() % 100, 2);
+ },
+ gggg : function () {
+ return leftZeroFill(this.weekYear(), 4);
+ },
+ ggggg : function () {
+ return leftZeroFill(this.weekYear(), 5);
+ },
+ GG : function () {
+ return leftZeroFill(this.isoWeekYear() % 100, 2);
+ },
+ GGGG : function () {
+ return leftZeroFill(this.isoWeekYear(), 4);
+ },
+ GGGGG : function () {
+ return leftZeroFill(this.isoWeekYear(), 5);
+ },
+ e : function () {
+ return this.weekday();
+ },
+ E : function () {
+ return this.isoWeekday();
+ },
+ a : function () {
+ return this.localeData().meridiem(this.hours(), this.minutes(), true);
+ },
+ A : function () {
+ return this.localeData().meridiem(this.hours(), this.minutes(), false);
+ },
+ H : function () {
+ return this.hours();
+ },
+ h : function () {
+ return this.hours() % 12 || 12;
+ },
+ m : function () {
+ return this.minutes();
+ },
+ s : function () {
+ return this.seconds();
+ },
+ S : function () {
+ return toInt(this.milliseconds() / 100);
+ },
+ SS : function () {
+ return leftZeroFill(toInt(this.milliseconds() / 10), 2);
+ },
+ SSS : function () {
+ return leftZeroFill(this.milliseconds(), 3);
+ },
+ SSSS : function () {
+ return leftZeroFill(this.milliseconds(), 3);
+ },
+ Z : function () {
+ var a = -this.zone(),
+ b = '+';
+ if (a < 0) {
+ a = -a;
+ b = '-';
+ }
+ return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2);
+ },
+ ZZ : function () {
+ var a = -this.zone(),
+ b = '+';
+ if (a < 0) {
+ a = -a;
+ b = '-';
+ }
+ return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
+ },
+ z : function () {
+ return this.zoneAbbr();
+ },
+ zz : function () {
+ return this.zoneName();
+ },
+ X : function () {
+ return this.unix();
+ },
+ Q : function () {
+ return this.quarter();
+ }
+ },
+
+ deprecations = {},
+
+ lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
+
+ // Pick the first defined of two or three arguments. dfl comes from
+ // default.
+ function dfl(a, b, c) {
+ switch (arguments.length) {
+ case 2: return a != null ? a : b;
+ case 3: return a != null ? a : b != null ? b : c;
+ default: throw new Error('Implement me');
+ }
+ }
+
+ function hasOwnProp(a, b) {
+ return hasOwnProperty.call(a, b);
+ }
+
+ function defaultParsingFlags() {
+ // We need to deep clone this object, and es5 standard is not very
+ // helpful.
+ return {
+ empty : false,
+ unusedTokens : [],
+ unusedInput : [],
+ overflow : -2,
+ charsLeftOver : 0,
+ nullInput : false,
+ invalidMonth : null,
+ invalidFormat : false,
+ userInvalidated : false,
+ iso: false
+ };
+ }
+
+ function printMsg(msg) {
+ if (moment.suppressDeprecationWarnings === false &&
+ typeof console !== 'undefined' && console.warn) {
+ console.warn('Deprecation warning: ' + msg);
+ }
+ }
+
+ function deprecate(msg, fn) {
+ var firstTime = true;
+ return extend(function () {
+ if (firstTime) {
+ printMsg(msg);
+ firstTime = false;
+ }
+ return fn.apply(this, arguments);
+ }, fn);
+ }
+
+ function deprecateSimple(name, msg) {
+ if (!deprecations[name]) {
+ printMsg(msg);
+ deprecations[name] = true;
+ }
+ }
+
+ function padToken(func, count) {
+ return function (a) {
+ return leftZeroFill(func.call(this, a), count);
+ };
+ }
+ function ordinalizeToken(func, period) {
+ return function (a) {
+ return this.localeData().ordinal(func.call(this, a), period);
+ };
+ }
+
+ while (ordinalizeTokens.length) {
+ i = ordinalizeTokens.pop();
+ formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
+ }
+ while (paddedTokens.length) {
+ i = paddedTokens.pop();
+ formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
+ }
+ formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
+
+
+ /************************************
+ Constructors
+ ************************************/
+
+ function Locale() {
+ }
+
+ // Moment prototype object
+ function Moment(config, skipOverflow) {
+ if (skipOverflow !== false) {
+ checkOverflow(config);
+ }
+ copyConfig(this, config);
+ this._d = new Date(+config._d);
+ }
+
+ // Duration Constructor
+ function Duration(duration) {
+ var normalizedInput = normalizeObjectUnits(duration),
+ years = normalizedInput.year || 0,
+ quarters = normalizedInput.quarter || 0,
+ months = normalizedInput.month || 0,
+ weeks = normalizedInput.week || 0,
+ days = normalizedInput.day || 0,
+ hours = normalizedInput.hour || 0,
+ minutes = normalizedInput.minute || 0,
+ seconds = normalizedInput.second || 0,
+ milliseconds = normalizedInput.millisecond || 0;
+
+ // representation for dateAddRemove
+ this._milliseconds = +milliseconds +
+ seconds * 1e3 + // 1000
+ minutes * 6e4 + // 1000 * 60
+ hours * 36e5; // 1000 * 60 * 60
+ // Because of dateAddRemove treats 24 hours as different from a
+ // day when working around DST, we need to store them separately
+ this._days = +days +
+ weeks * 7;
+ // It is impossible translate months into days without knowing
+ // which months you are are talking about, so we have to store
+ // it separately.
+ this._months = +months +
+ quarters * 3 +
+ years * 12;
+
+ this._data = {};
+
+ this._locale = moment.localeData();
+
+ this._bubble();
+ }
+
+ /************************************
+ Helpers
+ ************************************/
+
+
+ function extend(a, b) {
+ for (var i in b) {
+ if (hasOwnProp(b, i)) {
+ a[i] = b[i];
+ }
+ }
+
+ if (hasOwnProp(b, 'toString')) {
+ a.toString = b.toString;
+ }
+
+ if (hasOwnProp(b, 'valueOf')) {
+ a.valueOf = b.valueOf;
+ }
+
+ return a;
+ }
+
+ function copyConfig(to, from) {
+ var i, prop, val;
+
+ if (typeof from._isAMomentObject !== 'undefined') {
+ to._isAMomentObject = from._isAMomentObject;
+ }
+ if (typeof from._i !== 'undefined') {
+ to._i = from._i;
+ }
+ if (typeof from._f !== 'undefined') {
+ to._f = from._f;
+ }
+ if (typeof from._l !== 'undefined') {
+ to._l = from._l;
+ }
+ if (typeof from._strict !== 'undefined') {
+ to._strict = from._strict;
+ }
+ if (typeof from._tzm !== 'undefined') {
+ to._tzm = from._tzm;
+ }
+ if (typeof from._isUTC !== 'undefined') {
+ to._isUTC = from._isUTC;
+ }
+ if (typeof from._offset !== 'undefined') {
+ to._offset = from._offset;
+ }
+ if (typeof from._pf !== 'undefined') {
+ to._pf = from._pf;
+ }
+ if (typeof from._locale !== 'undefined') {
+ to._locale = from._locale;
+ }
+
+ if (momentProperties.length > 0) {
+ for (i in momentProperties) {
+ prop = momentProperties[i];
+ val = from[prop];
+ if (typeof val !== 'undefined') {
+ to[prop] = val;
+ }
+ }
+ }
+
+ return to;
+ }
+
+ function absRound(number) {
+ if (number < 0) {
+ return Math.ceil(number);
+ } else {
+ return Math.floor(number);
+ }
+ }
+
+ // left zero fill a number
+ // see http://jsperf.com/left-zero-filling for performance comparison
+ function leftZeroFill(number, targetLength, forceSign) {
+ var output = '' + Math.abs(number),
+ sign = number >= 0;
+
+ while (output.length < targetLength) {
+ output = '0' + output;
+ }
+ return (sign ? (forceSign ? '+' : '') : '-') + output;
+ }
+
+ function positiveMomentsDifference(base, other) {
+ var res = {milliseconds: 0, months: 0};
+
+ res.months = other.month() - base.month() +
+ (other.year() - base.year()) * 12;
+ if (base.clone().add(res.months, 'M').isAfter(other)) {
+ --res.months;
+ }
+
+ res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
+
+ return res;
+ }
+
+ function momentsDifference(base, other) {
+ var res;
+ other = makeAs(other, base);
+ if (base.isBefore(other)) {
+ res = positiveMomentsDifference(base, other);
+ } else {
+ res = positiveMomentsDifference(other, base);
+ res.milliseconds = -res.milliseconds;
+ res.months = -res.months;
+ }
+
+ return res;
+ }
+
+ // TODO: remove 'name' arg after deprecation is removed
+ function createAdder(direction, name) {
+ return function (val, period) {
+ var dur, tmp;
+ //invert the arguments, but complain about it
+ if (period !== null && !isNaN(+period)) {
+ deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
+ tmp = val; val = period; period = tmp;
+ }
+
+ val = typeof val === 'string' ? +val : val;
+ dur = moment.duration(val, period);
+ addOrSubtractDurationFromMoment(this, dur, direction);
+ return this;
+ };
+ }
+
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
+ var milliseconds = duration._milliseconds,
+ days = duration._days,
+ months = duration._months;
+ updateOffset = updateOffset == null ? true : updateOffset;
+
+ if (milliseconds) {
+ mom._d.setTime(+mom._d + milliseconds * isAdding);
+ }
+ if (days) {
+ rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
+ }
+ if (months) {
+ rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
+ }
+ if (updateOffset) {
+ moment.updateOffset(mom, days || months);
+ }
+ }
+
+ // check if is an array
+ function isArray(input) {
+ return Object.prototype.toString.call(input) === '[object Array]';
+ }
+
+ function isDate(input) {
+ return Object.prototype.toString.call(input) === '[object Date]' ||
+ input instanceof Date;
+ }
+
+ // compare two arrays, return the number of differences
+ function compareArrays(array1, array2, dontConvert) {
+ var len = Math.min(array1.length, array2.length),
+ lengthDiff = Math.abs(array1.length - array2.length),
+ diffs = 0,
+ i;
+ for (i = 0; i < len; i++) {
+ if ((dontConvert && array1[i] !== array2[i]) ||
+ (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
+ diffs++;
+ }
+ }
+ return diffs + lengthDiff;
+ }
+
+ function normalizeUnits(units) {
+ if (units) {
+ var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
+ units = unitAliases[units] || camelFunctions[lowered] || lowered;
+ }
+ return units;
+ }
+
+ function normalizeObjectUnits(inputObject) {
+ var normalizedInput = {},
+ normalizedProp,
+ prop;
+
+ for (prop in inputObject) {
+ if (hasOwnProp(inputObject, prop)) {
+ normalizedProp = normalizeUnits(prop);
+ if (normalizedProp) {
+ normalizedInput[normalizedProp] = inputObject[prop];
+ }
+ }
+ }
+
+ return normalizedInput;
+ }
+
+ function makeList(field) {
+ var count, setter;
+
+ if (field.indexOf('week') === 0) {
+ count = 7;
+ setter = 'day';
+ }
+ else if (field.indexOf('month') === 0) {
+ count = 12;
+ setter = 'month';
+ }
+ else {
+ return;
+ }
+
+ moment[field] = function (format, index) {
+ var i, getter,
+ method = moment._locale[field],
+ results = [];
+
+ if (typeof format === 'number') {
+ index = format;
+ format = undefined;
+ }
+
+ getter = function (i) {
+ var m = moment().utc().set(setter, i);
+ return method.call(moment._locale, m, format || '');
+ };
+
+ if (index != null) {
+ return getter(index);
+ }
+ else {
+ for (i = 0; i < count; i++) {
+ results.push(getter(i));
+ }
+ return results;
+ }
+ };
+ }
+
+ function toInt(argumentForCoercion) {
+ var coercedNumber = +argumentForCoercion,
+ value = 0;
+
+ if (coercedNumber !== 0 && isFinite(coercedNumber)) {
+ if (coercedNumber >= 0) {
+ value = Math.floor(coercedNumber);
+ } else {
+ value = Math.ceil(coercedNumber);
+ }
+ }
+
+ return value;
+ }
+
+ function daysInMonth(year, month) {
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+ }
+
+ function weeksInYear(year, dow, doy) {
+ return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
+ }
+
+ function daysInYear(year) {
+ return isLeapYear(year) ? 366 : 365;
+ }
+
+ function isLeapYear(year) {
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+ }
+
+ function checkOverflow(m) {
+ var overflow;
+ if (m._a && m._pf.overflow === -2) {
+ overflow =
+ m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
+ m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
+ m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
+ m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
+ m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
+ m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
+ -1;
+
+ if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
+ overflow = DATE;
+ }
+
+ m._pf.overflow = overflow;
+ }
+ }
+
+ function isValid(m) {
+ if (m._isValid == null) {
+ m._isValid = !isNaN(m._d.getTime()) &&
+ m._pf.overflow < 0 &&
+ !m._pf.empty &&
+ !m._pf.invalidMonth &&
+ !m._pf.nullInput &&
+ !m._pf.invalidFormat &&
+ !m._pf.userInvalidated;
+
+ if (m._strict) {
+ m._isValid = m._isValid &&
+ m._pf.charsLeftOver === 0 &&
+ m._pf.unusedTokens.length === 0;
+ }
+ }
+ return m._isValid;
+ }
+
+ function normalizeLocale(key) {
+ return key ? key.toLowerCase().replace('_', '-') : key;
+ }
+
+ // pick the locale from the array
+ // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+ // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+ function chooseLocale(names) {
+ var i = 0, j, next, locale, split;
+
+ while (i < names.length) {
+ split = normalizeLocale(names[i]).split('-');
+ j = split.length;
+ next = normalizeLocale(names[i + 1]);
+ next = next ? next.split('-') : null;
+ while (j > 0) {
+ locale = loadLocale(split.slice(0, j).join('-'));
+ if (locale) {
+ return locale;
+ }
+ if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
+ //the next array item is better than a shallower substring of this one
+ break;
+ }
+ j--;
+ }
+ i++;
+ }
+ return null;
+ }
+
+ function loadLocale(name) {
+ var oldLocale = null;
+ if (!locales[name] && hasModule) {
+ try {
+ oldLocale = moment.locale();
+ require('./locale/' + name);
+ // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales
+ moment.locale(oldLocale);
+ } catch (e) { }
+ }
+ return locales[name];
+ }
+
+ // Return a moment from input, that is local/utc/zone equivalent to model.
+ function makeAs(input, model) {
+ return model._isUTC ? moment(input).zone(model._offset || 0) :
+ moment(input).local();
+ }
+
+ /************************************
+ Locale
+ ************************************/
+
+
+ extend(Locale.prototype, {
+
+ set : function (config) {
+ var prop, i;
+ for (i in config) {
+ prop = config[i];
+ if (typeof prop === 'function') {
+ this[i] = prop;
+ } else {
+ this['_' + i] = prop;
+ }
+ }
+ },
+
+ _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
+ months : function (m) {
+ return this._months[m.month()];
+ },
+
+ _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
+ monthsShort : function (m) {
+ return this._monthsShort[m.month()];
+ },
+
+ monthsParse : function (monthName) {
+ var i, mom, regex;
+
+ if (!this._monthsParse) {
+ this._monthsParse = [];
+ }
+
+ for (i = 0; i < 12; i++) {
+ // make the regex if we don't have it already
+ if (!this._monthsParse[i]) {
+ mom = moment.utc([2000, i]);
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (this._monthsParse[i].test(monthName)) {
+ return i;
+ }
+ }
+ },
+
+ _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
+ weekdays : function (m) {
+ return this._weekdays[m.day()];
+ },
+
+ _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
+ weekdaysShort : function (m) {
+ return this._weekdaysShort[m.day()];
+ },
+
+ _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
+ weekdaysMin : function (m) {
+ return this._weekdaysMin[m.day()];
+ },
+
+ weekdaysParse : function (weekdayName) {
+ var i, mom, regex;
+
+ if (!this._weekdaysParse) {
+ this._weekdaysParse = [];
+ }
+
+ for (i = 0; i < 7; i++) {
+ // make the regex if we don't have it already
+ if (!this._weekdaysParse[i]) {
+ mom = moment([2000, 1]).day(i);
+ regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
+ this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (this._weekdaysParse[i].test(weekdayName)) {
+ return i;
+ }
+ }
+ },
+
+ _longDateFormat : {
+ LT : 'h:mm A',
+ L : 'MM/DD/YYYY',
+ LL : 'MMMM D, YYYY',
+ LLL : 'MMMM D, YYYY LT',
+ LLLL : 'dddd, MMMM D, YYYY LT'
+ },
+ longDateFormat : function (key) {
+ var output = this._longDateFormat[key];
+ if (!output && this._longDateFormat[key.toUpperCase()]) {
+ output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
+ return val.slice(1);
+ });
+ this._longDateFormat[key] = output;
+ }
+ return output;
+ },
+
+ isPM : function (input) {
+ // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+ // Using charAt should be more compatible.
+ return ((input + '').toLowerCase().charAt(0) === 'p');
+ },
+
+ _meridiemParse : /[ap]\.?m?\.?/i,
+ meridiem : function (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'pm' : 'PM';
+ } else {
+ return isLower ? 'am' : 'AM';
+ }
+ },
+
+ _calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ calendar : function (key, mom) {
+ var output = this._calendar[key];
+ return typeof output === 'function' ? output.apply(mom) : output;
+ },
+
+ _relativeTime : {
+ future : 'in %s',
+ past : '%s ago',
+ s : 'a few seconds',
+ m : 'a minute',
+ mm : '%d minutes',
+ h : 'an hour',
+ hh : '%d hours',
+ d : 'a day',
+ dd : '%d days',
+ M : 'a month',
+ MM : '%d months',
+ y : 'a year',
+ yy : '%d years'
+ },
+
+ relativeTime : function (number, withoutSuffix, string, isFuture) {
+ var output = this._relativeTime[string];
+ return (typeof output === 'function') ?
+ output(number, withoutSuffix, string, isFuture) :
+ output.replace(/%d/i, number);
+ },
+
+ pastFuture : function (diff, output) {
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+ return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
+ },
+
+ ordinal : function (number) {
+ return this._ordinal.replace('%d', number);
+ },
+ _ordinal : '%d',
+
+ preparse : function (string) {
+ return string;
+ },
+
+ postformat : function (string) {
+ return string;
+ },
+
+ week : function (mom) {
+ return weekOfYear(mom, this._week.dow, this._week.doy).week;
+ },
+
+ _week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ },
+
+ _invalidDate: 'Invalid date',
+ invalidDate: function () {
+ return this._invalidDate;
+ }
+ });
+
+ /************************************
+ Formatting
+ ************************************/
+
+
+ function removeFormattingTokens(input) {
+ if (input.match(/\[[\s\S]/)) {
+ return input.replace(/^\[|\]$/g, '');
+ }
+ return input.replace(/\\/g, '');
+ }
+
+ function makeFormatFunction(format) {
+ var array = format.match(formattingTokens), i, length;
+
+ for (i = 0, length = array.length; i < length; i++) {
+ if (formatTokenFunctions[array[i]]) {
+ array[i] = formatTokenFunctions[array[i]];
+ } else {
+ array[i] = removeFormattingTokens(array[i]);
+ }
+ }
+
+ return function (mom) {
+ var output = '';
+ for (i = 0; i < length; i++) {
+ output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
+ }
+ return output;
+ };
+ }
+
+ // format date using native date object
+ function formatMoment(m, format) {
+ if (!m.isValid()) {
+ return m.localeData().invalidDate();
+ }
+
+ format = expandFormat(format, m.localeData());
+
+ if (!formatFunctions[format]) {
+ formatFunctions[format] = makeFormatFunction(format);
+ }
+
+ return formatFunctions[format](m);
+ }
+
+ function expandFormat(format, locale) {
+ var i = 5;
+
+ function replaceLongDateFormatTokens(input) {
+ return locale.longDateFormat(input) || input;
+ }
+
+ localFormattingTokens.lastIndex = 0;
+ while (i >= 0 && localFormattingTokens.test(format)) {
+ format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+ localFormattingTokens.lastIndex = 0;
+ i -= 1;
+ }
+
+ return format;
+ }
+
+
+ /************************************
+ Parsing
+ ************************************/
+
+
+ // get the regex to find the next token
+ function getParseRegexForToken(token, config) {
+ var a, strict = config._strict;
+ switch (token) {
+ case 'Q':
+ return parseTokenOneDigit;
+ case 'DDDD':
+ return parseTokenThreeDigits;
+ case 'YYYY':
+ case 'GGGG':
+ case 'gggg':
+ return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
+ case 'Y':
+ case 'G':
+ case 'g':
+ return parseTokenSignedNumber;
+ case 'YYYYYY':
+ case 'YYYYY':
+ case 'GGGGG':
+ case 'ggggg':
+ return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
+ case 'S':
+ if (strict) {
+ return parseTokenOneDigit;
+ }
+ /* falls through */
+ case 'SS':
+ if (strict) {
+ return parseTokenTwoDigits;
+ }
+ /* falls through */
+ case 'SSS':
+ if (strict) {
+ return parseTokenThreeDigits;
+ }
+ /* falls through */
+ case 'DDD':
+ return parseTokenOneToThreeDigits;
+ case 'MMM':
+ case 'MMMM':
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ return parseTokenWord;
+ case 'a':
+ case 'A':
+ return config._locale._meridiemParse;
+ case 'X':
+ return parseTokenTimestampMs;
+ case 'Z':
+ case 'ZZ':
+ return parseTokenTimezone;
+ case 'T':
+ return parseTokenT;
+ case 'SSSS':
+ return parseTokenDigits;
+ case 'MM':
+ case 'DD':
+ case 'YY':
+ case 'GG':
+ case 'gg':
+ case 'HH':
+ case 'hh':
+ case 'mm':
+ case 'ss':
+ case 'ww':
+ case 'WW':
+ return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
+ case 'M':
+ case 'D':
+ case 'd':
+ case 'H':
+ case 'h':
+ case 'm':
+ case 's':
+ case 'w':
+ case 'W':
+ case 'e':
+ case 'E':
+ return parseTokenOneOrTwoDigits;
+ case 'Do':
+ return parseTokenOrdinal;
+ default :
+ a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i'));
+ return a;
+ }
+ }
+
+ function timezoneMinutesFromString(string) {
+ string = string || '';
+ var possibleTzMatches = (string.match(parseTokenTimezone) || []),
+ tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
+ parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
+ minutes = +(parts[1] * 60) + toInt(parts[2]);
+
+ return parts[0] === '+' ? -minutes : minutes;
+ }
+
+ // function to convert string input to date
+ function addTimeToArrayFromToken(token, input, config) {
+ var a, datePartArray = config._a;
+
+ switch (token) {
+ // QUARTER
+ case 'Q':
+ if (input != null) {
+ datePartArray[MONTH] = (toInt(input) - 1) * 3;
+ }
+ break;
+ // MONTH
+ case 'M' : // fall through to MM
+ case 'MM' :
+ if (input != null) {
+ datePartArray[MONTH] = toInt(input) - 1;
+ }
+ break;
+ case 'MMM' : // fall through to MMMM
+ case 'MMMM' :
+ a = config._locale.monthsParse(input);
+ // if we didn't find a month name, mark the date as invalid.
+ if (a != null) {
+ datePartArray[MONTH] = a;
+ } else {
+ config._pf.invalidMonth = input;
+ }
+ break;
+ // DAY OF MONTH
+ case 'D' : // fall through to DD
+ case 'DD' :
+ if (input != null) {
+ datePartArray[DATE] = toInt(input);
+ }
+ break;
+ case 'Do' :
+ if (input != null) {
+ datePartArray[DATE] = toInt(parseInt(input, 10));
+ }
+ break;
+ // DAY OF YEAR
+ case 'DDD' : // fall through to DDDD
+ case 'DDDD' :
+ if (input != null) {
+ config._dayOfYear = toInt(input);
+ }
+
+ break;
+ // YEAR
+ case 'YY' :
+ datePartArray[YEAR] = moment.parseTwoDigitYear(input);
+ break;
+ case 'YYYY' :
+ case 'YYYYY' :
+ case 'YYYYYY' :
+ datePartArray[YEAR] = toInt(input);
+ break;
+ // AM / PM
+ case 'a' : // fall through to A
+ case 'A' :
+ config._isPm = config._locale.isPM(input);
+ break;
+ // 24 HOUR
+ case 'H' : // fall through to hh
+ case 'HH' : // fall through to hh
+ case 'h' : // fall through to hh
+ case 'hh' :
+ datePartArray[HOUR] = toInt(input);
+ break;
+ // MINUTE
+ case 'm' : // fall through to mm
+ case 'mm' :
+ datePartArray[MINUTE] = toInt(input);
+ break;
+ // SECOND
+ case 's' : // fall through to ss
+ case 'ss' :
+ datePartArray[SECOND] = toInt(input);
+ break;
+ // MILLISECOND
+ case 'S' :
+ case 'SS' :
+ case 'SSS' :
+ case 'SSSS' :
+ datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
+ break;
+ // UNIX TIMESTAMP WITH MS
+ case 'X':
+ config._d = new Date(parseFloat(input) * 1000);
+ break;
+ // TIMEZONE
+ case 'Z' : // fall through to ZZ
+ case 'ZZ' :
+ config._useUTC = true;
+ config._tzm = timezoneMinutesFromString(input);
+ break;
+ // WEEKDAY - human
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ a = config._locale.weekdaysParse(input);
+ // if we didn't get a weekday name, mark the date as invalid
+ if (a != null) {
+ config._w = config._w || {};
+ config._w['d'] = a;
+ } else {
+ config._pf.invalidWeekday = input;
+ }
+ break;
+ // WEEK, WEEK DAY - numeric
+ case 'w':
+ case 'ww':
+ case 'W':
+ case 'WW':
+ case 'd':
+ case 'e':
+ case 'E':
+ token = token.substr(0, 1);
+ /* falls through */
+ case 'gggg':
+ case 'GGGG':
+ case 'GGGGG':
+ token = token.substr(0, 2);
+ if (input) {
+ config._w = config._w || {};
+ config._w[token] = toInt(input);
+ }
+ break;
+ case 'gg':
+ case 'GG':
+ config._w = config._w || {};
+ config._w[token] = moment.parseTwoDigitYear(input);
+ }
+ }
+
+ function dayOfYearFromWeekInfo(config) {
+ var w, weekYear, week, weekday, dow, doy, temp;
+
+ w = config._w;
+ if (w.GG != null || w.W != null || w.E != null) {
+ dow = 1;
+ doy = 4;
+
+ // TODO: We need to take the current isoWeekYear, but that depends on
+ // how we interpret now (local, utc, fixed offset). So create
+ // a now version of current config (take local/utc/offset flags, and
+ // create now).
+ weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year);
+ week = dfl(w.W, 1);
+ weekday = dfl(w.E, 1);
+ } else {
+ dow = config._locale._week.dow;
+ doy = config._locale._week.doy;
+
+ weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year);
+ week = dfl(w.w, 1);
+
+ if (w.d != null) {
+ // weekday -- low day numbers are considered next week
+ weekday = w.d;
+ if (weekday < dow) {
+ ++week;
+ }
+ } else if (w.e != null) {
+ // local weekday -- counting starts from begining of week
+ weekday = w.e + dow;
+ } else {
+ // default to begining of week
+ weekday = dow;
+ }
+ }
+ temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
+
+ config._a[YEAR] = temp.year;
+ config._dayOfYear = temp.dayOfYear;
+ }
+
+ // convert an array to a date.
+ // the array should mirror the parameters below
+ // note: all values past the year are optional and will default to the lowest possible value.
+ // [year, month, day , hour, minute, second, millisecond]
+ function dateFromConfig(config) {
+ var i, date, input = [], currentDate, yearToUse;
+
+ if (config._d) {
+ return;
+ }
+
+ currentDate = currentDateArray(config);
+
+ //compute day of the year from weeks and weekdays
+ if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
+ dayOfYearFromWeekInfo(config);
+ }
+
+ //if the day of the year is set, figure out what it is
+ if (config._dayOfYear) {
+ yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
+
+ if (config._dayOfYear > daysInYear(yearToUse)) {
+ config._pf._overflowDayOfYear = true;
+ }
+
+ date = makeUTCDate(yearToUse, 0, config._dayOfYear);
+ config._a[MONTH] = date.getUTCMonth();
+ config._a[DATE] = date.getUTCDate();
+ }
+
+ // Default to current date.
+ // * if no year, month, day of month are given, default to today
+ // * if day of month is given, default month and year
+ // * if month is given, default only year
+ // * if year is given, don't default anything
+ for (i = 0; i < 3 && config._a[i] == null; ++i) {
+ config._a[i] = input[i] = currentDate[i];
+ }
+
+ // Zero out whatever was not defaulted, including time
+ for (; i < 7; i++) {
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+ }
+
+ config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+ // Apply timezone offset from input. The actual zone can be changed
+ // with parseZone.
+ if (config._tzm != null) {
+ config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm);
+ }
+ }
+
+ function dateFromObject(config) {
+ var normalizedInput;
+
+ if (config._d) {
+ return;
+ }
+
+ normalizedInput = normalizeObjectUnits(config._i);
+ config._a = [
+ normalizedInput.year,
+ normalizedInput.month,
+ normalizedInput.day,
+ normalizedInput.hour,
+ normalizedInput.minute,
+ normalizedInput.second,
+ normalizedInput.millisecond
+ ];
+
+ dateFromConfig(config);
+ }
+
+ function currentDateArray(config) {
+ var now = new Date();
+ if (config._useUTC) {
+ return [
+ now.getUTCFullYear(),
+ now.getUTCMonth(),
+ now.getUTCDate()
+ ];
+ } else {
+ return [now.getFullYear(), now.getMonth(), now.getDate()];
+ }
+ }
+
+ // date from string and format string
+ function makeDateFromStringAndFormat(config) {
+ if (config._f === moment.ISO_8601) {
+ parseISO(config);
+ return;
+ }
+
+ config._a = [];
+ config._pf.empty = true;
+
+ // This array is used to make a Date, either with `new Date` or `Date.UTC`
+ var string = '' + config._i,
+ i, parsedInput, tokens, token, skipped,
+ stringLength = string.length,
+ totalParsedInputLength = 0;
+
+ tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
+
+ for (i = 0; i < tokens.length; i++) {
+ token = tokens[i];
+ parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
+ if (parsedInput) {
+ skipped = string.substr(0, string.indexOf(parsedInput));
+ if (skipped.length > 0) {
+ config._pf.unusedInput.push(skipped);
+ }
+ string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+ totalParsedInputLength += parsedInput.length;
+ }
+ // don't parse if it's not a known token
+ if (formatTokenFunctions[token]) {
+ if (parsedInput) {
+ config._pf.empty = false;
+ }
+ else {
+ config._pf.unusedTokens.push(token);
+ }
+ addTimeToArrayFromToken(token, parsedInput, config);
+ }
+ else if (config._strict && !parsedInput) {
+ config._pf.unusedTokens.push(token);
+ }
+ }
+
+ // add remaining unparsed input length to the string
+ config._pf.charsLeftOver = stringLength - totalParsedInputLength;
+ if (string.length > 0) {
+ config._pf.unusedInput.push(string);
+ }
+
+ // handle am pm
+ if (config._isPm && config._a[HOUR] < 12) {
+ config._a[HOUR] += 12;
+ }
+ // if is 12 am, change hours to 0
+ if (config._isPm === false && config._a[HOUR] === 12) {
+ config._a[HOUR] = 0;
+ }
+
+ dateFromConfig(config);
+ checkOverflow(config);
+ }
+
+ function unescapeFormat(s) {
+ return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
+ return p1 || p2 || p3 || p4;
+ });
+ }
+
+ // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+ function regexpEscape(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ }
+
+ // date from string and array of format strings
+ function makeDateFromStringAndArray(config) {
+ var tempConfig,
+ bestMoment,
+
+ scoreToBeat,
+ i,
+ currentScore;
+
+ if (config._f.length === 0) {
+ config._pf.invalidFormat = true;
+ config._d = new Date(NaN);
+ return;
+ }
+
+ for (i = 0; i < config._f.length; i++) {
+ currentScore = 0;
+ tempConfig = copyConfig({}, config);
+ if (config._useUTC != null) {
+ tempConfig._useUTC = config._useUTC;
+ }
+ tempConfig._pf = defaultParsingFlags();
+ tempConfig._f = config._f[i];
+ makeDateFromStringAndFormat(tempConfig);
+
+ if (!isValid(tempConfig)) {
+ continue;
+ }
+
+ // if there is any input that was not parsed add a penalty for that format
+ currentScore += tempConfig._pf.charsLeftOver;
+
+ //or tokens
+ currentScore += tempConfig._pf.unusedTokens.length * 10;
+
+ tempConfig._pf.score = currentScore;
+
+ if (scoreToBeat == null || currentScore < scoreToBeat) {
+ scoreToBeat = currentScore;
+ bestMoment = tempConfig;
+ }
+ }
+
+ extend(config, bestMoment || tempConfig);
+ }
+
+ // date from iso format
+ function parseISO(config) {
+ var i, l,
+ string = config._i,
+ match = isoRegex.exec(string);
+
+ if (match) {
+ config._pf.iso = true;
+ for (i = 0, l = isoDates.length; i < l; i++) {
+ if (isoDates[i][1].exec(string)) {
+ // match[5] should be 'T' or undefined
+ config._f = isoDates[i][0] + (match[6] || ' ');
+ break;
+ }
+ }
+ for (i = 0, l = isoTimes.length; i < l; i++) {
+ if (isoTimes[i][1].exec(string)) {
+ config._f += isoTimes[i][0];
+ break;
+ }
+ }
+ if (string.match(parseTokenTimezone)) {
+ config._f += 'Z';
+ }
+ makeDateFromStringAndFormat(config);
+ } else {
+ config._isValid = false;
+ }
+ }
+
+ // date from iso format or fallback
+ function makeDateFromString(config) {
+ parseISO(config);
+ if (config._isValid === false) {
+ delete config._isValid;
+ moment.createFromInputFallback(config);
+ }
+ }
+
+ function map(arr, fn) {
+ var res = [], i;
+ for (i = 0; i < arr.length; ++i) {
+ res.push(fn(arr[i], i));
+ }
+ return res;
+ }
+
+ function makeDateFromInput(config) {
+ var input = config._i, matched;
+ if (input === undefined) {
+ config._d = new Date();
+ } else if (isDate(input)) {
+ config._d = new Date(+input);
+ } else if ((matched = aspNetJsonRegex.exec(input)) !== null) {
+ config._d = new Date(+matched[1]);
+ } else if (typeof input === 'string') {
+ makeDateFromString(config);
+ } else if (isArray(input)) {
+ config._a = map(input.slice(0), function (obj) {
+ return parseInt(obj, 10);
+ });
+ dateFromConfig(config);
+ } else if (typeof(input) === 'object') {
+ dateFromObject(config);
+ } else if (typeof(input) === 'number') {
+ // from milliseconds
+ config._d = new Date(input);
+ } else {
+ moment.createFromInputFallback(config);
+ }
+ }
+
+ function makeDate(y, m, d, h, M, s, ms) {
+ //can't just apply() to create a date:
+ //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+ var date = new Date(y, m, d, h, M, s, ms);
+
+ //the date constructor doesn't accept years < 1970
+ if (y < 1970) {
+ date.setFullYear(y);
+ }
+ return date;
+ }
+
+ function makeUTCDate(y) {
+ var date = new Date(Date.UTC.apply(null, arguments));
+ if (y < 1970) {
+ date.setUTCFullYear(y);
+ }
+ return date;
+ }
+
+ function parseWeekday(input, locale) {
+ if (typeof input === 'string') {
+ if (!isNaN(input)) {
+ input = parseInt(input, 10);
+ }
+ else {
+ input = locale.weekdaysParse(input);
+ if (typeof input !== 'number') {
+ return null;
+ }
+ }
+ }
+ return input;
+ }
+
+ /************************************
+ Relative Time
+ ************************************/
+
+
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+ function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
+ return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+ }
+
+ function relativeTime(posNegDuration, withoutSuffix, locale) {
+ var duration = moment.duration(posNegDuration).abs(),
+ seconds = round(duration.as('s')),
+ minutes = round(duration.as('m')),
+ hours = round(duration.as('h')),
+ days = round(duration.as('d')),
+ months = round(duration.as('M')),
+ years = round(duration.as('y')),
+
+ args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
+ minutes === 1 && ['m'] ||
+ minutes < relativeTimeThresholds.m && ['mm', minutes] ||
+ hours === 1 && ['h'] ||
+ hours < relativeTimeThresholds.h && ['hh', hours] ||
+ days === 1 && ['d'] ||
+ days < relativeTimeThresholds.d && ['dd', days] ||
+ months === 1 && ['M'] ||
+ months < relativeTimeThresholds.M && ['MM', months] ||
+ years === 1 && ['y'] || ['yy', years];
+
+ args[2] = withoutSuffix;
+ args[3] = +posNegDuration > 0;
+ args[4] = locale;
+ return substituteTimeAgo.apply({}, args);
+ }
+
+
+ /************************************
+ Week of Year
+ ************************************/
+
+
+ // firstDayOfWeek 0 = sun, 6 = sat
+ // the day of the week that starts the week
+ // (usually sunday or monday)
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
+ // the first week is the week that contains the first
+ // of this day of the week
+ // (eg. ISO weeks use thursday (4))
+ function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
+ var end = firstDayOfWeekOfYear - firstDayOfWeek,
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
+ adjustedMoment;
+
+
+ if (daysToDayOfWeek > end) {
+ daysToDayOfWeek -= 7;
+ }
+
+ if (daysToDayOfWeek < end - 7) {
+ daysToDayOfWeek += 7;
+ }
+
+ adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd');
+ return {
+ week: Math.ceil(adjustedMoment.dayOfYear() / 7),
+ year: adjustedMoment.year()
+ };
+ }
+
+ //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+ function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
+ var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
+
+ d = d === 0 ? 7 : d;
+ weekday = weekday != null ? weekday : firstDayOfWeek;
+ daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
+ dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
+
+ return {
+ year: dayOfYear > 0 ? year : year - 1,
+ dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
+ };
+ }
+
+ /************************************
+ Top Level Functions
+ ************************************/
+
+ function makeMoment(config) {
+ var input = config._i,
+ format = config._f;
+
+ config._locale = config._locale || moment.localeData(config._l);
+
+ if (input === null || (format === undefined && input === '')) {
+ return moment.invalid({nullInput: true});
+ }
+
+ if (typeof input === 'string') {
+ config._i = input = config._locale.preparse(input);
+ }
+
+ if (moment.isMoment(input)) {
+ return new Moment(input, true);
+ } else if (format) {
+ if (isArray(format)) {
+ makeDateFromStringAndArray(config);
+ } else {
+ makeDateFromStringAndFormat(config);
+ }
+ } else {
+ makeDateFromInput(config);
+ }
+
+ return new Moment(config);
+ }
+
+ moment = function (input, format, locale, strict) {
+ var c;
+
+ if (typeof(locale) === 'boolean') {
+ strict = locale;
+ locale = undefined;
+ }
+ // object construction must be done this way.
+ // https://github.com/moment/moment/issues/1423
+ c = {};
+ c._isAMomentObject = true;
+ c._i = input;
+ c._f = format;
+ c._l = locale;
+ c._strict = strict;
+ c._isUTC = false;
+ c._pf = defaultParsingFlags();
+
+ return makeMoment(c);
+ };
+
+ moment.suppressDeprecationWarnings = false;
+
+ moment.createFromInputFallback = deprecate(
+ 'moment construction falls back to js Date. This is ' +
+ 'discouraged and will be removed in upcoming major ' +
+ 'release. Please refer to ' +
+ 'https://github.com/moment/moment/issues/1407 for more info.',
+ function (config) {
+ config._d = new Date(config._i);
+ }
+ );
+
+ // Pick a moment m from moments so that m[fn](other) is true for all
+ // other. This relies on the function fn to be transitive.
+ //
+ // moments should either be an array of moment objects or an array, whose
+ // first element is an array of moment objects.
+ function pickBy(fn, moments) {
+ var res, i;
+ if (moments.length === 1 && isArray(moments[0])) {
+ moments = moments[0];
+ }
+ if (!moments.length) {
+ return moment();
+ }
+ res = moments[0];
+ for (i = 1; i < moments.length; ++i) {
+ if (moments[i][fn](res)) {
+ res = moments[i];
+ }
+ }
+ return res;
+ }
+
+ moment.min = function () {
+ var args = [].slice.call(arguments, 0);
+
+ return pickBy('isBefore', args);
+ };
+
+ moment.max = function () {
+ var args = [].slice.call(arguments, 0);
+
+ return pickBy('isAfter', args);
+ };
+
+ // creating with utc
+ moment.utc = function (input, format, locale, strict) {
+ var c;
+
+ if (typeof(locale) === 'boolean') {
+ strict = locale;
+ locale = undefined;
+ }
+ // object construction must be done this way.
+ // https://github.com/moment/moment/issues/1423
+ c = {};
+ c._isAMomentObject = true;
+ c._useUTC = true;
+ c._isUTC = true;
+ c._l = locale;
+ c._i = input;
+ c._f = format;
+ c._strict = strict;
+ c._pf = defaultParsingFlags();
+
+ return makeMoment(c).utc();
+ };
+
+ // creating with unix timestamp (in seconds)
+ moment.unix = function (input) {
+ return moment(input * 1000);
+ };
+
+ // duration
+ moment.duration = function (input, key) {
+ var duration = input,
+ // matching against regexp is expensive, do it on demand
+ match = null,
+ sign,
+ ret,
+ parseIso,
+ diffRes;
+
+ if (moment.isDuration(input)) {
+ duration = {
+ ms: input._milliseconds,
+ d: input._days,
+ M: input._months
+ };
+ } else if (typeof input === 'number') {
+ duration = {};
+ if (key) {
+ duration[key] = input;
+ } else {
+ duration.milliseconds = input;
+ }
+ } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
+ sign = (match[1] === '-') ? -1 : 1;
+ duration = {
+ y: 0,
+ d: toInt(match[DATE]) * sign,
+ h: toInt(match[HOUR]) * sign,
+ m: toInt(match[MINUTE]) * sign,
+ s: toInt(match[SECOND]) * sign,
+ ms: toInt(match[MILLISECOND]) * sign
+ };
+ } else if (!!(match = isoDurationRegex.exec(input))) {
+ sign = (match[1] === '-') ? -1 : 1;
+ parseIso = function (inp) {
+ // We'd normally use ~~inp for this, but unfortunately it also
+ // converts floats to ints.
+ // inp may be undefined, so careful calling replace on it.
+ var res = inp && parseFloat(inp.replace(',', '.'));
+ // apply sign while we're at it
+ return (isNaN(res) ? 0 : res) * sign;
+ };
+ duration = {
+ y: parseIso(match[2]),
+ M: parseIso(match[3]),
+ d: parseIso(match[4]),
+ h: parseIso(match[5]),
+ m: parseIso(match[6]),
+ s: parseIso(match[7]),
+ w: parseIso(match[8])
+ };
+ } else if (typeof duration === 'object' &&
+ ('from' in duration || 'to' in duration)) {
+ diffRes = momentsDifference(moment(duration.from), moment(duration.to));
+
+ duration = {};
+ duration.ms = diffRes.milliseconds;
+ duration.M = diffRes.months;
+ }
+
+ ret = new Duration(duration);
+
+ if (moment.isDuration(input) && hasOwnProp(input, '_locale')) {
+ ret._locale = input._locale;
+ }
+
+ return ret;
+ };
+
+ // version number
+ moment.version = VERSION;
+
+ // default format
+ moment.defaultFormat = isoFormat;
+
+ // constant that refers to the ISO standard
+ moment.ISO_8601 = function () {};
+
+ // Plugins that add properties should also add the key here (null value),
+ // so we can properly clone ourselves.
+ moment.momentProperties = momentProperties;
+
+ // This function will be called whenever a moment is mutated.
+ // It is intended to keep the offset in sync with the timezone.
+ moment.updateOffset = function () {};
+
+ // This function allows you to set a threshold for relative time strings
+ moment.relativeTimeThreshold = function (threshold, limit) {
+ if (relativeTimeThresholds[threshold] === undefined) {
+ return false;
+ }
+ if (limit === undefined) {
+ return relativeTimeThresholds[threshold];
+ }
+ relativeTimeThresholds[threshold] = limit;
+ return true;
+ };
+
+ moment.lang = deprecate(
+ 'moment.lang is deprecated. Use moment.locale instead.',
+ function (key, value) {
+ return moment.locale(key, value);
+ }
+ );
+
+ // This function will load locale and then set the global locale. If
+ // no arguments are passed in, it will simply return the current global
+ // locale key.
+ moment.locale = function (key, values) {
+ var data;
+ if (key) {
+ if (typeof(values) !== 'undefined') {
+ data = moment.defineLocale(key, values);
+ }
+ else {
+ data = moment.localeData(key);
+ }
+
+ if (data) {
+ moment.duration._locale = moment._locale = data;
+ }
+ }
+
+ return moment._locale._abbr;
+ };
+
+ moment.defineLocale = function (name, values) {
+ if (values !== null) {
+ values.abbr = name;
+ if (!locales[name]) {
+ locales[name] = new Locale();
+ }
+ locales[name].set(values);
+
+ // backwards compat for now: also set the locale
+ moment.locale(name);
+
+ return locales[name];
+ } else {
+ // useful for testing
+ delete locales[name];
+ return null;
+ }
+ };
+
+ moment.langData = deprecate(
+ 'moment.langData is deprecated. Use moment.localeData instead.',
+ function (key) {
+ return moment.localeData(key);
+ }
+ );
+
+ // returns locale data
+ moment.localeData = function (key) {
+ var locale;
+
+ if (key && key._locale && key._locale._abbr) {
+ key = key._locale._abbr;
+ }
+
+ if (!key) {
+ return moment._locale;
+ }
+
+ if (!isArray(key)) {
+ //short-circuit everything else
+ locale = loadLocale(key);
+ if (locale) {
+ return locale;
+ }
+ key = [key];
+ }
+
+ return chooseLocale(key);
+ };
+
+ // compare moment object
+ moment.isMoment = function (obj) {
+ return obj instanceof Moment ||
+ (obj != null && hasOwnProp(obj, '_isAMomentObject'));
+ };
+
+ // for typechecking Duration objects
+ moment.isDuration = function (obj) {
+ return obj instanceof Duration;
+ };
+
+ for (i = lists.length - 1; i >= 0; --i) {
+ makeList(lists[i]);
+ }
+
+ moment.normalizeUnits = function (units) {
+ return normalizeUnits(units);
+ };
+
+ moment.invalid = function (flags) {
+ var m = moment.utc(NaN);
+ if (flags != null) {
+ extend(m._pf, flags);
+ }
+ else {
+ m._pf.userInvalidated = true;
+ }
+
+ return m;
+ };
+
+ moment.parseZone = function () {
+ return moment.apply(null, arguments).parseZone();
+ };
+
+ moment.parseTwoDigitYear = function (input) {
+ return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+ };
+
+ /************************************
+ Moment Prototype
+ ************************************/
+
+
+ extend(moment.fn = Moment.prototype, {
+
+ clone : function () {
+ return moment(this);
+ },
+
+ valueOf : function () {
+ return +this._d + ((this._offset || 0) * 60000);
+ },
+
+ unix : function () {
+ return Math.floor(+this / 1000);
+ },
+
+ toString : function () {
+ return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
+ },
+
+ toDate : function () {
+ return this._offset ? new Date(+this) : this._d;
+ },
+
+ toISOString : function () {
+ var m = moment(this).utc();
+ if (0 < m.year() && m.year() <= 9999) {
+ return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ } else {
+ return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ }
+ },
+
+ toArray : function () {
+ var m = this;
+ return [
+ m.year(),
+ m.month(),
+ m.date(),
+ m.hours(),
+ m.minutes(),
+ m.seconds(),
+ m.milliseconds()
+ ];
+ },
+
+ isValid : function () {
+ return isValid(this);
+ },
+
+ isDSTShifted : function () {
+ if (this._a) {
+ return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
+ }
+
+ return false;
+ },
+
+ parsingFlags : function () {
+ return extend({}, this._pf);
+ },
+
+ invalidAt: function () {
+ return this._pf.overflow;
+ },
+
+ utc : function (keepLocalTime) {
+ return this.zone(0, keepLocalTime);
+ },
+
+ local : function (keepLocalTime) {
+ if (this._isUTC) {
+ this.zone(0, keepLocalTime);
+ this._isUTC = false;
+
+ if (keepLocalTime) {
+ this.add(this._dateTzOffset(), 'm');
+ }
+ }
+ return this;
+ },
+
+ format : function (inputString) {
+ var output = formatMoment(this, inputString || moment.defaultFormat);
+ return this.localeData().postformat(output);
+ },
+
+ add : createAdder(1, 'add'),
+
+ subtract : createAdder(-1, 'subtract'),
+
+ diff : function (input, units, asFloat) {
+ var that = makeAs(input, this),
+ zoneDiff = (this.zone() - that.zone()) * 6e4,
+ diff, output, daysAdjust;
+
+ units = normalizeUnits(units);
+
+ if (units === 'year' || units === 'month') {
+ // average number of days in the months in the given dates
+ diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
+ // difference in months
+ output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
+ // adjust by taking difference in days, average number of days
+ // and dst in the given months.
+ daysAdjust = (this - moment(this).startOf('month')) -
+ (that - moment(that).startOf('month'));
+ // same as above but with zones, to negate all dst
+ daysAdjust -= ((this.zone() - moment(this).startOf('month').zone()) -
+ (that.zone() - moment(that).startOf('month').zone())) * 6e4;
+ output += daysAdjust / diff;
+ if (units === 'year') {
+ output = output / 12;
+ }
+ } else {
+ diff = (this - that);
+ output = units === 'second' ? diff / 1e3 : // 1000
+ units === 'minute' ? diff / 6e4 : // 1000 * 60
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
+ units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
+ units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
+ diff;
+ }
+ return asFloat ? output : absRound(output);
+ },
+
+ from : function (time, withoutSuffix) {
+ return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
+ },
+
+ fromNow : function (withoutSuffix) {
+ return this.from(moment(), withoutSuffix);
+ },
+
+ calendar : function (time) {
+ // We want to compare the start of today, vs this.
+ // Getting start-of-today depends on whether we're zone'd or not.
+ var now = time || moment(),
+ sod = makeAs(now, this).startOf('day'),
+ diff = this.diff(sod, 'days', true),
+ format = diff < -6 ? 'sameElse' :
+ diff < -1 ? 'lastWeek' :
+ diff < 0 ? 'lastDay' :
+ diff < 1 ? 'sameDay' :
+ diff < 2 ? 'nextDay' :
+ diff < 7 ? 'nextWeek' : 'sameElse';
+ return this.format(this.localeData().calendar(format, this));
+ },
+
+ isLeapYear : function () {
+ return isLeapYear(this.year());
+ },
+
+ isDST : function () {
+ return (this.zone() < this.clone().month(0).zone() ||
+ this.zone() < this.clone().month(5).zone());
+ },
+
+ day : function (input) {
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+ if (input != null) {
+ input = parseWeekday(input, this.localeData());
+ return this.add(input - day, 'd');
+ } else {
+ return day;
+ }
+ },
+
+ month : makeAccessor('Month', true),
+
+ startOf : function (units) {
+ units = normalizeUnits(units);
+ // the following switch intentionally omits break keywords
+ // to utilize falling through the cases.
+ switch (units) {
+ case 'year':
+ this.month(0);
+ /* falls through */
+ case 'quarter':
+ case 'month':
+ this.date(1);
+ /* falls through */
+ case 'week':
+ case 'isoWeek':
+ case 'day':
+ this.hours(0);
+ /* falls through */
+ case 'hour':
+ this.minutes(0);
+ /* falls through */
+ case 'minute':
+ this.seconds(0);
+ /* falls through */
+ case 'second':
+ this.milliseconds(0);
+ /* falls through */
+ }
+
+ // weeks are a special case
+ if (units === 'week') {
+ this.weekday(0);
+ } else if (units === 'isoWeek') {
+ this.isoWeekday(1);
+ }
+
+ // quarters are also special
+ if (units === 'quarter') {
+ this.month(Math.floor(this.month() / 3) * 3);
+ }
+
+ return this;
+ },
+
+ endOf: function (units) {
+ units = normalizeUnits(units);
+ return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
+ },
+
+ isAfter: function (input, units) {
+ units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
+ if (units === 'millisecond') {
+ input = moment.isMoment(input) ? input : moment(input);
+ return +this > +input;
+ } else {
+ return +this.clone().startOf(units) > +moment(input).startOf(units);
+ }
+ },
+
+ isBefore: function (input, units) {
+ units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
+ if (units === 'millisecond') {
+ input = moment.isMoment(input) ? input : moment(input);
+ return +this < +input;
+ } else {
+ return +this.clone().startOf(units) < +moment(input).startOf(units);
+ }
+ },
+
+ isSame: function (input, units) {
+ units = normalizeUnits(units || 'millisecond');
+ if (units === 'millisecond') {
+ input = moment.isMoment(input) ? input : moment(input);
+ return +this === +input;
+ } else {
+ return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
+ }
+ },
+
+ min: deprecate(
+ 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
+ function (other) {
+ other = moment.apply(null, arguments);
+ return other < this ? this : other;
+ }
+ ),
+
+ max: deprecate(
+ 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
+ function (other) {
+ other = moment.apply(null, arguments);
+ return other > this ? this : other;
+ }
+ ),
+
+ // keepLocalTime = true means only change the timezone, without
+ // affecting the local hour. So 5:31:26 +0300 --[zone(2, true)]-->
+ // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist int zone
+ // +0200, so we adjust the time as needed, to be valid.
+ //
+ // Keeping the time actually adds/subtracts (one hour)
+ // from the actual represented time. That is why we call updateOffset
+ // a second time. In case it wants us to change the offset again
+ // _changeInProgress == true case, then we have to adjust, because
+ // there is no such time in the given timezone.
+ zone : function (input, keepLocalTime) {
+ var offset = this._offset || 0,
+ localAdjust;
+ if (input != null) {
+ if (typeof input === 'string') {
+ input = timezoneMinutesFromString(input);
+ }
+ if (Math.abs(input) < 16) {
+ input = input * 60;
+ }
+ if (!this._isUTC && keepLocalTime) {
+ localAdjust = this._dateTzOffset();
+ }
+ this._offset = input;
+ this._isUTC = true;
+ if (localAdjust != null) {
+ this.subtract(localAdjust, 'm');
+ }
+ if (offset !== input) {
+ if (!keepLocalTime || this._changeInProgress) {
+ addOrSubtractDurationFromMoment(this,
+ moment.duration(offset - input, 'm'), 1, false);
+ } else if (!this._changeInProgress) {
+ this._changeInProgress = true;
+ moment.updateOffset(this, true);
+ this._changeInProgress = null;
+ }
+ }
+ } else {
+ return this._isUTC ? offset : this._dateTzOffset();
+ }
+ return this;
+ },
+
+ zoneAbbr : function () {
+ return this._isUTC ? 'UTC' : '';
+ },
+
+ zoneName : function () {
+ return this._isUTC ? 'Coordinated Universal Time' : '';
+ },
+
+ parseZone : function () {
+ if (this._tzm) {
+ this.zone(this._tzm);
+ } else if (typeof this._i === 'string') {
+ this.zone(this._i);
+ }
+ return this;
+ },
+
+ hasAlignedHourOffset : function (input) {
+ if (!input) {
+ input = 0;
+ }
+ else {
+ input = moment(input).zone();
+ }
+
+ return (this.zone() - input) % 60 === 0;
+ },
+
+ daysInMonth : function () {
+ return daysInMonth(this.year(), this.month());
+ },
+
+ dayOfYear : function (input) {
+ var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
+ return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
+ },
+
+ quarter : function (input) {
+ return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
+ },
+
+ weekYear : function (input) {
+ var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year;
+ return input == null ? year : this.add((input - year), 'y');
+ },
+
+ isoWeekYear : function (input) {
+ var year = weekOfYear(this, 1, 4).year;
+ return input == null ? year : this.add((input - year), 'y');
+ },
+
+ week : function (input) {
+ var week = this.localeData().week(this);
+ return input == null ? week : this.add((input - week) * 7, 'd');
+ },
+
+ isoWeek : function (input) {
+ var week = weekOfYear(this, 1, 4).week;
+ return input == null ? week : this.add((input - week) * 7, 'd');
+ },
+
+ weekday : function (input) {
+ var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
+ return input == null ? weekday : this.add(input - weekday, 'd');
+ },
+
+ isoWeekday : function (input) {
+ // behaves the same as moment#day except
+ // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+ // as a setter, sunday should belong to the previous week.
+ return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
+ },
+
+ isoWeeksInYear : function () {
+ return weeksInYear(this.year(), 1, 4);
+ },
+
+ weeksInYear : function () {
+ var weekInfo = this.localeData()._week;
+ return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
+ },
+
+ get : function (units) {
+ units = normalizeUnits(units);
+ return this[units]();
+ },
+
+ set : function (units, value) {
+ units = normalizeUnits(units);
+ if (typeof this[units] === 'function') {
+ this[units](value);
+ }
+ return this;
+ },
+
+ // If passed a locale key, it will set the locale for this
+ // instance. Otherwise, it will return the locale configuration
+ // variables for this instance.
+ locale : function (key) {
+ var newLocaleData;
+
+ if (key === undefined) {
+ return this._locale._abbr;
+ } else {
+ newLocaleData = moment.localeData(key);
+ if (newLocaleData != null) {
+ this._locale = newLocaleData;
+ }
+ return this;
+ }
+ },
+
+ lang : deprecate(
+ 'moment().lang() is deprecated. Use moment().localeData() instead.',
+ function (key) {
+ if (key === undefined) {
+ return this.localeData();
+ } else {
+ return this.locale(key);
+ }
+ }
+ ),
+
+ localeData : function () {
+ return this._locale;
+ },
+
+ _dateTzOffset : function () {
+ // On Firefox.24 Date#getTimezoneOffset returns a floating point.
+ // https://github.com/moment/moment/pull/1871
+ return Math.round(this._d.getTimezoneOffset() / 15) * 15;
+ }
+ });
+
+ function rawMonthSetter(mom, value) {
+ var dayOfMonth;
+
+ // TODO: Move this out of here!
+ if (typeof value === 'string') {
+ value = mom.localeData().monthsParse(value);
+ // TODO: Another silent failure?
+ if (typeof value !== 'number') {
+ return mom;
+ }
+ }
+
+ dayOfMonth = Math.min(mom.date(),
+ daysInMonth(mom.year(), value));
+ mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
+ return mom;
+ }
+
+ function rawGetter(mom, unit) {
+ return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
+ }
+
+ function rawSetter(mom, unit, value) {
+ if (unit === 'Month') {
+ return rawMonthSetter(mom, value);
+ } else {
+ return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
+ }
+ }
+
+ function makeAccessor(unit, keepTime) {
+ return function (value) {
+ if (value != null) {
+ rawSetter(this, unit, value);
+ moment.updateOffset(this, keepTime);
+ return this;
+ } else {
+ return rawGetter(this, unit);
+ }
+ };
+ }
+
+ moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
+ moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
+ moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
+ // Setting the hour should keep the time, because the user explicitly
+ // specified which hour he wants. So trying to maintain the same hour (in
+ // a new timezone) makes sense. Adding/subtracting hours does not follow
+ // this rule.
+ moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
+ // moment.fn.month is defined separately
+ moment.fn.date = makeAccessor('Date', true);
+ moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true));
+ moment.fn.year = makeAccessor('FullYear', true);
+ moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true));
+
+ // add plural methods
+ moment.fn.days = moment.fn.day;
+ moment.fn.months = moment.fn.month;
+ moment.fn.weeks = moment.fn.week;
+ moment.fn.isoWeeks = moment.fn.isoWeek;
+ moment.fn.quarters = moment.fn.quarter;
+
+ // add aliased format methods
+ moment.fn.toJSON = moment.fn.toISOString;
+
+ /************************************
+ Duration Prototype
+ ************************************/
+
+
+ function daysToYears (days) {
+ // 400 years have 146097 days (taking into account leap year rules)
+ return days * 400 / 146097;
+ }
+
+ function yearsToDays (years) {
+ // years * 365 + absRound(years / 4) -
+ // absRound(years / 100) + absRound(years / 400);
+ return years * 146097 / 400;
+ }
+
+ extend(moment.duration.fn = Duration.prototype, {
+
+ _bubble : function () {
+ var milliseconds = this._milliseconds,
+ days = this._days,
+ months = this._months,
+ data = this._data,
+ seconds, minutes, hours, years = 0;
+
+ // The following code bubbles up values, see the tests for
+ // examples of what that means.
+ data.milliseconds = milliseconds % 1000;
+
+ seconds = absRound(milliseconds / 1000);
+ data.seconds = seconds % 60;
+
+ minutes = absRound(seconds / 60);
+ data.minutes = minutes % 60;
+
+ hours = absRound(minutes / 60);
+ data.hours = hours % 24;
+
+ days += absRound(hours / 24);
+
+ // Accurately convert days to years, assume start from year 0.
+ years = absRound(daysToYears(days));
+ days -= absRound(yearsToDays(years));
+
+ // 30 days to a month
+ // TODO (iskren): Use anchor date (like 1st Jan) to compute this.
+ months += absRound(days / 30);
+ days %= 30;
+
+ // 12 months -> 1 year
+ years += absRound(months / 12);
+ months %= 12;
+
+ data.days = days;
+ data.months = months;
+ data.years = years;
+ },
+
+ abs : function () {
+ this._milliseconds = Math.abs(this._milliseconds);
+ this._days = Math.abs(this._days);
+ this._months = Math.abs(this._months);
+
+ this._data.milliseconds = Math.abs(this._data.milliseconds);
+ this._data.seconds = Math.abs(this._data.seconds);
+ this._data.minutes = Math.abs(this._data.minutes);
+ this._data.hours = Math.abs(this._data.hours);
+ this._data.months = Math.abs(this._data.months);
+ this._data.years = Math.abs(this._data.years);
+
+ return this;
+ },
+
+ weeks : function () {
+ return absRound(this.days() / 7);
+ },
+
+ valueOf : function () {
+ return this._milliseconds +
+ this._days * 864e5 +
+ (this._months % 12) * 2592e6 +
+ toInt(this._months / 12) * 31536e6;
+ },
+
+ humanize : function (withSuffix) {
+ var output = relativeTime(this, !withSuffix, this.localeData());
+
+ if (withSuffix) {
+ output = this.localeData().pastFuture(+this, output);
+ }
+
+ return this.localeData().postformat(output);
+ },
+
+ add : function (input, val) {
+ // supports only 2.0-style add(1, 's') or add(moment)
+ var dur = moment.duration(input, val);
+
+ this._milliseconds += dur._milliseconds;
+ this._days += dur._days;
+ this._months += dur._months;
+
+ this._bubble();
+
+ return this;
+ },
+
+ subtract : function (input, val) {
+ var dur = moment.duration(input, val);
+
+ this._milliseconds -= dur._milliseconds;
+ this._days -= dur._days;
+ this._months -= dur._months;
+
+ this._bubble();
+
+ return this;
+ },
+
+ get : function (units) {
+ units = normalizeUnits(units);
+ return this[units.toLowerCase() + 's']();
+ },
+
+ as : function (units) {
+ var days, months;
+ units = normalizeUnits(units);
+
+ if (units === 'month' || units === 'year') {
+ days = this._days + this._milliseconds / 864e5;
+ months = this._months + daysToYears(days) * 12;
+ return units === 'month' ? months : months / 12;
+ } else {
+ // handle milliseconds separately because of floating point math errors (issue #1867)
+ days = this._days + yearsToDays(this._months / 12);
+ switch (units) {
+ case 'week': return days / 7 + this._milliseconds / 6048e5;
+ case 'day': return days + this._milliseconds / 864e5;
+ case 'hour': return days * 24 + this._milliseconds / 36e5;
+ case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
+ case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
+ // Math.floor prevents floating point math errors here
+ case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
+ default: throw new Error('Unknown unit ' + units);
+ }
+ }
+ },
+
+ lang : moment.fn.lang,
+ locale : moment.fn.locale,
+
+ toIsoString : deprecate(
+ 'toIsoString() is deprecated. Please use toISOString() instead ' +
+ '(notice the capitals)',
+ function () {
+ return this.toISOString();
+ }
+ ),
+
+ toISOString : function () {
+ // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+ var years = Math.abs(this.years()),
+ months = Math.abs(this.months()),
+ days = Math.abs(this.days()),
+ hours = Math.abs(this.hours()),
+ minutes = Math.abs(this.minutes()),
+ seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
+
+ if (!this.asSeconds()) {
+ // this is the same as C#'s (Noda) and python (isodate)...
+ // but not other JS (goog.date)
+ return 'P0D';
+ }
+
+ return (this.asSeconds() < 0 ? '-' : '') +
+ 'P' +
+ (years ? years + 'Y' : '') +
+ (months ? months + 'M' : '') +
+ (days ? days + 'D' : '') +
+ ((hours || minutes || seconds) ? 'T' : '') +
+ (hours ? hours + 'H' : '') +
+ (minutes ? minutes + 'M' : '') +
+ (seconds ? seconds + 'S' : '');
+ },
+
+ localeData : function () {
+ return this._locale;
+ }
+ });
+
+ moment.duration.fn.toString = moment.duration.fn.toISOString;
+
+ function makeDurationGetter(name) {
+ moment.duration.fn[name] = function () {
+ return this._data[name];
+ };
+ }
+
+ for (i in unitMillisecondFactors) {
+ if (hasOwnProp(unitMillisecondFactors, i)) {
+ makeDurationGetter(i.toLowerCase());
+ }
+ }
+
+ moment.duration.fn.asMilliseconds = function () {
+ return this.as('ms');
+ };
+ moment.duration.fn.asSeconds = function () {
+ return this.as('s');
+ };
+ moment.duration.fn.asMinutes = function () {
+ return this.as('m');
+ };
+ moment.duration.fn.asHours = function () {
+ return this.as('h');
+ };
+ moment.duration.fn.asDays = function () {
+ return this.as('d');
+ };
+ moment.duration.fn.asWeeks = function () {
+ return this.as('weeks');
+ };
+ moment.duration.fn.asMonths = function () {
+ return this.as('M');
+ };
+ moment.duration.fn.asYears = function () {
+ return this.as('y');
+ };
+
+ /************************************
+ Default Locale
+ ************************************/
+
+
+ // Set default locale, other locale will inherit from English.
+ moment.locale('en', {
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (toInt(number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ }
+ });
+
+ /* EMBED_LOCALES */
+
+ /************************************
+ Exposing Moment
+ ************************************/
+
+ function makeGlobal(shouldDeprecate) {
+ /*global ender:false */
+ if (typeof ender !== 'undefined') {
+ return;
+ }
+ oldGlobalMoment = globalScope.moment;
+ if (shouldDeprecate) {
+ globalScope.moment = deprecate(
+ 'Accessing Moment through the global scope is ' +
+ 'deprecated, and will be removed in an upcoming ' +
+ 'release.',
+ moment);
+ } else {
+ globalScope.moment = moment;
+ }
+ }
+
+ // CommonJS module is defined
+ if (hasModule) {
+ module.exports = moment;
+ } else if (typeof define === 'function' && define.amd) {
+ define('moment', function (require, exports, module) {
+ if (module.config && module.config() && module.config().noGlobal === true) {
+ // release the global variable
+ globalScope.moment = oldGlobalMoment;
+ }
+
+ return moment;
+ });
+ makeGlobal(true);
+ } else {
+ makeGlobal();
+ }
+}).call(this);
\ No newline at end of file
diff --git a/unicalendar/lang/index.php b/unicalendar/lang/index.php
new file mode 100644
index 0000000..4ca25aa
--- /dev/null
+++ b/unicalendar/lang/index.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/lang/ru.php b/unicalendar/lang/ru.php
new file mode 100644
index 0000000..39ccbbd
--- /dev/null
+++ b/unicalendar/lang/ru.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/lang/ru.txt b/unicalendar/lang/ru.txt
new file mode 100644
index 0000000..a7febf0
--- /dev/null
+++ b/unicalendar/lang/ru.txt
@@ -0,0 +1,34 @@
+UCA_NO_EVENTS = "Нет запланированных событий"
+UCA_REAL_EVENTS = "события:"
+UCA_NEXT_EVENTS = "Следующие события:"
+UCA_LOOK_EVENTS = "Смотреть"
+UCA_MONTHS_EVENTS = "[ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' ]"
+UCA_MONSHORT_EVENTS = "[ 'Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек' ]"
+UCA_WEEKDAYS_EVENTS = "[ 'Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота' ]"
+UCA_WDAYSHORT_EVENTS = "[ 'Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб' ]"
+UCA_WDAYMIN_EVENTS = "[ 'Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб' ]"
+
+[admin]
+ModName = "Управление модулем Unicalendar"
+ModTitle = "В данном разделе находится список всех календарей. Так же, вы можете создать новый календарь."
+UCA_LIST = "Список календарей"
+UCA_NEW = "Создать новый календарь"
+UCA_NO_ITEMS = "В настоящий момент не существует ни одного календаря."
+UCA_TITLE = "Название календаря"
+UCA_EVENTS = "События календаря"
+UCA_EVENTS_SELECT = "Выберите тип событий календаря"
+UCA_EVENTS_SELECT_A = "Все документы из заданной рубрики"
+UCA_EVENTS_SELECT_AC = "Выбор рубрики"
+UCA_EVENTS_SELECT_DOC = "Отметить для выбора"
+UCA_EVENTS_SELECT_B = "Выбранные документы из заданной рубрики"
+UCA_EVENTS_SELECT_C = "Выбранные документы без привязки к рубрикам"
+UCA_BTN_CREATE = "Создать"
+UCA_CP_TAG = "Тег в системе"
+UCA_COPY_BUFF = "Скопировать тег в буфер обмена"
+UCA_ACTIONS = "Действия"
+UCA_DELETE_UNICLN = "Удалить календарь"
+UCA_DELETE_UNICLN_A = "Вы уверены, что хотите удалить этот календарь?"
+UCA_LIST_ALLDOC_RUB = "Выводит все документы из рубрики c Id = "
+UCA_LIST_SELDOC_RUB = "Выводит выбранные документы из рубрики c Id = "
+UCA_TITLE_WARNING = "Не заполнено поле Название календаря"
+UCA_EVENTS_WARNING = "События для этого календаря не заданы..."
\ No newline at end of file
diff --git a/unicalendar/module.php b/unicalendar/module.php
new file mode 100644
index 0000000..057a8f9
--- /dev/null
+++ b/unicalendar/module.php
@@ -0,0 +1,85 @@
+Модуль позволяет создавать различные календари событий. Для вывода календаря используйте системный тег [mod_unicalendar:XXX] ';
+ $modul['ModuleAutor'] = 'Repellent';
+ $modul['ModuleCopyright'] = '© AVE.cms Team 2017';
+ $modul['ModuleIsFunction'] = 1;
+ $modul['ModuleAdminEdit'] = 1;
+ $modul['ModuleFunction'] = 'mod_unicalendar';
+ $modul['ModuleTag'] = '[mod_unicalendar:XXX]';
+ $modul['ModuleTagLink'] = null;
+ $modul['ModuleAveTag'] = '#\\\[mod_unicalendar:([\\\d-]+)]#';
+ $modul['ModulePHPTag'] = "";
+}
+
+/**
+ * Публичная часть - вывод календаря
+ *
+ * @param string $id идентификатор календаря
+ */
+
+function mod_unicalendar($id)
+{
+ global $AVE_Template;
+
+ require_once(BASE_DIR . '/modules/unicalendar/class.unicalendar.php');
+ $unicalendar = new Unicalendar;
+
+ // папка с шаблонами
+ $tpl_dir = BASE_DIR . '/modules/unicalendar/templates/';
+
+ // ленги
+ $lang_file = BASE_DIR . '/modules/unicalendar/lang/' . $_SESSION['user_language'] . '.txt';
+
+ $AVE_Template->config_load($lang_file);
+
+ $unicalendar->unicalendarShow($tpl_dir, $id);
+}
+
+/**
+ * Админка
+ */
+
+if (defined('ACP') && (isset($_REQUEST['moduleaction'])))
+{
+ // класс
+ require_once(BASE_DIR . '/modules/unicalendar/class.unicalendar.php');
+ $unicalendar = new Unicalendar;
+
+ // папка с шаблонами
+ $tpl_dir = BASE_DIR . '/modules/unicalendar/templates/';
+
+ // ленги
+ $AVE_Template->config_load(BASE_DIR . '/modules/unicalendar/lang/' . $_SESSION['admin_language'] . '.txt', 'admin');
+ $AVE_Template->assign('config_vars', $AVE_Template->get_config_vars());
+
+ switch($_REQUEST['moduleaction'])
+ {
+ case '1': // Просмотр списка календарей
+ $unicalendar->unicalendarList($tpl_dir);
+ break;
+ case 'new': // Создать новый календарь
+ $unicalendar->unicalendarNew();
+ break;
+ case 'delunicalendar': // Удаление календаря
+ $unicalendar->unicalendarDelete(intval($_REQUEST['id']));
+ break;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/unicalendar/sql.php b/unicalendar/sql.php
new file mode 100644
index 0000000..97755bc
--- /dev/null
+++ b/unicalendar/sql.php
@@ -0,0 +1,43 @@
+
\ No newline at end of file
diff --git a/unicalendar/templates/admin_unicalendar_list.tpl b/unicalendar/templates/admin_unicalendar_list.tpl
new file mode 100644
index 0000000..9e2d440
--- /dev/null
+++ b/unicalendar/templates/admin_unicalendar_list.tpl
@@ -0,0 +1,279 @@
+
{#ModName#}
+
+
+
+{if $page_nav}
+
+{/if}
+
+
+{if $page_nav}
+
+{/if}
+
\ No newline at end of file
diff --git a/unicalendar/templates/index.php b/unicalendar/templates/index.php
new file mode 100644
index 0000000..4ca25aa
--- /dev/null
+++ b/unicalendar/templates/index.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/unicalendar/templates/unicalendar.tpl b/unicalendar/templates/unicalendar.tpl
new file mode 100644
index 0000000..9bc584c
--- /dev/null
+++ b/unicalendar/templates/unicalendar.tpl
@@ -0,0 +1,39 @@
+
+
+
+
+{foreach from=$unicalendars item=unicalendar}
+
+
+{/foreach}
\ No newline at end of file
diff --git a/unicalendar/uca.res.php b/unicalendar/uca.res.php
new file mode 100644
index 0000000..e6a13cd
--- /dev/null
+++ b/unicalendar/uca.res.php
@@ -0,0 +1,54 @@
+Query("
+ SELECT Id, rubric_title
+ FROM " . PREFIX . "_rubrics
+ ");
+ echo "".$select_category." ";
+ while($result = $sql->FetchRow()){
+ echo "".$result->rubric_title." ";
+ };
+}
+if (isset($_POST['2']))
+{
+ global $AVE_DB;
+ $sql = $AVE_DB->Query("
+ SELECT Id, rubric_title
+ FROM " . PREFIX . "_rubrics
+ ");
+ echo "".$select_category." ";
+ while($result = $sql->FetchRow()){
+ echo "".$result->rubric_title." ";
+ };
+}
+if (isset($_POST['a']))
+{
+ global $AVE_DB;
+ $sql = $AVE_DB->Query("
+ SELECT Id, document_alias, document_title, document_published, document_meta_description
+ FROM " . PREFIX . "_documents
+ WHERE rubric_id = '" . $_POST['b'] . "' AND Id > 2
+ ");
+ $u_sel_doc = array();
+ while ($row = $sql->FetchAssocArray())
+ {
+ array_push($u_sel_doc, $row);
+ }
+ foreach ( $u_sel_doc as $k=>$v )
+ {
+ echo " "."".$v['Id']." "."".$v['document_title']." "."".$v['document_published'] = pretty_date(strftime(TIME_FORMAT, $v['document_published']))." ";
+ }
+}
+?>
\ No newline at end of file