From d41548a425e18b93d4bd53a411be0afdfc52898e Mon Sep 17 00:00:00 2001 From: Mikalai Shershan Date: Fri, 4 May 2018 15:40:37 +0300 Subject: [PATCH 001/240] added / path for mautic tracking cookies --- app/bundles/CoreBundle/EventListener/BuildJsSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/CoreBundle/EventListener/BuildJsSubscriber.php b/app/bundles/CoreBundle/EventListener/BuildJsSubscriber.php index 75e8c6b1e04..605c89e854d 100644 --- a/app/bundles/CoreBundle/EventListener/BuildJsSubscriber.php +++ b/app/bundles/CoreBundle/EventListener/BuildJsSubscriber.php @@ -110,7 +110,7 @@ function CustomEvent ( event, params ) { }; MauticJS.setCookie = function(name, value) { - document.cookie = name+"="+value+";"; + document.cookie = name+"="+value+"; path=/"; }; MauticJS.createCORSRequest = function(method, url) { From 97a9557cd7028e0a469eee8805060677f424f81c Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 31 Jul 2018 15:44:04 +0200 Subject: [PATCH 002/240] Init commit --- .../Views/Slots/saveprefsbutton.html.php | 1 - .../Controller/PublicController.php | 7 +- .../EventListener/BuilderSubscriber.php | 67 ++++++++++++++++--- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/app/bundles/CoreBundle/Views/Slots/saveprefsbutton.html.php b/app/bundles/CoreBundle/Views/Slots/saveprefsbutton.html.php index a92fa0ea1b2..5a7dd1caae1 100644 --- a/app/bundles/CoreBundle/Views/Slots/saveprefsbutton.html.php +++ b/app/bundles/CoreBundle/Views/Slots/saveprefsbutton.html.php @@ -11,7 +11,6 @@ if (isset($form)) { // add form tag echo ''; - $view['assets']->addCustomDeclaration($view['form']->start($form), 'bodyOpen'); } ?> get('templating.helper.form'); + $params = array_merge( $viewParameters, [ 'form' => $formView, + 'action' => $formHelper->start($formView), 'custom_tag' => '', 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index f62e230e2ca..c7f951b6e13 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -45,6 +45,7 @@ class BuilderSubscriber extends CommonSubscriber * @var PageModel */ protected $pageModel; + protected $pageTokenRegex = '{pagelink=(.*?)}'; protected $dwcTokenRegex = '{dwc=(.*?)}'; protected $langBarRegex = '{langbar}'; @@ -119,8 +120,12 @@ public function onPageBuild(Events\PageBuilderEvent $event) $event->addTokensFromHelper($tokenHelper, $this->pageTokenRegex, 'title', 'id', false, true); // add only filter based dwc tokens - $dwcTokenHelper = new BuilderTokenHelper($this->factory, 'dynamicContent', 'dynamiccontent:dynamiccontents'); - $expr = $this->factory->getDatabase()->getExpressionBuilder()->andX('e.is_campaign_based <> 1 and e.slot_name is not null'); + $dwcTokenHelper = new BuilderTokenHelper( + $this->factory, 'dynamicContent', 'dynamiccontent:dynamiccontents' + ); + $expr = $this->factory->getDatabase()->getExpressionBuilder()->andX( + 'e.is_campaign_based <> 1 and e.slot_name is not null' + ); $tokens = $dwcTokenHelper->getTokens( $this->dwcTokenRegex, '', @@ -205,7 +210,11 @@ public function onPageBuild(Events\PageBuilderEvent $event) 'slot_socialfollow', 600 ); - if ($this->security->isGranted(['page:preference_center:editown', 'page:preference_center:editother'], 'MATCH_ONE')) { + if ($this->security->isGranted( + ['page:preference_center:editown', 'page:preference_center:editother'], + 'MATCH_ONE' + ) + ) { $event->addSlotType( 'segmentlist', $this->translator->trans('mautic.core.slot.label.segmentlist'), @@ -405,8 +414,20 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $savePrefs = $this->renderSavePrefs($params); $content = str_ireplace(self::saveprefsRegex, $savePrefs, $content); } - } + $dom = new DOMDocument('1.0', 'utf-8'); + $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR); + $xpath = new DOMXPath($dom); + $divContent = $xpath->query('//*[@data-prefs-center-first="1"]'); + for ($i = 0; $i < $divContent->length; ++$i) { + $slot = $divContent->item($i); + $newnode = $dom->createElement('startform'); + $firstSibling = $slot->parentNode->firstChild; + $slot->parentNode->firstChild->parentNode->insertBefore($newnode, $slot->parentNode->firstChild); + } + $content = $dom->saveHTML(); + //$content = str_replace('', $params['action'], $content); + } $clickThrough = ['source' => ['page', $page->getId()]]; $tokens = $this->tokenHelper->findPageTokens($content, $clickThrough); @@ -417,6 +438,16 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $event->setContent($content); } + public function appendHTML(DOMNode $parent, $source) + { + $tmpDoc = new DOMDocument(); + $tmpDoc->loadHTML($source); + foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) { + $node = $parent->ownerDocument->importNode($node, true); + $parent->appendChild($node); + } + } + /** * Renders the HTML for the social share buttons. * @@ -442,6 +473,19 @@ protected function renderSocialShareButtons() return $content; } + /** + * @return string + */ + private function setAttributeForFirtSlot() + { + static $exist = false; + if (!$exist) { + $exist = true; + + return 'data-prefs-center-first="1"'; + } + } + /** * Renders the HTML for the segment list. * @@ -454,7 +498,7 @@ protected function renderSegmentList(array $params = []) static $content = ''; if (empty($content)) { - $content = "
\n"; + $content = "
setAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:segmentlist.html.php', $params); $content .= "
\n"; } @@ -472,7 +516,7 @@ protected function renderCategoryList(array $params = []) static $content = ''; if (empty($content)) { - $content = "
\n"; + $content = "
setAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:categorylist.html.php', $params); $content .= "
\n"; } @@ -526,7 +570,7 @@ protected function renderSavePrefs(array $params = []) static $content = ''; if (empty($content)) { - $content = "
\n"; + $content = "
setAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:saveprefsbutton.html.php', $params); $content .= "
\n"; } @@ -572,7 +616,7 @@ protected function renderLanguageBar($page) $related[$parent->getId()] = [ 'lang' => $trans, // Add ntrd to not auto redirect to another language - 'url' => $this->pageModel->generateUrl($parent, false).'?ntrd=1', + 'url' => $this->pageModel->generateUrl($parent, false).'?ntrd=1', ]; foreach ($children as $c) { $lang = $c->getLanguage(); @@ -583,7 +627,7 @@ protected function renderLanguageBar($page) $related[$c->getId()] = [ 'lang' => $trans, // Add ntrd to not auto redirect to another language - 'url' => $this->pageModel->generateUrl($c, false).'?ntrd=1', + 'url' => $this->pageModel->generateUrl($c, false).'?ntrd=1', ]; } } @@ -600,7 +644,10 @@ function ($a, $b) { return; } - $langbar = $this->templating->render('MauticPageBundle:SubscribedEvents\PageToken:langbar.html.php', ['pages' => $related]); + $langbar = $this->templating->render( + 'MauticPageBundle:SubscribedEvents\PageToken:langbar.html.php', + ['pages' => $related] + ); } return $langbar; From 0e69d70e3de6721c19337fd27d55e1a1f1f6dfcb Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 31 Jul 2018 16:16:44 +0200 Subject: [PATCH 003/240] Support for wrap prefs center form --- .../Controller/PublicController.php | 14 ++++----- .../EventListener/BuilderSubscriber.php | 29 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/app/bundles/EmailBundle/Controller/PublicController.php b/app/bundles/EmailBundle/Controller/PublicController.php index c24488890b6..7ec6c9627a3 100644 --- a/app/bundles/EmailBundle/Controller/PublicController.php +++ b/app/bundles/EmailBundle/Controller/PublicController.php @@ -223,7 +223,7 @@ public function unsubscribeAction($idHash) $frequencyPresent = false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency); $tokensPresent = $savePrefsPresent && $frequencyPresent; - if ($savePrefsPresent) { + if ($tokensPresent) { // set custom tag to inject end form // update show pref center slots by looking for their presence in the html /** @var \Mautic\CoreBundle\Templating\Helper\FormHelper $formHelper */ @@ -231,12 +231,12 @@ public function unsubscribeAction($idHash) $params = array_merge( $viewParameters, [ - 'form' => $formView, - 'action' => $formHelper->start($formView), - 'custom_tag' => '', - 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), - 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), - 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), + 'form' => $formView, + 'starform' => $formHelper->start($formView), + 'custom_tag' => '', + 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), + 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), + 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), ] ); // Replace tokens in preference center page diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index c7f951b6e13..a2eba96d059 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -356,6 +356,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); $slot->nodeValue = self::segmentListRegex; + $slot->setAttribute('data-prefs-center', '1'); $content = $dom->saveHTML(); } @@ -363,6 +364,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); $slot->nodeValue = self::categoryListRegex; + $slot->setAttribute('data-prefs-center', '1'); $content = $dom->saveHTML(); } @@ -370,6 +372,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); $slot->nodeValue = self::preferredchannel; + $slot->setAttribute('data-prefs-center', '1'); $content = $dom->saveHTML(); } @@ -377,6 +380,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); $slot->nodeValue = self::channelfrequency; + $slot->setAttribute('data-prefs-center', '1'); $content = $dom->saveHTML(); } @@ -384,6 +388,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); $slot->nodeValue = self::saveprefsRegex; + $slot->setAttribute('data-prefs-center', '1'); $content = $dom->saveHTML(); } @@ -418,15 +423,20 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $dom = new DOMDocument('1.0', 'utf-8'); $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR); $xpath = new DOMXPath($dom); - $divContent = $xpath->query('//*[@data-prefs-center-first="1"]'); + // If use slots + $divContent = $xpath->query('//*[@data-prefs-center="1"]'); + if (!$divContent->length) { + // If use tokens + $divContent = $xpath->query('//*[@data-prefs-center="1"]'); + } for ($i = 0; $i < $divContent->length; ++$i) { - $slot = $divContent->item($i); - $newnode = $dom->createElement('startform'); - $firstSibling = $slot->parentNode->firstChild; - $slot->parentNode->firstChild->parentNode->insertBefore($newnode, $slot->parentNode->firstChild); + $slot = $divContent->item($i); + $newnode = $dom->createElement('startform'); + $slot->parentNode->insertBefore($newnode, $slot); + break; } $content = $dom->saveHTML(); - //$content = str_replace('', $params['action'], $content); + $content = str_replace('', $params['startform'], $content); } $clickThrough = ['source' => ['page', $page->getId()]]; $tokens = $this->tokenHelper->findPageTokens($content, $clickThrough); @@ -478,12 +488,7 @@ protected function renderSocialShareButtons() */ private function setAttributeForFirtSlot() { - static $exist = false; - if (!$exist) { - $exist = true; - - return 'data-prefs-center-first="1"'; - } + return 'data-prefs-center-first="1"'; } /** From 8aaa209ba3207f1fb8982569f441b936857201c1 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 31 Jul 2018 16:23:05 +0200 Subject: [PATCH 004/240] Minor --- .../EmailBundle/Controller/PublicController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/bundles/EmailBundle/Controller/PublicController.php b/app/bundles/EmailBundle/Controller/PublicController.php index 7ec6c9627a3..a097597c0fc 100644 --- a/app/bundles/EmailBundle/Controller/PublicController.php +++ b/app/bundles/EmailBundle/Controller/PublicController.php @@ -223,7 +223,7 @@ public function unsubscribeAction($idHash) $frequencyPresent = false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency); $tokensPresent = $savePrefsPresent && $frequencyPresent; - if ($tokensPresent) { + if ($savePrefsPresent) { // set custom tag to inject end form // update show pref center slots by looking for their presence in the html /** @var \Mautic\CoreBundle\Templating\Helper\FormHelper $formHelper */ @@ -231,12 +231,12 @@ public function unsubscribeAction($idHash) $params = array_merge( $viewParameters, [ - 'form' => $formView, - 'starform' => $formHelper->start($formView), - 'custom_tag' => '', - 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), - 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), - 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), + 'form' => $formView, + 'startform' => $formHelper->start($formView), + 'custom_tag' => '', + 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), + 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), + 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), ] ); // Replace tokens in preference center page From 6c29e9b4f1ed5e29a6bae5f0c16c3d99a42e9d10 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 31 Jul 2018 16:25:34 +0200 Subject: [PATCH 005/240] Support for tokens --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index a2eba96d059..4f7c41a5853 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -427,7 +427,7 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $divContent = $xpath->query('//*[@data-prefs-center="1"]'); if (!$divContent->length) { // If use tokens - $divContent = $xpath->query('//*[@data-prefs-center="1"]'); + $divContent = $xpath->query('//*[@data-prefs-center-first="1"]'); } for ($i = 0; $i < $divContent->length; ++$i) { $slot = $divContent->item($i); From 16a40d8aca8c500c3854db4c97239e732d64a629 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 31 Jul 2018 16:27:23 +0200 Subject: [PATCH 006/240] Revert $tokensPresent --- app/bundles/EmailBundle/Controller/PublicController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/EmailBundle/Controller/PublicController.php b/app/bundles/EmailBundle/Controller/PublicController.php index a097597c0fc..d72e2213885 100644 --- a/app/bundles/EmailBundle/Controller/PublicController.php +++ b/app/bundles/EmailBundle/Controller/PublicController.php @@ -223,7 +223,7 @@ public function unsubscribeAction($idHash) $frequencyPresent = false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency); $tokensPresent = $savePrefsPresent && $frequencyPresent; - if ($savePrefsPresent) { + if ($tokensPresent) { // set custom tag to inject end form // update show pref center slots by looking for their presence in the html /** @var \Mautic\CoreBundle\Templating\Helper\FormHelper $formHelper */ From b9d4b17d75639678fa9125c2fa0609501b4631e1 Mon Sep 17 00:00:00 2001 From: Tim Boesenberg Date: Fri, 28 Sep 2018 14:23:34 -0400 Subject: [PATCH 007/240] adds 'isRedirect' to the `page_hit` queueing payload consumer decides which type of entity to build (either Page or Redirect) based on 'isRedirect' --- .../PageBundle/EventListener/PageSubscriber.php | 9 ++++++++- app/bundles/PageBundle/Model/PageModel.php | 11 ++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/PageSubscriber.php b/app/bundles/PageBundle/EventListener/PageSubscriber.php index 5d7d8f22eeb..d6f4df97a31 100644 --- a/app/bundles/PageBundle/EventListener/PageSubscriber.php +++ b/app/bundles/PageBundle/EventListener/PageSubscriber.php @@ -188,13 +188,20 @@ public function onPageHit(QueueConsumerEvent $event) $trackingNewlyGenerated = $payload['isNew']; $pageId = $payload['pageId']; $leadId = $payload['leadId']; + $isRedirect = $payload['isRedirect']; $hitRepo = $this->em->getRepository('MauticPageBundle:Hit'); $pageRepo = $this->em->getRepository('MauticPageBundle:Page'); + $redirectRepo = $this->em->getRepository('MauticPageBundle:Redirect'); $leadRepo = $this->em->getRepository('MauticLeadBundle:Lead'); $hit = $hitRepo->find((int) $payload['hitId']); - $page = $pageId ? $pageRepo->find((int) $pageId) : null; $lead = $leadId ? $leadRepo->find((int) $leadId) : null; + if ($isRedirect) { + $page = $pageId ? $redirectRepo->find((int) $pageId) : null; + } else { + $page = $pageId ? $pageRepo->find((int) $pageId) : null; + } + $this->pageModel->processPageHit($hit, $page, $request, $lead, $trackingNewlyGenerated, false); $event->setResult(QueueConsumerResults::ACKNOWLEDGE); } diff --git a/app/bundles/PageBundle/Model/PageModel.php b/app/bundles/PageBundle/Model/PageModel.php index 912a1d5d9d4..d9a2a342558 100644 --- a/app/bundles/PageBundle/Model/PageModel.php +++ b/app/bundles/PageBundle/Model/PageModel.php @@ -533,11 +533,12 @@ public function hitPage($page, Request $request, $code = '200', Lead $lead = nul if ($this->queueService->isQueueEnabled()) { $msg = [ - 'hitId' => $hit->getId(), - 'pageId' => $page ? $page->getId() : null, - 'request' => $request, - 'leadId' => $lead ? $lead->getId() : null, - 'isNew' => $this->deviceTracker->wasDeviceChanged(), + 'hitId' => $hit->getId(), + 'pageId' => $page ? $page->getId() : null, + 'request' => $request, + 'leadId' => $lead ? $lead->getId() : null, + 'isNew' => $this->deviceTracker->wasDeviceChanged(), + 'isRedirect' => $page instanceof Redirect ? true : false, ]; $this->queueService->publishToQueue(QueueName::PAGE_HIT, $msg); } else { From 6f9b7677cdbba33c1da88b3b55c8301b90a84e2c Mon Sep 17 00:00:00 2001 From: Tim Boesenberg Date: Fri, 28 Sep 2018 15:47:38 -0400 Subject: [PATCH 008/240] simplify conditional --- app/bundles/PageBundle/Model/PageModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/PageBundle/Model/PageModel.php b/app/bundles/PageBundle/Model/PageModel.php index d9a2a342558..d4c183b627c 100644 --- a/app/bundles/PageBundle/Model/PageModel.php +++ b/app/bundles/PageBundle/Model/PageModel.php @@ -538,7 +538,7 @@ public function hitPage($page, Request $request, $code = '200', Lead $lead = nul 'request' => $request, 'leadId' => $lead ? $lead->getId() : null, 'isNew' => $this->deviceTracker->wasDeviceChanged(), - 'isRedirect' => $page instanceof Redirect ? true : false, + 'isRedirect' => ($page instanceof Redirect), ]; $this->queueService->publishToQueue(QueueName::PAGE_HIT, $msg); } else { From baae7d780d109a839504d075c27a365a84ae604a Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Thu, 4 Oct 2018 12:42:36 +0200 Subject: [PATCH 009/240] Remove empty line --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index 4f7c41a5853..71edc382363 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -45,7 +45,6 @@ class BuilderSubscriber extends CommonSubscriber * @var PageModel */ protected $pageModel; - protected $pageTokenRegex = '{pagelink=(.*?)}'; protected $dwcTokenRegex = '{dwc=(.*?)}'; protected $langBarRegex = '{langbar}'; From 781abf9b22b55d7c71fac03dfb243e253a99c385 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Thu, 4 Oct 2018 12:46:03 +0200 Subject: [PATCH 010/240] Revert back analytics to preview --- app/bundles/EmailBundle/Controller/PublicController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/bundles/EmailBundle/Controller/PublicController.php b/app/bundles/EmailBundle/Controller/PublicController.php index d26109d018e..3dc92d86863 100644 --- a/app/bundles/EmailBundle/Controller/PublicController.php +++ b/app/bundles/EmailBundle/Controller/PublicController.php @@ -510,6 +510,10 @@ function (&$field) { $content = $event->getContent(true); + if ($this->get('mautic.security')->isAnonymous()) { + $content = $this->get('mautic.helper.template.analytics')->addCode($content); + } + return new Response($content); } From e30289b7292bf9f23d648b8c1ca1bdbf85c0d707 Mon Sep 17 00:00:00 2001 From: Adrian Waler Date: Fri, 5 Oct 2018 14:19:45 +0200 Subject: [PATCH 011/240] 6687: fix endless loop --- app/bundles/LeadBundle/Controller/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/LeadBundle/Controller/ListController.php b/app/bundles/LeadBundle/Controller/ListController.php index 7cdf19cd711..e79cf1d8242 100644 --- a/app/bundles/LeadBundle/Controller/ListController.php +++ b/app/bundles/LeadBundle/Controller/ListController.php @@ -80,7 +80,7 @@ public function indexAction($page = 1) $translator = $this->get('translator'); $mine = $translator->trans('mautic.core.searchcommand.ismine'); $global = $translator->trans('mautic.lead.list.searchcommand.isglobal'); - $filter['force'] = " ($mine or $global)"; + $filter['force'] = "($mine or $global)"; } $items = $model->getEntities( From bfc4640b00528e97f8bc2c19c7b48dd2524f77de Mon Sep 17 00:00:00 2001 From: Adrian Waler Date: Fri, 5 Oct 2018 14:20:32 +0200 Subject: [PATCH 012/240] 6688: fix view own segments --- app/bundles/LeadBundle/Controller/ListController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/bundles/LeadBundle/Controller/ListController.php b/app/bundles/LeadBundle/Controller/ListController.php index e79cf1d8242..f1196ad1216 100644 --- a/app/bundles/LeadBundle/Controller/ListController.php +++ b/app/bundles/LeadBundle/Controller/ListController.php @@ -728,9 +728,8 @@ public function viewAction($objectId) ], ]); } elseif (!$this->get('mautic.security')->hasEntityAccess( + 'lead:leads:viewown', 'lead:lists:viewother', - 'lead:lists:editother', - 'lead:lists:deleteother', $list->getCreatedBy() ) ) { From 811e002b0adc87c45bf3b4ceeba91309d846cac9 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 9 Oct 2018 16:23:45 +0200 Subject: [PATCH 013/240] remove old stuff --- .../PageBundle/EventListener/BuilderSubscriber.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index 71edc382363..bc64d635835 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -447,16 +447,6 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $event->setContent($content); } - public function appendHTML(DOMNode $parent, $source) - { - $tmpDoc = new DOMDocument(); - $tmpDoc->loadHTML($source); - foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) { - $node = $parent->ownerDocument->importNode($node, true); - $parent->appendChild($node); - } - } - /** * Renders the HTML for the social share buttons. * From 68811bcbf79efe955944d53d5c5a1f3760aa0183 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Thu, 11 Oct 2018 18:15:17 +0200 Subject: [PATCH 014/240] Fix notice on preview --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index bc64d635835..597d4317736 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -435,7 +435,9 @@ public function onPageDisplay(Events\PageDisplayEvent $event) break; } $content = $dom->saveHTML(); - $content = str_replace('', $params['startform'], $content); + if (isset($params['startform'])) { + $content = str_replace('', $params['startform'], $content); + } } $clickThrough = ['source' => ['page', $page->getId()]]; $tokens = $this->tokenHelper->findPageTokens($content, $clickThrough); From 67a7ce2047bd85e6f23434c7533ce8bd7174d40c Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Fri, 12 Oct 2018 18:11:08 +0200 Subject: [PATCH 015/240] Refactor setAttributeForFirtSlot method --- .../PageBundle/EventListener/BuilderSubscriber.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index 597d4317736..726276c3b2b 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -477,7 +477,7 @@ protected function renderSocialShareButtons() /** * @return string */ - private function setAttributeForFirtSlot() + private function getAttributeForFirtSlot() { return 'data-prefs-center-first="1"'; } @@ -494,7 +494,7 @@ protected function renderSegmentList(array $params = []) static $content = ''; if (empty($content)) { - $content = "
setAttributeForFirtSlot().">\n"; + $content = "
getAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:segmentlist.html.php', $params); $content .= "
\n"; } @@ -512,7 +512,7 @@ protected function renderCategoryList(array $params = []) static $content = ''; if (empty($content)) { - $content = "
setAttributeForFirtSlot().">\n"; + $content = "
getAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:categorylist.html.php', $params); $content .= "
\n"; } @@ -566,7 +566,7 @@ protected function renderSavePrefs(array $params = []) static $content = ''; if (empty($content)) { - $content = "
setAttributeForFirtSlot().">\n"; + $content = "
getAttributeForFirtSlot().">\n"; $content .= $this->templating->render('MauticCoreBundle:Slots:saveprefsbutton.html.php', $params); $content .= "
\n"; } From 8b4b1b10ed16a38d833e2598b37dc4edc5c8d8f3 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Fri, 12 Oct 2018 18:22:32 +0200 Subject: [PATCH 016/240] Remove some spaces --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index 726276c3b2b..ffd286197cf 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -434,9 +434,9 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $slot->parentNode->insertBefore($newnode, $slot); break; } - $content = $dom->saveHTML(); + $content= $dom->saveHTML(); if (isset($params['startform'])) { - $content = str_replace('', $params['startform'], $content); + $content = str_replace('', $params['startform'], $content); } } $clickThrough = ['source' => ['page', $page->getId()]]; From cf2949c66267347c1067dab7ff74cb04ef1e04b8 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Fri, 12 Oct 2018 18:24:26 +0200 Subject: [PATCH 017/240] Remove spaces --- .../EmailBundle/Controller/PublicController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/bundles/EmailBundle/Controller/PublicController.php b/app/bundles/EmailBundle/Controller/PublicController.php index 3dc92d86863..3b04ed07c52 100644 --- a/app/bundles/EmailBundle/Controller/PublicController.php +++ b/app/bundles/EmailBundle/Controller/PublicController.php @@ -219,13 +219,13 @@ public function unsubscribeAction($idHash) $params = array_merge( $viewParameters, [ - 'form' => $formView, - 'startform' => $formHelper->start($formView), - 'custom_tag' => '', - 'showContactFrequency' => false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency), - 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), - 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), - 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), + 'form' => $formView, + 'startform' => $formHelper->start($formView), + 'custom_tag' => '', + 'showContactFrequency' => false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency), + 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), + 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), + 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), ] ); // Replace tokens in preference center page From ccf07fec215727ceab8cb532a541060abe4e7034 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Sun, 14 Oct 2018 12:17:21 +0200 Subject: [PATCH 018/240] Improve to generate start form --- .../EventListener/BuilderSubscriber.php | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index ffd286197cf..5fd4c33ddf2 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -419,26 +419,29 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $content = str_ireplace(self::saveprefsRegex, $savePrefs, $content); } - $dom = new DOMDocument('1.0', 'utf-8'); - $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR); - $xpath = new DOMXPath($dom); - // If use slots - $divContent = $xpath->query('//*[@data-prefs-center="1"]'); - if (!$divContent->length) { - // If use tokens - $divContent = $xpath->query('//*[@data-prefs-center-first="1"]'); - } - for ($i = 0; $i < $divContent->length; ++$i) { - $slot = $divContent->item($i); - $newnode = $dom->createElement('startform'); - $slot->parentNode->insertBefore($newnode, $slot); - break; - } - $content= $dom->saveHTML(); - if (isset($params['startform'])) { + // add form before first block of prefs center + if (isset($params['startform']) && strpos('data-prefs-center', $content) !== false) { + $dom = new DOMDocument('1.0', 'utf-8'); + $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR); + $xpath = new DOMXPath($dom); + // If use slots + $divContent = $xpath->query('//*[@data-prefs-center="1"]'); + if (!$divContent->length) { + // If use tokens + $divContent = $xpath->query('//*[@data-prefs-center-first="1"]'); + } + + if ($divContent->length) { + $slot = $divContent->item(0); + $newnode = $dom->createElement('startform'); + $slot->parentNode->insertBefore($newnode, $slot); + } + + $content = $dom->saveHTML(); $content = str_replace('', $params['startform'], $content); } } + $clickThrough = ['source' => ['page', $page->getId()]]; $tokens = $this->tokenHelper->findPageTokens($content, $clickThrough); From 0eda52bd0c6043683430afce67a2807057e26bf3 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Sun, 14 Oct 2018 12:19:22 +0200 Subject: [PATCH 019/240] Minor --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index 5fd4c33ddf2..d9b91aabe64 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -435,10 +435,9 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $slot = $divContent->item(0); $newnode = $dom->createElement('startform'); $slot->parentNode->insertBefore($newnode, $slot); + $content = $dom->saveHTML(); + $content = str_replace('', $params['startform'], $content); } - - $content = $dom->saveHTML(); - $content = str_replace('', $params['startform'], $content); } } From 1ebbf21205f28ea964a7ae50cf7fa9339d3ec12f Mon Sep 17 00:00:00 2001 From: Mohammadi El Youzghi Date: Mon, 15 Oct 2018 11:00:22 +0200 Subject: [PATCH 020/240] =?UTF-8?q?Regions=20typo=20fix=20(Cord=C3=B3ba=20?= =?UTF-8?q?>=20C=C3=B3rdoba)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/bundles/CoreBundle/Assets/json/regions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/CoreBundle/Assets/json/regions.json b/app/bundles/CoreBundle/Assets/json/regions.json index 52d83833903..b992f78b121 100644 --- a/app/bundles/CoreBundle/Assets/json/regions.json +++ b/app/bundles/CoreBundle/Assets/json/regions.json @@ -1435,7 +1435,7 @@ "Castell\u00f3n", "Ciudad Real", "Cuenca", - "Cord\u00f3ba", + "C\u00f3rdoba", "Girona [Gerona]", "Granada", "Guadalajara", From 730d2898e5377ccc46c07bbb4324e5cf6b1b15d7 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Mon, 15 Oct 2018 20:13:56 +0200 Subject: [PATCH 021/240] Fix strpos condition --- app/bundles/PageBundle/EventListener/BuilderSubscriber.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php index d9b91aabe64..24aef1281ff 100644 --- a/app/bundles/PageBundle/EventListener/BuilderSubscriber.php +++ b/app/bundles/PageBundle/EventListener/BuilderSubscriber.php @@ -418,9 +418,8 @@ public function onPageDisplay(Events\PageDisplayEvent $event) $savePrefs = $this->renderSavePrefs($params); $content = str_ireplace(self::saveprefsRegex, $savePrefs, $content); } - // add form before first block of prefs center - if (isset($params['startform']) && strpos('data-prefs-center', $content) !== false) { + if (isset($params['startform']) && strpos($content, 'data-prefs-center') !== false) { $dom = new DOMDocument('1.0', 'utf-8'); $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR); $xpath = new DOMXPath($dom); From f3a3dd99378d23d6e7491acf686da35c0a633096 Mon Sep 17 00:00:00 2001 From: Mohammadi El Youzghi Date: Wed, 17 Oct 2018 16:35:03 +0200 Subject: [PATCH 022/240] =?UTF-8?q?Added=20migration=20to=20prevent=20BC?= =?UTF-8?q?=20breaks=20for=20C=C3=B3rdoba=20spanish=20region?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CoreBundle/Assets/json/regions.json | 4 +- app/migrations/Version20181017154600.php | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 app/migrations/Version20181017154600.php diff --git a/app/bundles/CoreBundle/Assets/json/regions.json b/app/bundles/CoreBundle/Assets/json/regions.json index b992f78b121..37753221aee 100644 --- a/app/bundles/CoreBundle/Assets/json/regions.json +++ b/app/bundles/CoreBundle/Assets/json/regions.json @@ -1417,7 +1417,7 @@ "Gash-Barka", "Maakel [Maekel]", "Semenawi Keyih Bahri [Semien-Keih-Bahri]" - ], + ], "Spain": [ "\u00c1lava", "Albacete", @@ -4073,4 +4073,4 @@ "St. Eustatius", "St. Maarten" ] -} +} \ No newline at end of file diff --git a/app/migrations/Version20181017154600.php b/app/migrations/Version20181017154600.php new file mode 100644 index 00000000000..f60a3bae322 --- /dev/null +++ b/app/migrations/Version20181017154600.php @@ -0,0 +1,75 @@ + Córdoba). + */ +class Version20181017154600 extends AbstractMauticMigration +{ + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $oldRegionName = 'Cordóba'; + $newRegionName = 'Córdoba'; + + // Fix region name for leads. + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->update("{$this->prefix}leads") + ->set('state', ':newRegionName') + ->where( + $queryBuilder->expr()->eq('state', $queryBuilder->expr()->literal($oldRegionName)) + ) + ->setParameter('newRegionName', $newRegionName, 'string') + ->execute(); + + // Fix region name for companies. + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->update("{$this->prefix}companies") + ->set('companystate', ':newRegionName') + ->where( + $queryBuilder->expr()->eq('companystate', $queryBuilder->expr()->literal($oldRegionName)) + ) + ->setParameter('newRegionName', $newRegionName, 'string') + ->execute(); + + // Fix region name for page hits. + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->update("{$this->prefix}page_hits") + ->set('region', ':newRegionName') + ->where( + $queryBuilder->expr()->eq('region', $queryBuilder->expr()->literal($oldRegionName)) + ) + ->setParameter('newRegionName', $newRegionName, 'string') + ->execute(); + + // Fix region name for video hits. + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->update("{$this->prefix}video_hits") + ->set('region', ':newRegionName') + ->where( + $queryBuilder->expr()->eq('region', $queryBuilder->expr()->literal($oldRegionName)) + ) + ->setParameter('newRegionName', $newRegionName, 'string') + ->execute(); + + // Fix region name for 'filters' field of lead_lists, dynamic_content & reports. + // As the old string & the new one are of the same length, a general 'REPLACE' is OK even on (DC2Type:array). + $this->addSql("UPDATE `{$this->prefix}lead_lists` SET filters = REPLACE(filters, '$oldRegionName', '$newRegionName') WHERE filters LIKE '%$oldRegionName%'"); + $this->addSql("UPDATE `{$this->prefix}dynamic_content` SET filters = REPLACE(filters, '$oldRegionName', '$newRegionName') WHERE filters LIKE '%$oldRegionName%'"); + $this->addSql("UPDATE `{$this->prefix}reports` SET filters = REPLACE(filters, '$oldRegionName', '$newRegionName') WHERE filters LIKE '%$oldRegionName%'"); + } +} From 4507e16d241140cce3236edaaf169bb71d0dd804 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 13 Nov 2018 11:10:37 +0100 Subject: [PATCH 023/240] inital commit --- app/bundles/ReportBundle/Model/ReportExporter.php | 2 -- app/bundles/ReportBundle/Scheduler/Model/SendSchedule.php | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/bundles/ReportBundle/Model/ReportExporter.php b/app/bundles/ReportBundle/Model/ReportExporter.php index 32edbdb8c4f..fd9d6af9899 100644 --- a/app/bundles/ReportBundle/Model/ReportExporter.php +++ b/app/bundles/ReportBundle/Model/ReportExporter.php @@ -102,8 +102,6 @@ private function processReport(Scheduler $scheduler) $event = new ReportScheduleSendEvent($scheduler, $file); $this->eventDispatcher->dispatch(ReportEvents::REPORT_SCHEDULE_SEND, $event); - $this->reportFileWriter->clear($scheduler); - $this->schedulerModel->reportWasScheduled($report); } } diff --git a/app/bundles/ReportBundle/Scheduler/Model/SendSchedule.php b/app/bundles/ReportBundle/Scheduler/Model/SendSchedule.php index 9465ca6f516..9c45a9ca2e8 100644 --- a/app/bundles/ReportBundle/Scheduler/Model/SendSchedule.php +++ b/app/bundles/ReportBundle/Scheduler/Model/SendSchedule.php @@ -39,6 +39,8 @@ public function __construct(MailHelper $mailer, MessageSchedule $messageSchedule */ public function send(Scheduler $scheduler, $filePath) { + $this->mailer->reset(true); + $transformer = new ArrayStringTransformer(); $report = $scheduler->getReport(); $emails = $transformer->reverseTransform($report->getToAddress()); From 1f807d762fbd0f736f9a22d2c957812a00a3bf0c Mon Sep 17 00:00:00 2001 From: Patryk Gruszka Date: Fri, 16 Nov 2018 14:05:12 +0100 Subject: [PATCH 024/240] Fix form actions are not removed through REST API using DELETE --- .../FormBundle/Controller/Api/FormApiController.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/bundles/FormBundle/Controller/Api/FormApiController.php b/app/bundles/FormBundle/Controller/Api/FormApiController.php index 59158daf2ee..c37d60b9931 100644 --- a/app/bundles/FormBundle/Controller/Api/FormApiController.php +++ b/app/bundles/FormBundle/Controller/Api/FormApiController.php @@ -97,15 +97,7 @@ public function deleteActionsAction($formId) return $this->badRequest('The actions attribute must be array.'); } - $currentActions = $entity->getActions(); - - foreach ($currentActions as $currentAction) { - if (in_array($currentAction->getId(), $actionsToDelete)) { - $entity->removeAction($currentAction); - } - } - - $this->model->saveEntity($entity); + $this->model->deleteActions($entity, $actionsToDelete); $view = $this->view([$this->entityNameOne => $entity]); From 0b6aff71b13b711568c42dc00f459606dc8fdca1 Mon Sep 17 00:00:00 2001 From: Patryk Gruszka Date: Fri, 16 Nov 2018 14:38:23 +0100 Subject: [PATCH 025/240] Fix form actions are not updated through REST API using PUT --- .../FormBundle/Controller/Api/FormApiController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/bundles/FormBundle/Controller/Api/FormApiController.php b/app/bundles/FormBundle/Controller/Api/FormApiController.php index 59158daf2ee..d4de9870cd5 100644 --- a/app/bundles/FormBundle/Controller/Api/FormApiController.php +++ b/app/bundles/FormBundle/Controller/Api/FormApiController.php @@ -246,11 +246,17 @@ protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit') // Remove actions which weren't in the PUT request if (!$isNew && $method === 'PUT') { + $actionsToDelete = []; + foreach ($currentActions as $currentAction) { if (!in_array($currentAction->getId(), $requestActionIds)) { - $entity->removeAction($currentAction); + $actionsToDelete[] = $currentAction->getId(); } } + + if ($actionsToDelete) { + $this->model->deleteActions($entity, $actionsToDelete); + } } } From 4025836b17e84bdbae7f90656266365c5bf1f895 Mon Sep 17 00:00:00 2001 From: Woeler Date: Fri, 16 Nov 2018 18:06:11 +0100 Subject: [PATCH 026/240] Clean form field alias with correct method --- app/bundles/FormBundle/Controller/Api/FormApiController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/bundles/FormBundle/Controller/Api/FormApiController.php b/app/bundles/FormBundle/Controller/Api/FormApiController.php index 59158daf2ee..82d72b4f6c9 100644 --- a/app/bundles/FormBundle/Controller/Api/FormApiController.php +++ b/app/bundles/FormBundle/Controller/Api/FormApiController.php @@ -13,7 +13,6 @@ use FOS\RestBundle\Util\Codes; use Mautic\ApiBundle\Controller\CommonApiController; -use Mautic\CoreBundle\Helper\InputHelper; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; @@ -174,7 +173,7 @@ protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit') $fieldEntityArray['formId'] = $formId; if (!empty($fieldParams['alias'])) { - $fieldParams['alias'] = InputHelper::filename($fieldParams['alias']); + $fieldParams['alias'] = $fieldModel->cleanAlias($fieldParams['alias'], '', 25); if (!in_array($fieldParams['alias'], $aliases)) { $fieldEntityArray['alias'] = $fieldParams['alias']; From a91a7028b61cf82b5a2e73f545af8283a05a2983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Sun, 18 Nov 2018 09:54:50 +0100 Subject: [PATCH 027/240] add tagList group on lead post save (#6861) --- app/bundles/LeadBundle/EventListener/WebhookSubscriber.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php b/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php index 756a33917b0..0531e1c5b1f 100644 --- a/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php +++ b/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php @@ -118,6 +118,7 @@ public function onLeadNewUpdate(LeadEvent $event) 'userList', 'publishDetails', 'ipAddress', + 'tagList', ] ); } From 745ddedbba79eff08e55556bc8aa97eccc4643c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Sun, 18 Nov 2018 11:30:58 +0100 Subject: [PATCH 028/240] add tagList group on lead points and channel subscription change --- app/bundles/LeadBundle/EventListener/WebhookSubscriber.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php b/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php index 0531e1c5b1f..312e34db6aa 100644 --- a/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php +++ b/app/bundles/LeadBundle/EventListener/WebhookSubscriber.php @@ -143,6 +143,7 @@ public function onLeadPointChange(PointsChangeEvent $event) 'userList', 'publishDetails', 'ipAddress', + 'tagList', ] ); } @@ -187,6 +188,7 @@ public function onChannelSubscriptionChange(ChannelSubscriptionChange $event) 'userList', 'publishDetails', 'ipAddress', + 'tagList', ] ); } From 5fc5e162b9092dbdffb29984a0238346ef140150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Sun, 18 Nov 2018 17:49:53 +0100 Subject: [PATCH 029/240] fix unit webhook subscriber unit test --- .../LeadBundle/Tests/EventListener/WebhookSubscriberTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bundles/LeadBundle/Tests/EventListener/WebhookSubscriberTest.php b/app/bundles/LeadBundle/Tests/EventListener/WebhookSubscriberTest.php index 2c672cfc70e..5ed142515e1 100644 --- a/app/bundles/LeadBundle/Tests/EventListener/WebhookSubscriberTest.php +++ b/app/bundles/LeadBundle/Tests/EventListener/WebhookSubscriberTest.php @@ -154,6 +154,7 @@ public function testChannelChangeIsPickedUpByWebhook() 'userList', 'publishDetails', 'ipAddress', + 'tagList', ] ); From 6413214e08ef75a43a0e9d8491c843fcfcabbcdd Mon Sep 17 00:00:00 2001 From: Enc3phale Date: Mon, 19 Nov 2018 10:55:27 +0100 Subject: [PATCH 030/240] Fix front js interface bug --- app/bundles/LeadBundle/Assets/js/lead.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/bundles/LeadBundle/Assets/js/lead.js b/app/bundles/LeadBundle/Assets/js/lead.js index 6ed6a89a2c5..4a638eabbbb 100644 --- a/app/bundles/LeadBundle/Assets/js/lead.js +++ b/app/bundles/LeadBundle/Assets/js/lead.js @@ -306,6 +306,7 @@ Mautic.leadlistOnLoad = function(container) { 'fast', function () { mQuery(this).remove(); + Mautic.reorderSegmentFilters(); } ); @@ -389,7 +390,7 @@ Mautic.reorderSegmentFilters = function() { } var newName = prefix+'[filters]['+counter+']['+suffix+']'; - if (name.slice(-2) === '[]') { + if (name !== undefined && name.slice(-2) === '[]') { newName += '[]'; } @@ -408,7 +409,6 @@ Mautic.reorderSegmentFilters = function() { mQuery('#' + prefix + '_filters .panel-heading').removeClass('hide'); mQuery('#' + prefix + '_filters .panel-heading').first().addClass('hide'); - mQuery('#' + prefix + '_filters .panel').first().removeClass('in-group'); }; Mautic.convertLeadFilterInput = function(el) { @@ -1432,10 +1432,11 @@ Mautic.initUniqueIdentifierFields = function() { }; Mautic.updateFilterPositioning = function (el) { - var $el = mQuery(el); + var $el = mQuery(el); var $parentEl = $el.closest('.panel'); + var list = $parentEl.parent().children('.panel'); - if ($el.val() == 'and') { + if ($el.val() == 'and' && list.index($parentEl) !== 0) { $parentEl.addClass('in-group'); } else { $parentEl.removeClass('in-group'); From a12c95f9f7a2956198ff43f7e5c80db1c5a2cc59 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 13:45:16 +0100 Subject: [PATCH 031/240] Add all events for tabs --- .../Controller/CampaignController.php | 3 +-- .../Entity/LeadEventLogRepository.php | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 0c9fc82a636..8b1267da93f 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -657,7 +657,7 @@ protected function getViewArguments(array $args, $action) $eventLogRepo = $this->getDoctrine()->getManager()->getRepository('MauticCampaignBundle:LeadEventLog'); $events = $this->getCampaignModel()->getEventRepository()->getCampaignEvents($entity->getId()); $leadCount = $this->getCampaignModel()->getRepository()->getCampaignLeadCount($entity->getId()); - $campaignLogCounts = $eventLogRepo->getCampaignLogCounts($entity->getId(), false, false); + $campaignLogCounts = $eventLogRepo->getCampaignLogCounts($entity->getId(), false, false, true); $sortedEvents = [ 'decision' => [], 'action' => [], @@ -691,7 +691,6 @@ protected function getViewArguments(array $args, $action) null, ['campaign_id' => $objectId] ); - $session = $this->get('session'); $campaignSources = $this->getCampaignModel()->getSourceLists(); diff --git a/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php b/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php index d7aba37b84b..c4b9f421849 100644 --- a/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php +++ b/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php @@ -215,19 +215,26 @@ public function getUpcomingEvents(array $options = null) /** * @param $campaignId * @param bool $excludeScheduled + * @param bool $excludeNegative + * @param bool $all * * @return array */ - public function getCampaignLogCounts($campaignId, $excludeScheduled = false, $excludeNegative = true) + public function getCampaignLogCounts($campaignId, $excludeScheduled = false, $excludeNegative = true, $all = false) { $q = $this->getSlaveConnection()->createQueryBuilder() - ->from(MAUTIC_TABLE_PREFIX.'campaign_lead_event_log', 'o') - ->innerJoin( - 'o', - MAUTIC_TABLE_PREFIX.'campaign_leads', - 'l', - 'l.campaign_id = '.(int) $campaignId.' and l.manually_removed = 0 and o.lead_id = l.lead_id and l.rotation = o.rotation' - ); + ->from(MAUTIC_TABLE_PREFIX.'campaign_lead_event_log', 'o'); + + $join = 'innerJoin'; + if ($all === true) { + $join = 'leftJoin'; + } + $q->$join( + 'o', + MAUTIC_TABLE_PREFIX.'campaign_leads', + 'l', + 'l.campaign_id = '.(int) $campaignId.' and l.manually_removed = 0 and o.lead_id = l.lead_id and l.rotation = o.rotation' + ); $expr = $q->expr()->andX( $q->expr()->eq('o.campaign_id', (int) $campaignId) From f7f649a1aa9a26ea9813f4dcfc5e6a146f3eab5f Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 13:47:21 +0100 Subject: [PATCH 032/240] Remove empty line --- app/bundles/CampaignBundle/Controller/CampaignController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 8b1267da93f..dcd4987e3d7 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -683,7 +683,6 @@ protected function getViewArguments(array $args, $action) $sortedEvents[$event['eventType']][] = $event; } - $stats = $this->getCampaignModel()->getCampaignMetricsLineChartData( null, new \DateTime($dateRangeForm->get('date_from')->getData()), From c5da20ea2de91c8f1e0fba2bd263f39ab5435706 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 13:49:26 +0100 Subject: [PATCH 033/240] Empty lines --- app/bundles/CampaignBundle/Controller/CampaignController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index dcd4987e3d7..b0940290878 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -683,6 +683,7 @@ protected function getViewArguments(array $args, $action) $sortedEvents[$event['eventType']][] = $event; } + $stats = $this->getCampaignModel()->getCampaignMetricsLineChartData( null, new \DateTime($dateRangeForm->get('date_from')->getData()), @@ -690,6 +691,7 @@ protected function getViewArguments(array $args, $action) null, ['campaign_id' => $objectId] ); + $session = $this->get('session'); $campaignSources = $this->getCampaignModel()->getSourceLists(); From 8292bb2f9f720683c90ad1ad2a4608578ea8d02b Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 16:11:24 +0100 Subject: [PATCH 034/240] Recalcualte events counts --- .../Controller/CampaignController.php | 44 ++++++++++++------- .../Views/Campaign/events.html.php | 2 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index b0940290878..752752d93ff 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -654,36 +654,48 @@ protected function getViewArguments(array $args, $action) $dateRangeForm = $this->get('form.factory')->create('daterange', $dateRangeValues, ['action' => $action]); /** @var LeadEventLogRepository $eventLogRepo */ - $eventLogRepo = $this->getDoctrine()->getManager()->getRepository('MauticCampaignBundle:LeadEventLog'); - $events = $this->getCampaignModel()->getEventRepository()->getCampaignEvents($entity->getId()); - $leadCount = $this->getCampaignModel()->getRepository()->getCampaignLeadCount($entity->getId()); - $campaignLogCounts = $eventLogRepo->getCampaignLogCounts($entity->getId(), false, false, true); - $sortedEvents = [ + $eventLogRepo = $this->getDoctrine()->getManager()->getRepository('MauticCampaignBundle:LeadEventLog'); + $events = $this->getCampaignModel()->getEventRepository()->getCampaignEvents($entity->getId()); + $leadCount = $this->getCampaignModel()->getRepository()->getCampaignLeadCount($entity->getId()); + $campaignLogCounts = $eventLogRepo->getCampaignLogCounts($entity->getId(), false, false, true); + $pendingCampaignLogCounts = $eventLogRepo->getCampaignLogCounts($entity->getId(), false, false); + $sortedEvents = [ 'decision' => [], 'action' => [], 'condition' => [], ]; foreach ($events as $event) { - $event['logCount'] = - $event['percent'] = - $event['yesPercent'] = - $event['noPercent'] = 0; - $event['leadCount'] = $leadCount; + $event['logCount'] = + $event['logCountForPending'] = + $event['percent'] = + $event['yesPercent'] = + $event['noPercent'] = 0; + $event['leadCount'] = $leadCount; if (isset($campaignLogCounts[$event['id']])) { - $event['logCount'] = array_sum($campaignLogCounts[$event['id']]); - + $event['logCount'] = array_sum($campaignLogCounts[$event['id']]); + $event['logCountForPending'] = array_sum($pendingCampaignLogCounts[$event['id']]); + + $pending = $event['leadCount'] - $event['logCountForPending']; + $total = $event['logCount'] + $pending; + $totalYes = $campaignLogCounts[$event['id']][1] + round($pending / 2); + $totalNo = $campaignLogCounts[$event['id']][0] + round($pending / 2); + echo $total; + echo '-'; + echo $totalYes; + echo '-'; + echo $totalNo; + echo '----'; if ($leadCount) { - $event['percent'] = round(($event['logCount'] / $leadCount) * 100, 1); - $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / $leadCount) * 100, 1); - $event['noPercent'] = round(($campaignLogCounts[$event['id']][0] / $leadCount) * 100, 1); + $event['percent'] = round(($event['logCount'] / $total) * 100, 1); + $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / ($totalYes + $totalNo)) * 100, 1); + $event['noPercent'] = $totalNo ? round(($campaignLogCounts[$event['id']][0] / ($totalYes + $totalNo)) * 100, 1) : 0; } } $sortedEvents[$event['eventType']][] = $event; } - $stats = $this->getCampaignModel()->getCampaignMetricsLineChartData( null, new \DateTime($dateRangeForm->get('date_from')->getData()), diff --git a/app/bundles/CampaignBundle/Views/Campaign/events.html.php b/app/bundles/CampaignBundle/Views/Campaign/events.html.php index c4e3dd26d39..9f8d0fcf1f5 100644 --- a/app/bundles/CampaignBundle/Views/Campaign/events.html.php +++ b/app/bundles/CampaignBundle/Views/Campaign/events.html.php @@ -29,7 +29,7 @@ - +
From d743bd04875409b4d758caa0d3a4cb9aedabb19b Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 16:28:01 +0100 Subject: [PATCH 035/240] Minor to change calc --- .../Controller/CampaignController.php | 18 ++++++------------ .../Views/Campaign/events.html.php | 1 + 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 752752d93ff..1f7f2da507f 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -678,19 +678,14 @@ protected function getViewArguments(array $args, $action) $event['logCountForPending'] = array_sum($pendingCampaignLogCounts[$event['id']]); $pending = $event['leadCount'] - $event['logCountForPending']; - $total = $event['logCount'] + $pending; - $totalYes = $campaignLogCounts[$event['id']][1] + round($pending / 2); - $totalNo = $campaignLogCounts[$event['id']][0] + round($pending / 2); - echo $total; - echo '-'; - echo $totalYes; - echo '-'; - echo $totalNo; - echo '----'; + $totalYes = $campaignLogCounts[$event['id']][1]; + $totalNo = $campaignLogCounts[$event['id']][0]; + $total = $totalYes + $totalNo + $pending; + if ($leadCount) { $event['percent'] = round(($event['logCount'] / $total) * 100, 1); - $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / ($totalYes + $totalNo)) * 100, 1); - $event['noPercent'] = $totalNo ? round(($campaignLogCounts[$event['id']][0] / ($totalYes + $totalNo)) * 100, 1) : 0; + $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / $total) * 100, 1); + $event['noPercent'] = $totalNo ? round(($campaignLogCounts[$event['id']][0] / $total) * 100, 1) : 0; } } @@ -703,7 +698,6 @@ protected function getViewArguments(array $args, $action) null, ['campaign_id' => $objectId] ); - $session = $this->get('session'); $campaignSources = $this->getCampaignModel()->getSourceLists(); diff --git a/app/bundles/CampaignBundle/Views/Campaign/events.html.php b/app/bundles/CampaignBundle/Views/Campaign/events.html.php index 9f8d0fcf1f5..8ed9ee78e60 100644 --- a/app/bundles/CampaignBundle/Views/Campaign/events.html.php +++ b/app/bundles/CampaignBundle/Views/Campaign/events.html.php @@ -16,6 +16,7 @@ +
  • From 0d3a84b9c25e6134e8abdca6dfe86c958e028653 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 17:57:45 +0100 Subject: [PATCH 036/240] Share stats data from parent --- .../Controller/CampaignController.php | 25 +++++++++++++++---- .../Views/Campaign/events.html.php | 1 - 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 1f7f2da507f..ab4e1052331 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -664,8 +664,7 @@ protected function getViewArguments(array $args, $action) 'action' => [], 'condition' => [], ]; - - foreach ($events as $event) { + foreach ($events as &$event) { $event['logCount'] = $event['logCountForPending'] = $event['percent'] = @@ -675,22 +674,38 @@ protected function getViewArguments(array $args, $action) if (isset($campaignLogCounts[$event['id']])) { $event['logCount'] = array_sum($campaignLogCounts[$event['id']]); - $event['logCountForPending'] = array_sum($pendingCampaignLogCounts[$event['id']]); + $event['logCountForPending'] = isset($pendingCampaignLogCounts[$event['id']]) ? array_sum($pendingCampaignLogCounts[$event['id']]) : 0; $pending = $event['leadCount'] - $event['logCountForPending']; $totalYes = $campaignLogCounts[$event['id']][1]; $totalNo = $campaignLogCounts[$event['id']][0]; $total = $totalYes + $totalNo + $pending; - if ($leadCount) { $event['percent'] = round(($event['logCount'] / $total) * 100, 1); $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / $total) * 100, 1); - $event['noPercent'] = $totalNo ? round(($campaignLogCounts[$event['id']][0] / $total) * 100, 1) : 0; + $event['noPercent'] = $total ? round(($campaignLogCounts[$event['id']][0] / $total) * 100, 1) : 0; } } + } + // rewrite stats data from parent condition if exist + foreach ($events as &$event) { + if (!empty($event['decisionPath']) && !empty($event['parent_id']) && isset($events[$event['parent_id']])) { + $parentEvent = $events[$event['parent_id']]; + $event['logCount'] = $parentEvent['logCount']; + $event['logCountForPending'] = $parentEvent['logCountForPending']; + $event['percent'] = $parentEvent['percent']; + $event['yesPercent'] = $parentEvent['yesPercent']; + $event['noPercent'] = $parentEvent['noPercent']; + if ($event['decisionPath'] == 'yes') { + $event['noPercent'] = 0; + } else { + $event['yesPercent'] = 0; + } + } $sortedEvents[$event['eventType']][] = $event; } + $stats = $this->getCampaignModel()->getCampaignMetricsLineChartData( null, new \DateTime($dateRangeForm->get('date_from')->getData()), diff --git a/app/bundles/CampaignBundle/Views/Campaign/events.html.php b/app/bundles/CampaignBundle/Views/Campaign/events.html.php index 8ed9ee78e60..9f8d0fcf1f5 100644 --- a/app/bundles/CampaignBundle/Views/Campaign/events.html.php +++ b/app/bundles/CampaignBundle/Views/Campaign/events.html.php @@ -16,7 +16,6 @@ -
  • From 1edd2797b8ffcb57b5c678c5da8b56cfe4c69ec3 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 17:59:07 +0100 Subject: [PATCH 037/240] Remove total condition --- app/bundles/CampaignBundle/Controller/CampaignController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index ab4e1052331..45a5516cc18 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -683,7 +683,7 @@ protected function getViewArguments(array $args, $action) if ($leadCount) { $event['percent'] = round(($event['logCount'] / $total) * 100, 1); $event['yesPercent'] = round(($campaignLogCounts[$event['id']][1] / $total) * 100, 1); - $event['noPercent'] = $total ? round(($campaignLogCounts[$event['id']][0] / $total) * 100, 1) : 0; + $event['noPercent'] = round(($campaignLogCounts[$event['id']][0] / $total) * 100, 1); } } } @@ -713,6 +713,7 @@ protected function getViewArguments(array $args, $action) null, ['campaign_id' => $objectId] ); + $session = $this->get('session'); $campaignSources = $this->getCampaignModel()->getSourceLists(); From 4b7c92d21c76241a510ae4258d33de0b383c7555 Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 18:00:20 +0100 Subject: [PATCH 038/240] Minor --- .../CampaignBundle/Controller/CampaignController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 45a5516cc18..69c7389ab6c 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -665,12 +665,12 @@ protected function getViewArguments(array $args, $action) 'condition' => [], ]; foreach ($events as &$event) { - $event['logCount'] = - $event['logCountForPending'] = - $event['percent'] = - $event['yesPercent'] = - $event['noPercent'] = 0; - $event['leadCount'] = $leadCount; + $event['logCount'] = + $event['logCountForPending'] = + $event['percent'] = + $event['yesPercent'] = + $event['noPercent'] = 0; + $event['leadCount'] = $leadCount; if (isset($campaignLogCounts[$event['id']])) { $event['logCount'] = array_sum($campaignLogCounts[$event['id']]); From e2c0f1dc7e791e6d9742242add93d108c33cf9af Mon Sep 17 00:00:00 2001 From: Zdeno Kuzmany Date: Tue, 20 Nov 2018 18:09:21 +0100 Subject: [PATCH 039/240] Fix action counts --- app/bundles/CampaignBundle/Controller/CampaignController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/bundles/CampaignBundle/Controller/CampaignController.php b/app/bundles/CampaignBundle/Controller/CampaignController.php index 69c7389ab6c..7e6e43f1709 100644 --- a/app/bundles/CampaignBundle/Controller/CampaignController.php +++ b/app/bundles/CampaignBundle/Controller/CampaignController.php @@ -692,7 +692,6 @@ protected function getViewArguments(array $args, $action) foreach ($events as &$event) { if (!empty($event['decisionPath']) && !empty($event['parent_id']) && isset($events[$event['parent_id']])) { $parentEvent = $events[$event['parent_id']]; - $event['logCount'] = $parentEvent['logCount']; $event['logCountForPending'] = $parentEvent['logCountForPending']; $event['percent'] = $parentEvent['percent']; $event['yesPercent'] = $parentEvent['yesPercent']; From fb1e757594c33989d5cd69c723abe9e3660775c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Srde=C4=8Dn=C3=BD?= Date: Thu, 22 Nov 2018 12:46:58 +0100 Subject: [PATCH 040/240] Allow downloading themes when having View permissions. --- app/bundles/CoreBundle/Controller/ThemeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/CoreBundle/Controller/ThemeController.php b/app/bundles/CoreBundle/Controller/ThemeController.php index 8b80451d793..08f0cc87320 100644 --- a/app/bundles/CoreBundle/Controller/ThemeController.php +++ b/app/bundles/CoreBundle/Controller/ThemeController.php @@ -122,7 +122,7 @@ public function downloadAction($themeName) $flashes = []; $error = false; - if (!$this->get('mautic.security')->isGranted('core:themes:edit')) { + if (!$this->get('mautic.security')->isGranted('core:themes:view')) { return $this->accessDenied(); } From ecbc7b8ff643e820862bbdda666c838a0d80bbde Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 26 Nov 2018 00:46:39 -0500 Subject: [PATCH 041/240] Reference system parameters instead of copying This speeds up cache generation and page load time, while shrinking cache file size. Instead of copying a huge array into the PHP code once for each event listener, the existing parameter array is fetched at runtime, allowing it to be shared between listeners (reducing memory overhead) and avoiding array creation overhead. On a development/test install, the size of the service container cache file was reduced by about 1.5M and cache warmup time dropped by about 100ms. --- .../CoreBundle/DependencyInjection/Compiler/EventPass.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/bundles/CoreBundle/DependencyInjection/Compiler/EventPass.php b/app/bundles/CoreBundle/DependencyInjection/Compiler/EventPass.php index bf32f823d5d..633f5d9a0d4 100644 --- a/app/bundles/CoreBundle/DependencyInjection/Compiler/EventPass.php +++ b/app/bundles/CoreBundle/DependencyInjection/Compiler/EventPass.php @@ -15,8 +15,8 @@ use Mautic\WebhookBundle\EventListener\WebhookSubscriberBase; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; /** * Class EventPass. @@ -40,7 +40,7 @@ public function process(ContainerBuilder $container) $definition->addMethodCall('setRequest', [new Reference('request_stack')]); $definition->addMethodCall('setSecurity', [new Reference('mautic.security')]); $definition->addMethodCall('setSerializer', [new Reference('jms_serializer')]); - $definition->addMethodCall('setSystemParameters', [new Parameter('mautic.parameters')]); + $definition->addMethodCall('setSystemParameters', [new Expression("parameter('mautic.parameters')")]); $definition->addMethodCall('setDispatcher', [new Reference('event_dispatcher')]); $definition->addMethodCall('setTranslator', [new Reference('translator')]); $definition->addMethodCall('setEntityManager', [new Reference('doctrine.orm.entity_manager')]); From d6aedbd4ee63aeca69106c78e9135347975c5a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Srde=C4=8Dn=C3=BD?= Date: Mon, 26 Nov 2018 16:43:39 +0100 Subject: [PATCH 042/240] Make item entry rows unclickable when not having permissions to view them. --- .../LeadBundle/Controller/CompanyController.php | 1 + app/bundles/LeadBundle/Views/Company/list.html.php | 11 +++++++++++ app/bundles/PointBundle/Views/Point/list.html.php | 14 +++++++++----- app/bundles/StageBundle/Views/Stage/list.html.php | 4 ++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/bundles/LeadBundle/Controller/CompanyController.php b/app/bundles/LeadBundle/Controller/CompanyController.php index 37c1d1469a0..ebdc54cfd99 100644 --- a/app/bundles/LeadBundle/Controller/CompanyController.php +++ b/app/bundles/LeadBundle/Controller/CompanyController.php @@ -35,6 +35,7 @@ public function indexAction($page = 1) 'lead:leads:viewother', 'lead:leads:create', 'lead:leads:editother', + 'lead:leads:editown', 'lead:leads:deleteother', ], 'RETURN_ARRAY' diff --git a/app/bundles/LeadBundle/Views/Company/list.html.php b/app/bundles/LeadBundle/Views/Company/list.html.php index 04db7f82c3c..a4e55cf56f9 100644 --- a/app/bundles/LeadBundle/Views/Company/list.html.php +++ b/app/bundles/LeadBundle/Views/Company/list.html.php @@ -108,6 +108,12 @@
    + hasEntityAccess( + $permissions['lead:leads:editown'], + $permissions['lead:leads:editother'], + $item->getCreatedBy() + ) + ): ?> path( - 'mautic_point_action', - ['objectAction' => 'edit', 'objectId' => $item->getId()] - ); ?>" data-toggle="ajax"> + + + getName(); ?> + + getName(); ?> - +
    getDescription()): ?>
    diff --git a/app/bundles/StageBundle/Views/Stage/list.html.php b/app/bundles/StageBundle/Views/Stage/list.html.php index b1c1d81427e..8432cc0499b 100644 --- a/app/bundles/StageBundle/Views/Stage/list.html.php +++ b/app/bundles/StageBundle/Views/Stage/list.html.php @@ -90,12 +90,16 @@ 'MauticCoreBundle:Helper:publishstatus_icon.html.php', ['item' => $item, 'model' => 'stage'] ); ?> + getName(); ?> + + getName(); ?> +
    getDescription()): ?>
    From 924ba04645ffaa79ce01bebe943dcf9e4c569403 Mon Sep 17 00:00:00 2001 From: luke Date: Wed, 28 Nov 2018 22:07:33 +0100 Subject: [PATCH 043/240] Report field system compatibility issue in MySQL8 --- app/bundles/ReportBundle/Entity/Report.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/ReportBundle/Entity/Report.php b/app/bundles/ReportBundle/Entity/Report.php index 1c13eeb9a0f..3402cc40cb5 100644 --- a/app/bundles/ReportBundle/Entity/Report.php +++ b/app/bundles/ReportBundle/Entity/Report.php @@ -133,7 +133,7 @@ public static function loadMetadata(ORM\ClassMetadata $metadata) $builder->addIdColumns(); - $builder->addField('system', Type::BOOLEAN); + $builder->addField('system', Type::BOOLEAN, array("columnName"=>"`system`")); $builder->addField('source', Type::STRING); From f30745af3f3bf8d850b1c0bb2e65ee53acf6c2f9 Mon Sep 17 00:00:00 2001 From: luke Date: Thu, 29 Nov 2018 10:07:34 +0100 Subject: [PATCH 044/240] Mysql 8 compatibility --- app/bundles/ReportBundle/Entity/Report.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/ReportBundle/Entity/Report.php b/app/bundles/ReportBundle/Entity/Report.php index 3402cc40cb5..6302dd602a3 100644 --- a/app/bundles/ReportBundle/Entity/Report.php +++ b/app/bundles/ReportBundle/Entity/Report.php @@ -133,7 +133,7 @@ public static function loadMetadata(ORM\ClassMetadata $metadata) $builder->addIdColumns(); - $builder->addField('system', Type::BOOLEAN, array("columnName"=>"`system`")); + $builder->addField('system', Type::BOOLEAN, ["columnName"=>"`system`"]); $builder->addField('source', Type::STRING); From aeec272ef61bfd9692c9826a6fd6d651d747c236 Mon Sep 17 00:00:00 2001 From: luke Date: Thu, 29 Nov 2018 10:09:12 +0100 Subject: [PATCH 045/240] Mysql 8 compatibility --- app/bundles/ReportBundle/Entity/Report.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/ReportBundle/Entity/Report.php b/app/bundles/ReportBundle/Entity/Report.php index 6302dd602a3..3ef83242b86 100644 --- a/app/bundles/ReportBundle/Entity/Report.php +++ b/app/bundles/ReportBundle/Entity/Report.php @@ -133,7 +133,7 @@ public static function loadMetadata(ORM\ClassMetadata $metadata) $builder->addIdColumns(); - $builder->addField('system', Type::BOOLEAN, ["columnName"=>"`system`"]); + $builder->addField('system', Type::BOOLEAN, ['columnName'=>'`system`']); $builder->addField('source', Type::STRING); From b709bc4cf0d24767f6565361e099a635d1ddc016 Mon Sep 17 00:00:00 2001 From: Enc3phale Date: Thu, 29 Nov 2018 15:02:32 +0100 Subject: [PATCH 046/240] Add user exist verification --- app/bundles/EmailBundle/Model/EmailModel.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/bundles/EmailBundle/Model/EmailModel.php b/app/bundles/EmailBundle/Model/EmailModel.php index e0ccfb3c9f0..818599b3f15 100644 --- a/app/bundles/EmailBundle/Model/EmailModel.php +++ b/app/bundles/EmailBundle/Model/EmailModel.php @@ -1610,7 +1610,12 @@ public function sendEmailToUser( } if (!isset($user['email'])) { - $userEntity = $this->userModel->getEntity($id); + $userEntity = $this->userModel->getEntity($id); + + if ($userEntity === null) { + continue; + } + $user['email'] = $userEntity->getEmail(); $user['firstname'] = $userEntity->getFirstName(); $user['lastname'] = $userEntity->getLastName(); From f5df1ab20d8835f48139af5ad918dbc0376c4d39 Mon Sep 17 00:00:00 2001 From: heathdutton Date: Thu, 29 Nov 2018 14:56:19 -0500 Subject: [PATCH 047/240] Update xdan/datetimepicker to 2.5.20. --- .../css/libraries/other/datetimepicker.less | 2 +- .../Assets/js/libraries/7.datetimepicker.js | 4742 +++++++++-------- 2 files changed, 2654 insertions(+), 2090 deletions(-) diff --git a/app/bundles/CoreBundle/Assets/css/libraries/other/datetimepicker.less b/app/bundles/CoreBundle/Assets/css/libraries/other/datetimepicker.less index 493bb231f57..0ac2232e6e5 100644 --- a/app/bundles/CoreBundle/Assets/css/libraries/other/datetimepicker.less +++ b/app/bundles/CoreBundle/Assets/css/libraries/other/datetimepicker.less @@ -102,7 +102,7 @@ margin-bottom: 3px } -.xdsoft_datetimepicker .xdsoft_mounthpicker { +.xdsoft_datetimepicker .xdsoft_monthpicker { position: relative; text-align: center; } diff --git a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js index 398a70c79b0..318c79d6ed9 100644 --- a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js +++ b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js @@ -1,2092 +1,2656 @@ /** - * @preserve jQuery DateTimePicker plugin v2.4.5 + * @preserve jQuery DateTimePicker * @homepage http://xdsoft.net/jqplugins/datetimepicker/ - * (c) 2014, Chupurnov Valeriy. + * @author Chupurnov Valeriy () */ -/*global document,window,jQuery,setTimeout,clearTimeout,HighlightedDate,getCurrentValue*/ -(function ($) { - 'use strict'; - var default_options = { - i18n: { - ar: { // Arabic - months: [ - "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول" - ], - dayOfWeek: [ - "ن", "ث", "ع", "خ", "ج", "س", "ح" - ] - }, - ro: { // Romanian - months: [ - "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie" - ], - dayOfWeek: [ - "l", "ma", "mi", "j", "v", "s", "d" - ] - }, - id: { // Indonesian - months: [ - "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" - ], - dayOfWeek: [ - "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab" - ] - }, - is: { // Icelandic - months: [ - "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember" - ], - dayOfWeek: [ - "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau" - ] - }, - bg: { // Bulgarian - months: [ - "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември" - ], - dayOfWeek: [ - "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ] - }, - fa: { // Persian/Farsi - months: [ - 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' - ], - dayOfWeek: [ - 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه' - ] - }, - ru: { // Russian - months: [ - 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' - ], - dayOfWeek: [ - "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ] - }, - uk: { // Ukrainian - months: [ - 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень' - ], - dayOfWeek: [ - "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт" - ] - }, - en: { // English - months: [ - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ], - dayOfWeek: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ] - }, - el: { // Ελληνικά - months: [ - "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος" - ], - dayOfWeek: [ - "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ" - ] - }, - de: { // German - months: [ - 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' - ], - dayOfWeek: [ - "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" - ] - }, - nl: { // Dutch - months: [ - "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december" - ], - dayOfWeek: [ - "zo", "ma", "di", "wo", "do", "vr", "za" - ] - }, - tr: { // Turkish - months: [ - "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık" - ], - dayOfWeek: [ - "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts" - ] - }, - fr: { //French - months: [ - "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" - ], - dayOfWeek: [ - "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" - ] - }, - es: { // Spanish - months: [ - "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" - ], - dayOfWeek: [ - "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb" - ] - }, - th: { // Thai - months: [ - 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม' - ], - dayOfWeek: [ - 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.' - ] - }, - pl: { // Polish - months: [ - "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" - ], - dayOfWeek: [ - "nd", "pn", "wt", "śr", "cz", "pt", "sb" - ] - }, - pt: { // Portuguese - months: [ - "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" - ], - dayOfWeek: [ - "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" - ] - }, - ch: { // Simplified Chinese - months: [ - "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" - ], - dayOfWeek: [ - "日", "一", "二", "三", "四", "五", "六" - ] - }, - se: { // Swedish - months: [ - "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" - ], - dayOfWeek: [ - "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" - ] - }, - kr: { // Korean - months: [ - "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" - ], - dayOfWeek: [ - "일", "월", "화", "수", "목", "금", "토" - ] - }, - it: { // Italian - months: [ - "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" - ], - dayOfWeek: [ - "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" - ] - }, - da: { // Dansk - months: [ - "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December" - ], - dayOfWeek: [ - "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ] - }, - no: { // Norwegian - months: [ - "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember" - ], - dayOfWeek: [ - "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ] - }, - ja: { // Japanese - months: [ - "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" - ], - dayOfWeek: [ - "日", "月", "火", "水", "木", "金", "土" - ] - }, - vi: { // Vietnamese - months: [ - "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12" - ], - dayOfWeek: [ - "CN", "T2", "T3", "T4", "T5", "T6", "T7" - ] - }, - sl: { // Slovenščina - months: [ - "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December" - ], - dayOfWeek: [ - "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob" - ] - }, - cs: { // Čeština - months: [ - "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec" - ], - dayOfWeek: [ - "Ne", "Po", "Út", "St", "Čt", "Pá", "So" - ] - }, - hu: { // Hungarian - months: [ - "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December" - ], - dayOfWeek: [ - "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo" - ] - }, - az: { //Azerbaijanian (Azeri) - months: [ - "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr" - ], - dayOfWeek: [ - "B", "Be", "Ça", "Ç", "Ca", "C", "Ş" - ] - }, - bs: { //Bosanski - months: [ - "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" - ], - dayOfWeek: [ - "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" - ] - }, - ca: { //Català - months: [ - "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre" - ], - dayOfWeek: [ - "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds" - ] - }, - 'en-GB': { //English (British) - months: [ - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ], - dayOfWeek: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ] - }, - et: { //"Eesti" - months: [ - "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember" - ], - dayOfWeek: [ - "P", "E", "T", "K", "N", "R", "L" - ] - }, - eu: { //Euskara - months: [ - "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua" - ], - dayOfWeek: [ - "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La." - ] - }, - fi: { //Finnish (Suomi) - months: [ - "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu" - ], - dayOfWeek: [ - "Su", "Ma", "Ti", "Ke", "To", "Pe", "La" - ] - }, - gl: { //Galego - months: [ - "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec" - ], - dayOfWeek: [ - "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab" - ] - }, - hr: { //Hrvatski - months: [ - "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac" - ], - dayOfWeek: [ - "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" - ] - }, - ko: { //Korean (한국어) - months: [ - "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" - ], - dayOfWeek: [ - "일", "월", "화", "수", "목", "금", "토" - ] - }, - lt: { //Lithuanian (lietuvių) - months: [ - "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio" - ], - dayOfWeek: [ - "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš" - ] - }, - lv: { //Latvian (Latviešu) - months: [ - "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris" - ], - dayOfWeek: [ - "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St" - ] - }, - mk: { //Macedonian (Македонски) - months: [ - "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември" - ], - dayOfWeek: [ - "нед", "пон", "вто", "сре", "чет", "пет", "саб" - ] - }, - mn: { //Mongolian (Монгол) - months: [ - "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар" - ], - dayOfWeek: [ - "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням" - ] - }, - 'pt-BR': { //Português(Brasil) - months: [ - "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" - ], - dayOfWeek: [ - "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb" - ] - }, - sk: { //Slovenčina - months: [ - "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December" - ], - dayOfWeek: [ - "Ne", "Po", "Ut", "St", "Št", "Pi", "So" - ] - }, - sq: { //Albanian (Shqip) - months: [ - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ], - dayOfWeek: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ] - }, - 'sr-YU': { //Serbian (Srpski) - months: [ - "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" - ], - dayOfWeek: [ - "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub" - ] - }, - sr: { //Serbian Cyrillic (Српски) - months: [ - "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар" - ], - dayOfWeek: [ - "нед", "пон", "уто", "сре", "чет", "пет", "суб" - ] - }, - sv: { //Svenska - months: [ - "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" - ], - dayOfWeek: [ - "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" - ] - }, - 'zh-TW': { //Traditional Chinese (繁體中文) - months: [ - "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" - ], - dayOfWeek: [ - "日", "一", "二", "三", "四", "五", "六" - ] - }, - zh: { //Simplified Chinese (简体中文) - months: [ - "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" - ], - dayOfWeek: [ - "日", "一", "二", "三", "四", "五", "六" - ] - }, - he: { //Hebrew (עברית) - months: [ - 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר' - ], - dayOfWeek: [ - 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת' - ] - }, - hy: { // Armenian - months: [ - "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր" - ], - dayOfWeek: [ - "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ" - ] - }, - kg: { // Kyrgyz - months: [ - 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы' - ], - dayOfWeek: [ - "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише" - ] - } - }, - value: '', - lang: 'en', - rtl: false, - - format: 'Y/m/d H:i', - formatTime: 'H:i', - formatDate: 'Y/m/d', - - startDate: false, // new Date(), '1986/12/08', '-1970/01/05','-1970/01/05', - step: 60, - monthChangeSpinner: true, - - closeOnDateSelect: false, - closeOnTimeSelect: true, - closeOnWithoutClick: true, - closeOnInputClick: true, - - timepicker: true, - datepicker: true, - weeks: false, - - defaultTime: false, // use formatTime format (ex. '10:00' for formatTime: 'H:i') - defaultDate: false, // use formatDate format (ex new Date() or '1986/12/08' or '-1970/01/05' or '-1970/01/05') - - minDate: false, - maxDate: false, - minTime: false, - maxTime: false, - disabledMinTime: false, - disabledMaxTime: false, - - allowTimes: [], - opened: false, - initTime: true, - inline: false, - theme: '', - - onSelectDate: function () {}, - onSelectTime: function () {}, - onChangeMonth: function () {}, - onChangeYear: function () {}, - onChangeDateTime: function () {}, - onShow: function () {}, - onClose: function () {}, - onGenerate: function () {}, - - withoutCopyright: true, - inverseButton: false, - hours12: false, - next: 'xdsoft_next', - prev : 'xdsoft_prev', - dayOfWeekStart: 0, - parentID: 'body', - timeHeightInTimePicker: 25, - timepickerScrollbar: true, - todayButton: true, - prevButton: true, - nextButton: true, - defaultSelect: true, - - scrollMonth: true, - scrollTime: true, - scrollInput: true, - - lazyInit: false, - mask: false, - validateOnBlur: true, - allowBlank: true, - yearStart: 1950, - yearEnd: 2050, - monthStart: 0, - monthEnd: 11, - style: '', - id: '', - fixed: false, - roundTime: 'round', // ceil, floor - className: '', - weekends: [], - highlightedDates: [], - highlightedPeriods: [], - disabledDates : [], - disabledWeekDays: [], - yearOffset: 0, - beforeShowDay: null, - - enterLikeTab: true, - showApplyButton: false - }; - // fix for ie8 - if (!window.getComputedStyle) { - window.getComputedStyle = function (el, pseudo) { - this.el = el; - this.getPropertyValue = function (prop) { - var re = /(\-([a-z]){1})/g; - if (prop === 'float') { - prop = 'styleFloat'; - } - if (re.test(prop)) { - prop = prop.replace(re, function (a, b, c) { - return c.toUpperCase(); - }); - } - return el.currentStyle[prop] || null; - }; - return this; - }; - } - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (obj, start) { - var i, j; - for (i = (start || 0), j = this.length; i < j; i += 1) { - if (this[i] === obj) { return i; } - } - return -1; - }; - } - Date.prototype.countDaysInMonth = function () { - return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate(); - }; - $.fn.xdsoftScroller = function (percent) { - return this.each(function () { - var timeboxparent = $(this), - pointerEventToXY = function (e) { - var out = {x: 0, y: 0}, - touch; - if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') { - touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; - out.x = touch.clientX; - out.y = touch.clientY; - } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'mousemove' || e.type === 'mouseover' || e.type === 'mouseout' || e.type === 'mouseenter' || e.type === 'mouseleave') { - out.x = e.clientX; - out.y = e.clientY; - } - return out; - }, - move = 0, - timebox, - parentHeight, - height, - scrollbar, - scroller, - maximumOffset = 100, - start = false, - startY = 0, - startTop = 0, - h1 = 0, - touchStart = false, - startTopScroll = 0, - calcOffset = function () {}; - if (percent === 'hide') { - timeboxparent.find('.xdsoft_scrollbar').hide(); - return; - } - if (!$(this).hasClass('xdsoft_scroller_box')) { - timebox = timeboxparent.children().eq(0); - parentHeight = timeboxparent[0].clientHeight; - height = timebox[0].offsetHeight; - scrollbar = $('
    '); - scroller = $('
    '); - scrollbar.append(scroller); - - timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar); - calcOffset = function calcOffset(event) { - var offset = pointerEventToXY(event).y - startY + startTopScroll; - if (offset < 0) { - offset = 0; - } - if (offset + scroller[0].offsetHeight > h1) { - offset = h1 - scroller[0].offsetHeight; - } - timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]); - }; - - scroller - .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) { - if (!parentHeight) { - timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); - } - - startY = pointerEventToXY(event).y; - startTopScroll = parseInt(scroller.css('margin-top'), 10); - h1 = scrollbar[0].offsetHeight; - - if (event.type === 'mousedown') { - if (document) { - $(document.body).addClass('xdsoft_noselect'); - } - $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() { - $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee) - .off('mousemove.xdsoft_scroller', calcOffset) - .removeClass('xdsoft_noselect'); - }); - $(document.body).on('mousemove.xdsoft_scroller', calcOffset); - } else { - touchStart = true; - event.stopPropagation(); - event.preventDefault(); - } - }) - .on('touchmove', function (event) { - if (touchStart) { - event.preventDefault(); - calcOffset(event); - } - }) - .on('touchend touchcancel', function (event) { - touchStart = false; - startTopScroll = 0; - }); - - timeboxparent - .on('scroll_element.xdsoft_scroller', function (event, percentage) { - if (!parentHeight) { - timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]); - } - percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage; - - scroller.css('margin-top', maximumOffset * percentage); - - setTimeout(function () { - timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10)); - }, 10); - }) - .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) { - var percent, sh; - parentHeight = timeboxparent[0].clientHeight; - height = timebox[0].offsetHeight; - percent = parentHeight / height; - sh = percent * scrollbar[0].offsetHeight; - if (percent > 1) { - scroller.hide(); - } else { - scroller.show(); - scroller.css('height', parseInt(sh > 10 ? sh : 10, 10)); - maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight; - if (noTriggerScroll !== true) { - timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]); - } - } - }); - - timeboxparent.on('mousewheel', function (event) { - var top = Math.abs(parseInt(timebox.css('marginTop'), 10)); - - top = top - (event.deltaY * 20); - if (top < 0) { - top = 0; - } - - timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]); - event.stopPropagation(); - return false; - }); - - timeboxparent.on('touchstart', function (event) { - start = pointerEventToXY(event); - startTop = Math.abs(parseInt(timebox.css('marginTop'), 10)); - }); - - timeboxparent.on('touchmove', function (event) { - if (start) { - event.preventDefault(); - var coord = pointerEventToXY(event); - timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]); - } - }); - - timeboxparent.on('touchend touchcancel', function (event) { - start = false; - startTop = 0; - }); - } - timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); - }); - }; - - $.fn.datetimepicker = function (opt) { - var KEY0 = 48, - KEY9 = 57, - _KEY0 = 96, - _KEY9 = 105, - CTRLKEY = 17, - DEL = 46, - ENTER = 13, - ESC = 27, - BACKSPACE = 8, - ARROWLEFT = 37, - ARROWUP = 38, - ARROWRIGHT = 39, - ARROWDOWN = 40, - TAB = 9, - F5 = 116, - AKEY = 65, - CKEY = 67, - VKEY = 86, - ZKEY = 90, - YKEY = 89, - ctrlDown = false, - options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options), - - lazyInitTimer = 0, - createDateTimePicker, - destroyDateTimePicker, - - lazyInit = function (input) { - input - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) { - if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) { - return; - } - clearTimeout(lazyInitTimer); - lazyInitTimer = setTimeout(function () { - - if (!input.data('xdsoft_datetimepicker')) { - createDateTimePicker(input); - } - input - .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback) - .trigger('open.xdsoft'); - }, 100); - }); - }; - - createDateTimePicker = function (input) { - var datetimepicker = $('
    '), - xdsoft_copyright = $(''), - datepicker = $('
    '), - mounth_picker = $('
    ' + - '
    ' + - '
    ' + - '
    '), - calendar = $('
    '), - timepicker = $('
    '), - timeboxparent = timepicker.find('.xdsoft_time_box').eq(0), - timebox = $('
    '), - applyButton = $(''), - /*scrollbar = $('
    '), - scroller = $('
    '),*/ - monthselect = $('
    '), - yearselect = $('
    '), - triggerAfterOpen = false, - XDSoft_datetime, - //scroll_element, - xchangeTimer, - timerclick, - current_time_index, - setPos, - timer = 0, - timer1 = 0, - _xdsoft_datetime; - - if (options.id) { - datetimepicker.attr('id', options.id); - } - if (options.style) { - datetimepicker.attr('style', options.style); - } - if (options.weeks) { - datetimepicker.addClass('xdsoft_showweeks'); - } - if (options.rtl) { - datetimepicker.addClass('xdsoft_rtl'); - } - - datetimepicker.addClass('xdsoft_' + options.theme); - datetimepicker.addClass(options.className); - - mounth_picker - .find('.xdsoft_month span') - .after(monthselect); - mounth_picker - .find('.xdsoft_year span') - .after(yearselect); - - mounth_picker - .find('.xdsoft_month,.xdsoft_year') - .on('mousedown.xdsoft', function (event) { - var select = $(this).find('.xdsoft_select').eq(0), - val = 0, - top = 0, - visible = select.is(':visible'), - items, - i; - - mounth_picker - .find('.xdsoft_select') - .hide(); - if (_xdsoft_datetime.currentTime) { - val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear'](); - } - - select[visible ? 'hide' : 'show'](); - for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) { - if (items.eq(i).data('value') === val) { - break; - } else { - top += items[0].offsetHeight; - } - } - - select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight))); - event.stopPropagation(); - return false; - }); - - mounth_picker - .find('.xdsoft_select') - .xdsoftScroller() - .on('mousedown.xdsoft', function (event) { - event.stopPropagation(); - event.preventDefault(); - }) - .on('mousedown.xdsoft', '.xdsoft_option', function (event) { - - if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - } - - var year = _xdsoft_datetime.currentTime.getFullYear(); - if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { - _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); - } - - $(this).parent().parent().hide(); - - datetimepicker.trigger('xchange.xdsoft'); - if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { - options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - - if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { - options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - }); - - datetimepicker.setOptions = function (_options) { - var highlightedDates = {}, - getCaretPos = function (input) { - try { - if (document.selection && document.selection.createRange) { - var range = document.selection.createRange(); - return range.getBookmark().charCodeAt(2) - 2; - } - if (input.setSelectionRange) { - return input.selectionStart; - } - } catch (e) { - return 0; - } - }, - setCaretPos = function (node, pos) { - node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node; - if (!node) { - return false; - } - if (node.createTextRange) { - var textRange = node.createTextRange(); - textRange.collapse(true); - textRange.moveEnd('character', pos); - textRange.moveStart('character', pos); - textRange.select(); - return true; - } - if (node.setSelectionRange) { - node.setSelectionRange(pos, pos); - return true; - } - return false; - }, - isValidValue = function (mask, value) { - var reg = mask - .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') - .replace(/_/g, '{digit+}') - .replace(/([0-9]{1})/g, '{digit$1}') - .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') - .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); - return (new RegExp(reg)).test(value); - }; - options = $.extend(true, {}, options, _options); - - if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) { - options.allowTimes = $.extend(true, [], _options.allowTimes); - } - - if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) { - options.weekends = $.extend(true, [], _options.weekends); - } - - if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) { - $.each(_options.highlightedDates, function (index, value) { - var splitData = $.map(value.split(','), $.trim), - exDesc, - hDate = new HighlightedDate(Date.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style - keyDate = hDate.date.dateFormat(options.formatDate); - if (highlightedDates[keyDate] !== undefined) { - exDesc = highlightedDates[keyDate].desc; - if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { - highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; - } - } else { - highlightedDates[keyDate] = hDate; - } - }); - - options.highlightedDates = $.extend(true, [], highlightedDates); - } - - if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) { - highlightedDates = $.extend(true, [], options.highlightedDates); - $.each(_options.highlightedPeriods, function (index, value) { - var dateTest, // start date - dateEnd, - desc, - hDate, - keyDate, - exDesc, - style; - if ($.isArray(value)) { - dateTest = value[0]; - dateEnd = value[1]; - desc = value[2]; - style = value[3]; - } - else { - var splitData = $.map(value.split(','), $.trim); - dateTest = Date.parseDate(splitData[0], options.formatDate); - dateEnd = Date.parseDate(splitData[1], options.formatDate); - desc = splitData[2]; - style = splitData[3]; - } - - while (dateTest <= dateEnd) { - hDate = new HighlightedDate(dateTest, desc, style); - keyDate = dateTest.dateFormat(options.formatDate); - dateTest.setDate(dateTest.getDate() + 1); - if (highlightedDates[keyDate] !== undefined) { - exDesc = highlightedDates[keyDate].desc; - if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { - highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; - } - } else { - highlightedDates[keyDate] = hDate; - } - } - }); - - options.highlightedDates = $.extend(true, [], highlightedDates); - } - - if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) { - options.disabledDates = $.extend(true, [], _options.disabledDates); - } - - if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) { - options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays); - } - - if ((options.open || options.opened) && (!options.inline)) { - input.trigger('open.xdsoft'); - } - - if (options.inline) { - triggerAfterOpen = true; - datetimepicker.addClass('xdsoft_inline'); - input.after(datetimepicker).hide(); - } - - if (options.inverseButton) { - options.next = 'xdsoft_prev'; - options.prev = 'xdsoft_next'; - } - - if (options.datepicker) { - datepicker.addClass('active'); - } else { - datepicker.removeClass('active'); - } - - if (options.timepicker) { - timepicker.addClass('active'); - } else { - timepicker.removeClass('active'); - } - - if (options.value) { - _xdsoft_datetime.setCurrentTime(options.value); - if (input && input.val) { - input.val(_xdsoft_datetime.str); - } - } - - if (isNaN(options.dayOfWeekStart)) { - options.dayOfWeekStart = 0; - } else { - options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7; - } - - if (!options.timepickerScrollbar) { - timeboxparent.xdsoftScroller('hide'); - } - - if (options.minDate && /^[\+\-](.*)$/.test(options.minDate)) { - options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate); - } - - if (options.maxDate && /^[\+\-](.*)$/.test(options.maxDate)) { - options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate); - } - - applyButton.toggle(options.showApplyButton); - - mounth_picker - .find('.xdsoft_today_button') - .css('visibility', !options.todayButton ? 'hidden' : 'visible'); - - mounth_picker - .find('.' + options.prev) - .css('visibility', !options.prevButton ? 'hidden' : 'visible'); - - mounth_picker - .find('.' + options.next) - .css('visibility', !options.nextButton ? 'hidden' : 'visible'); - - if (options.mask) { - input.off('keydown.xdsoft'); - - if (options.mask === true) { - options.mask = options.format - .replace(/Y/g, '9999') - .replace(/F/g, '9999') - .replace(/m/g, '19') - .replace(/d/g, '39') - .replace(/H/g, '29') - .replace(/i/g, '59') - .replace(/s/g, '59'); - } - - if ($.type(options.mask) === 'string') { - if (!isValidValue(options.mask, input.val())) { - input.val(options.mask.replace(/[0-9]/g, '_')); - } - - input.on('keydown.xdsoft', function (event) { - var val = this.value, - key = event.which, - pos, - digit; - - if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) { - pos = getCaretPos(this); - digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_'; - - if ((key === BACKSPACE || key === DEL) && pos) { - pos -= 1; - digit = '_'; - } - - while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { - pos += (key === BACKSPACE || key === DEL) ? -1 : 1; - } - - val = val.substr(0, pos) + digit + val.substr(pos + 1); - if ($.trim(val) === '') { - val = options.mask.replace(/[0-9]/g, '_'); - } else { - if (pos === options.mask.length) { - event.preventDefault(); - return false; - } - } - - pos += (key === BACKSPACE || key === DEL) ? 0 : 1; - while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { - pos += (key === BACKSPACE || key === DEL) ? -1 : 1; - } - - if (isValidValue(options.mask, val)) { - this.value = val; - setCaretPos(this, pos); - } else if ($.trim(val) === '') { - this.value = options.mask.replace(/[0-9]/g, '_'); - } else { - input.trigger('error_input.xdsoft'); - } - } else { - if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { - return true; - } - } - - event.preventDefault(); - return false; - }); - } - } - if (options.validateOnBlur) { - input - .off('blur.xdsoft') - .on('blur.xdsoft', function () { - if (options.allowBlank && !$.trim($(this).val()).length) { - $(this).val(null); - datetimepicker.data('xdsoft_datetime').empty(); - } else if (!Date.parseDate($(this).val(), options.format)) { - var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')), - splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join('')); - - // parse the numbers as 0312 => 03:12 - if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) { - $(this).val([splittedHours, splittedMinutes].map(function (item) { - return item > 9 ? item : '0' + item; - }).join(':')); - } else { - $(this).val((_xdsoft_datetime.now()).dateFormat(options.format)); - } - - datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); - } else { - datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); - } - - datetimepicker.trigger('changedatetime.xdsoft'); - }); - } - options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1; - - datetimepicker - .trigger('xchange.xdsoft') - .trigger('afterOpen.xdsoft'); - }; - - datetimepicker - .data('options', options) - .on('mousedown.xdsoft', function (event) { - event.stopPropagation(); - event.preventDefault(); - yearselect.hide(); - monthselect.hide(); - return false; - }); - - //scroll_element = timepicker.find('.xdsoft_time_box'); - timeboxparent.append(timebox); - timeboxparent.xdsoftScroller(); - - datetimepicker.on('afterOpen.xdsoft', function () { - timeboxparent.xdsoftScroller(); - }); - - datetimepicker - .append(datepicker) - .append(timepicker); - - if (options.withoutCopyright !== true) { - datetimepicker - .append(xdsoft_copyright); - } - - datepicker - .append(mounth_picker) - .append(calendar) - .append(applyButton); - - $(options.parentID) - .append(datetimepicker); - - XDSoft_datetime = function () { - var _this = this; - _this.now = function (norecursion) { - var d = new Date(), - date, - time; - - if (!norecursion && options.defaultDate) { - date = _this.strToDateTime(options.defaultDate); - d.setFullYear(date.getFullYear()); - d.setMonth(date.getMonth()); - d.setDate(date.getDate()); - } - - if (options.yearOffset) { - d.setFullYear(d.getFullYear() + options.yearOffset); - } - - if (!norecursion && options.defaultTime) { - time = _this.strtotime(options.defaultTime); - d.setHours(time.getHours()); - d.setMinutes(time.getMinutes()); - } - return d; - }; - - _this.isValidDate = function (d) { - if (Object.prototype.toString.call(d) !== "[object Date]") { - return false; - } - return !isNaN(d.getTime()); - }; - - _this.setCurrentTime = function (dTime) { - _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now(); - datetimepicker.trigger('xchange.xdsoft'); - }; - - _this.empty = function () { - _this.currentTime = null; - }; - - _this.getCurrentTime = function (dTime) { - return _this.currentTime; - }; - - _this.nextMonth = function () { - - if (_this.currentTime === undefined || _this.currentTime === null) { - _this.currentTime = _this.now(); - } - - var month = _this.currentTime.getMonth() + 1, - year; - if (month === 12) { - _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1); - month = 0; - } - - year = _this.currentTime.getFullYear(); - - _this.currentTime.setDate( - Math.min( - new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), - _this.currentTime.getDate() - ) - ); - _this.currentTime.setMonth(month); - - if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { - options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - - if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { - options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - - datetimepicker.trigger('xchange.xdsoft'); - return month; - }; - - _this.prevMonth = function () { - - if (_this.currentTime === undefined || _this.currentTime === null) { - _this.currentTime = _this.now(); - } - - var month = _this.currentTime.getMonth() - 1; - if (month === -1) { - _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1); - month = 11; - } - _this.currentTime.setDate( - Math.min( - new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), - _this.currentTime.getDate() - ) - ); - _this.currentTime.setMonth(month); - if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { - options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - datetimepicker.trigger('xchange.xdsoft'); - return month; - }; - - _this.getWeekOfYear = function (datetime) { - var onejan = new Date(datetime.getFullYear(), 0, 1); - return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7); - }; - - _this.strToDateTime = function (sDateTime) { - var tmpDate = [], timeOffset, currentTime; - - if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) { - return sDateTime; - } - - tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime); - if (tmpDate) { - tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate); - } - if (tmpDate && tmpDate[2]) { - timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000; - currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); - } else { - currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now(); - } - - if (!_this.isValidDate(currentTime)) { - currentTime = _this.now(); - } - - return currentTime; - }; - - _this.strToDate = function (sDate) { - if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) { - return sDate; - } - - var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true); - if (!_this.isValidDate(currentTime)) { - currentTime = _this.now(true); - } - return currentTime; - }; - - _this.strtotime = function (sTime) { - if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) { - return sTime; - } - var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true); - if (!_this.isValidDate(currentTime)) { - currentTime = _this.now(true); - } - return currentTime; - }; - - _this.str = function () { - return _this.currentTime.dateFormat(options.format); - }; - _this.currentTime = this.now(); - }; - - _xdsoft_datetime = new XDSoft_datetime(); - - applyButton.on('click', function (e) {//pathbrite - e.preventDefault(); - datetimepicker.data('changed', true); - _xdsoft_datetime.setCurrentTime(getCurrentValue()); - input.val(_xdsoft_datetime.str()); - datetimepicker.trigger('close.xdsoft'); - }); - mounth_picker - .find('.xdsoft_today_button') - .on('mousedown.xdsoft', function () { - datetimepicker.data('changed', true); - _xdsoft_datetime.setCurrentTime(0); - datetimepicker.trigger('afterOpen.xdsoft'); - }).on('dblclick.xdsoft', function () { - var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate; - currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); - minDate = _xdsoft_datetime.strToDate(options.minDate); - minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); - if (currentDate < minDate) { - return; - } - maxDate = _xdsoft_datetime.strToDate(options.maxDate); - maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()); - if (currentDate > maxDate) { - return; - } - input.val(_xdsoft_datetime.str()); - input.trigger('change'); - datetimepicker.trigger('close.xdsoft'); - }); - mounth_picker - .find('.xdsoft_prev,.xdsoft_next') - .on('mousedown.xdsoft', function () { - var $this = $(this), - timer = 0, - stop = false; - - (function arguments_callee1(v) { - if ($this.hasClass(options.next)) { - _xdsoft_datetime.nextMonth(); - } else if ($this.hasClass(options.prev)) { - _xdsoft_datetime.prevMonth(); - } - if (options.monthChangeSpinner) { - if (!stop) { - timer = setTimeout(arguments_callee1, v || 100); - } - } - }(500)); - - $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() { - clearTimeout(timer); - stop = true; - $([document.body, window]).off('mouseup.xdsoft', arguments_callee2); - }); - }); - - timepicker - .find('.xdsoft_prev,.xdsoft_next') - .on('mousedown.xdsoft', function () { - var $this = $(this), - timer = 0, - stop = false, - period = 110; - (function arguments_callee4(v) { - var pheight = timeboxparent[0].clientHeight, - height = timebox[0].offsetHeight, - top = Math.abs(parseInt(timebox.css('marginTop'), 10)); - if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) { - timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px'); - } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) { - timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px'); - } - timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]); - period = (period > 10) ? 10 : period - 10; - if (!stop) { - timer = setTimeout(arguments_callee4, v || period); - } - }(500)); - $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() { - clearTimeout(timer); - stop = true; - $([document.body, window]) - .off('mouseup.xdsoft', arguments_callee5); - }); - }); - - xchangeTimer = 0; - // base handler - generating a calendar and timepicker - datetimepicker - .on('xchange.xdsoft', function (event) { - clearTimeout(xchangeTimer); - xchangeTimer = setTimeout(function () { - - if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - } - - var table = '', - start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0), - i = 0, - j, - today = _xdsoft_datetime.now(), - maxDate = false, - minDate = false, - hDate, - day, - d, - y, - m, - w, - classes = [], - customDateSettings, - newRow = true, - time = '', - h = '', - line_time, - description; - - while (start.getDay() !== options.dayOfWeekStart) { - start.setDate(start.getDate() - 1); - } - - table += ''; - - if (options.weeks) { - table += ''; - } - - for (j = 0; j < 7; j += 1) { - table += ''; - } - - table += ''; - table += ''; - - if (options.maxDate !== false) { - maxDate = _xdsoft_datetime.strToDate(options.maxDate); - maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999); - } - - if (options.minDate !== false) { - minDate = _xdsoft_datetime.strToDate(options.minDate); - minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); - } - - while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) { - classes = []; - i += 1; - - day = start.getDay(); - d = start.getDate(); - y = start.getFullYear(); - m = start.getMonth(); - w = _xdsoft_datetime.getWeekOfYear(start); - description = ''; - - classes.push('xdsoft_date'); - - if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) { - customDateSettings = options.beforeShowDay.call(datetimepicker, start); - } else { - customDateSettings = null; - } - - if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) { - classes.push('xdsoft_disabled'); - } else if (options.disabledDates.indexOf(start.dateFormat(options.formatDate)) !== -1) { - classes.push('xdsoft_disabled'); - } else if (options.disabledWeekDays.indexOf(day) !== -1) { - classes.push('xdsoft_disabled'); - } - - if (customDateSettings && customDateSettings[1] !== "") { - classes.push(customDateSettings[1]); - } - - if (_xdsoft_datetime.currentTime.getMonth() !== m) { - classes.push('xdsoft_other_month'); - } - - if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { - classes.push('xdsoft_current'); - } - - if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { - classes.push('xdsoft_today'); - } - - if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) !== -1) { - classes.push('xdsoft_weekend'); - } - - if (options.highlightedDates[start.dateFormat(options.formatDate)] !== undefined) { - hDate = options.highlightedDates[start.dateFormat(options.formatDate)]; - classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style); - description = hDate.desc === undefined ? '' : hDate.desc; - } - - if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) { - classes.push(options.beforeShowDay(start)); - } - - if (newRow) { - table += ''; - newRow = false; - if (options.weeks) { - table += ''; - } - } - - table += ''; - - if (start.getDay() === options.dayOfWeekStartPrev) { - table += ''; - newRow = true; - } - - start.setDate(d + 1); - } - table += '
    ' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + '
    ' + w + '' + - '
    ' + d + '
    ' + - '
    '; - - calendar.html(table); - - mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]); - mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear()); - - // generate timebox - time = ''; - h = ''; - m = ''; - line_time = function line_time(h, m) { - var now = _xdsoft_datetime.now(), optionDateTime, current_time; - now.setHours(h); - h = parseInt(now.getHours(), 10); - now.setMinutes(m); - m = parseInt(now.getMinutes(), 10); - optionDateTime = new Date(_xdsoft_datetime.currentTime); - optionDateTime.setHours(h); - optionDateTime.setMinutes(m); - classes = []; - if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || (options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) { - classes.push('xdsoft_disabled'); - } - if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || ((options.disabledMinTime !== false && now.getTime() > _xdsoft_datetime.strtotime(options.disabledMinTime).getTime()) && (options.disabledMaxTime !== false && now.getTime() < _xdsoft_datetime.strtotime(options.disabledMaxTime).getTime()))) { - classes.push('xdsoft_disabled'); - } - - current_time = new Date(_xdsoft_datetime.currentTime); - current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10)); - current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step); - - if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && (options.step > 59 || current_time.getMinutes() === parseInt(m, 10))) { - if (options.defaultSelect || datetimepicker.data('changed')) { - classes.push('xdsoft_current'); - } else if (options.initTime) { - classes.push('xdsoft_init_time'); - } - } - if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) { - classes.push('xdsoft_today'); - } - time += '
    ' + now.dateFormat(options.formatTime) + '
    '; - }; - - if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) { - for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) { - for (j = 0; j < 60; j += options.step) { - h = (i < 10 ? '0' : '') + i; - m = (j < 10 ? '0' : '') + j; - line_time(h, m); - } - } - } else { - for (i = 0; i < options.allowTimes.length; i += 1) { - h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours(); - m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes(); - line_time(h, m); - } - } - - timebox.html(time); - - opt = ''; - i = 0; - - for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) { - opt += '
    ' + i + '
    '; - } - yearselect.children().eq(0) - .html(opt); - - for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) { - opt += '
    ' + options.i18n[options.lang].months[i] + '
    '; - } - monthselect.children().eq(0).html(opt); - $(datetimepicker) - .trigger('generate.xdsoft'); - }, 10); - event.stopPropagation(); - }) - .on('afterOpen.xdsoft', function () { - if (options.timepicker) { - var classType, pheight, height, top; - if (timebox.find('.xdsoft_current').length) { - classType = '.xdsoft_current'; - } else if (timebox.find('.xdsoft_init_time').length) { - classType = '.xdsoft_init_time'; - } - if (classType) { - pheight = timeboxparent[0].clientHeight; - height = timebox[0].offsetHeight; - top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1; - if ((height - pheight) < top) { - top = height - pheight; - } - timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]); - } else { - timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]); - } - } - }); - - timerclick = 0; - calendar - .on('click.xdsoft', 'td', function (xdevent) { - xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap - timerclick += 1; - var $this = $(this), - currentTime = _xdsoft_datetime.currentTime; - - if (currentTime === undefined || currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - currentTime = _xdsoft_datetime.currentTime; - } - - if ($this.hasClass('xdsoft_disabled')) { - return false; - } - - currentTime.setDate(1); - currentTime.setFullYear($this.data('year')); - currentTime.setMonth($this.data('month')); - currentTime.setDate($this.data('date')); - - datetimepicker.trigger('select.xdsoft', [currentTime]); - - input.val(_xdsoft_datetime.str()); - if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) { - datetimepicker.trigger('close.xdsoft'); - } - - if (options.onSelectDate && $.isFunction(options.onSelectDate)) { - options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); - } - - datetimepicker.data('changed', true); - datetimepicker.trigger('xchange.xdsoft'); - datetimepicker.trigger('changedatetime.xdsoft'); - setTimeout(function () { - timerclick = 0; - }, 200); - }); - - timebox - .on('click.xdsoft', 'div', function (xdevent) { - xdevent.stopPropagation(); - var $this = $(this), - currentTime = _xdsoft_datetime.currentTime; - - if (currentTime === undefined || currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - currentTime = _xdsoft_datetime.currentTime; - } - - if ($this.hasClass('xdsoft_disabled')) { - return false; - } - currentTime.setHours($this.data('hour')); - currentTime.setMinutes($this.data('minute')); - datetimepicker.trigger('select.xdsoft', [currentTime]); - - datetimepicker.data('input').val(_xdsoft_datetime.str()); - - if (options.inline !== true && options.closeOnTimeSelect === true) { - datetimepicker.trigger('close.xdsoft'); - } - - if (options.onSelectTime && $.isFunction(options.onSelectTime)) { - options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); - } - datetimepicker.data('changed', true); - datetimepicker.trigger('xchange.xdsoft'); - datetimepicker.trigger('changedatetime.xdsoft'); - }); - - - datepicker - .on('mousewheel.xdsoft', function (event) { - if (!options.scrollMonth) { - return true; - } - if (event.deltaY < 0) { - _xdsoft_datetime.nextMonth(); - } else { - _xdsoft_datetime.prevMonth(); - } - return false; - }); - - input - .on('mousewheel.xdsoft', function (event) { - if (!options.scrollInput) { - return true; - } - if (!options.datepicker && options.timepicker) { - current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0; - if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) { - current_time_index += event.deltaY; - } - if (timebox.children().eq(current_time_index).length) { - timebox.children().eq(current_time_index).trigger('mousedown'); - } - return false; - } - if (options.datepicker && !options.timepicker) { - datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]); - if (input.val) { - input.val(_xdsoft_datetime.str()); - } - datetimepicker.trigger('changedatetime.xdsoft'); - return false; - } - }); - - datetimepicker - .on('changedatetime.xdsoft', function (event) { - if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) { - var $input = datetimepicker.data('input'); - options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event); - delete options.value; - $input.trigger('change'); - } - }) - .on('generate.xdsoft', function () { - if (options.onGenerate && $.isFunction(options.onGenerate)) { - options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } - if (triggerAfterOpen) { - datetimepicker.trigger('afterOpen.xdsoft'); - triggerAfterOpen = false; - } - }) - .on('click.xdsoft', function (xdevent) { - xdevent.stopPropagation(); - }); - - current_time_index = 0; - - setPos = function () { - var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute", node; - if (datetimepicker.data('input').parent().css('direction') == 'rtl') - left -= (datetimepicker.outerWidth() - datetimepicker.data('input').outerWidth()); - if (options.fixed) { - top -= $(window).scrollTop(); - left -= $(window).scrollLeft(); - position = "fixed"; - } else { - if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) { - top = offset.top - datetimepicker[0].offsetHeight + 1; - } - if (top < 0) { - top = 0; - } - if (left + datetimepicker[0].offsetWidth > $(window).width()) { - left = $(window).width() - datetimepicker[0].offsetWidth; - } - } - - node = datetimepicker[0]; - do { - node = node.parentNode; - if (window.getComputedStyle(node).getPropertyValue('position') === 'relative' && $(window).width() >= node.offsetWidth) { - left = left - (($(window).width() - node.offsetWidth) / 2); - break; - } - } while (node.nodeName !== 'HTML'); - datetimepicker.css({ - left: left, - top: top, - position: position - }); - }; - datetimepicker - .on('open.xdsoft', function (event) { - var onShow = true; - if (options.onShow && $.isFunction(options.onShow)) { - onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); - } - if (onShow !== false) { - datetimepicker.show(); - setPos(); - $(window) - .off('resize.xdsoft', setPos) - .on('resize.xdsoft', setPos); - - if (options.closeOnWithoutClick) { - $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() { - datetimepicker.trigger('close.xdsoft'); - $([document.body, window]).off('mousedown.xdsoft', arguments_callee6); - }); - } - } - }) - .on('close.xdsoft', function (event) { - var onClose = true; - mounth_picker - .find('.xdsoft_month,.xdsoft_year') - .find('.xdsoft_select') - .hide(); - if (options.onClose && $.isFunction(options.onClose)) { - onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); - } - if (onClose !== false && !options.opened && !options.inline) { - datetimepicker.hide(); - } - event.stopPropagation(); - }) - .on('toggle.xdsoft', function (event) { - if (datetimepicker.is(':visible')) { - datetimepicker.trigger('close.xdsoft'); - } else { - datetimepicker.trigger('open.xdsoft'); - } - }) - .data('input', input); - - timer = 0; - timer1 = 0; - - datetimepicker.data('xdsoft_datetime', _xdsoft_datetime); - datetimepicker.setOptions(options); - - function getCurrentValue() { - var ct = false, time; - - if (options.startDate) { - ct = _xdsoft_datetime.strToDate(options.startDate); - } else { - ct = options.value || ((input && input.val && input.val()) ? input.val() : ''); - if (ct) { - ct = _xdsoft_datetime.strToDateTime(ct); - } else if (options.defaultDate) { - ct = _xdsoft_datetime.strToDateTime(options.defaultDate); - if (options.defaultTime) { - time = _xdsoft_datetime.strtotime(options.defaultTime); - ct.setHours(time.getHours()); - ct.setMinutes(time.getMinutes()); - } - } - } - - if (ct && _xdsoft_datetime.isValidDate(ct)) { - datetimepicker.data('changed', true); - } else { - ct = ''; - } - - return ct || 0; - } - - _xdsoft_datetime.setCurrentTime(getCurrentValue()); - - input - .data('xdsoft_datetimepicker', datetimepicker) - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) { - if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { - return; - } - clearTimeout(timer); - timer = setTimeout(function () { - if (input.is(':disabled')) { - return; - } - - triggerAfterOpen = true; - _xdsoft_datetime.setCurrentTime(getCurrentValue()); - - datetimepicker.trigger('open.xdsoft'); - }, 100); - }) - .on('keydown.xdsoft', function (event) { - var val = this.value, elementSelector, - key = event.which; - if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) { - elementSelector = $("input:visible,textarea:visible"); - datetimepicker.trigger('close.xdsoft'); - elementSelector.eq(elementSelector.index(this) + 1).focus(); - return false; - } - if ([TAB].indexOf(key) !== -1) { - datetimepicker.trigger('close.xdsoft'); - return true; - } - }); - }; - destroyDateTimePicker = function (input) { - var datetimepicker = input.data('xdsoft_datetimepicker'); - if (datetimepicker) { - datetimepicker.data('xdsoft_datetime', null); - datetimepicker.remove(); - input - .data('xdsoft_datetimepicker', null) - .off('.xdsoft'); - $(window).off('resize.xdsoft'); - $([window, document.body]).off('mousedown.xdsoft'); - if (input.unmousewheel) { - input.unmousewheel(); - } - } - }; - $(document) - .off('keydown.xdsoftctrl keyup.xdsoftctrl') - .on('keydown.xdsoftctrl', function (e) { - if (e.keyCode === CTRLKEY) { - ctrlDown = true; - } - }) - .on('keyup.xdsoftctrl', function (e) { - if (e.keyCode === CTRLKEY) { - ctrlDown = false; - } - }); - return this.each(function () { - var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input; - if (datetimepicker) { - if ($.type(opt) === 'string') { - switch (opt) { - case 'show': - $(this).select().focus(); - datetimepicker.trigger('open.xdsoft'); - break; - case 'hide': - datetimepicker.trigger('close.xdsoft'); - break; - case 'toggle': - datetimepicker.trigger('toggle.xdsoft'); - break; - case 'destroy': - destroyDateTimePicker($(this)); - break; - case 'reset': - this.value = this.defaultValue; - if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) { - datetimepicker.data('changed', false); - } - datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value); - break; - case 'validate': - $input = datetimepicker.data('input'); - $input.trigger('blur.xdsoft'); - break; - } - } else { - datetimepicker - .setOptions(opt); - } - return 0; - } - if ($.type(opt) !== 'string') { - if (!options.lazyInit || options.open || options.inline) { - createDateTimePicker($(this)); - } else { - lazyInit($(this)); - } - } - }); - }; - $.fn.datetimepicker.defaults = default_options; -}(jQuery)); - -function HighlightedDate(date, desc, style) { - "use strict"; - this.date = date; - this.desc = desc; - this.style = style; -} - -(function () { - - /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) - * Licensed under the MIT License (LICENSE.txt). - * - * Version: 3.1.12 - * - * Requires: jQuery 1.2.2+ - */ - !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); - -// Parse and Format Library -//http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/ - /* - * Copyright (C) 2004 Baron Schwartz - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, version 2.1. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var codePrefix="Date.prototype."+funcName+" = function() {return ";var code="";var special=false;var ch="";for(var i=0;i 0) {";var regex="";var special=false;var ch="";for(var i=0;i 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$",'i');eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b
    '); + scroller = $('
    '); + scrollbar.append(scroller); + + timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar); + calcOffset = function calcOffset(event) { + var offset = pointerEventToXY(event).y - startY + startTopScroll; + if (offset < 0) { + offset = 0; + } + if (offset + scroller[0].offsetHeight > h1) { + offset = h1 - scroller[0].offsetHeight; + } + timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]); + }; + + scroller + .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) { + if (!parentHeight) { + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); + } + + startY = pointerEventToXY(event).y; + startTopScroll = parseInt(scroller.css('margin-top'), 10); + h1 = scrollbar[0].offsetHeight; + + if (event.type === 'mousedown' || event.type === 'touchstart') { + if (options.ownerDocument) { + $(options.ownerDocument.body).addClass('xdsoft_noselect'); + } + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft_scroller', function arguments_callee() { + $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft_scroller', arguments_callee) + .off('mousemove.xdsoft_scroller', calcOffset) + .removeClass('xdsoft_noselect'); + }); + $(options.ownerDocument.body).on('mousemove.xdsoft_scroller', calcOffset); + } else { + touchStart = true; + event.stopPropagation(); + event.preventDefault(); + } + }) + .on('touchmove', function (event) { + if (touchStart) { + event.preventDefault(); + calcOffset(event); + } + }) + .on('touchend touchcancel', function () { + touchStart = false; + startTopScroll = 0; + }); + + timeboxparent + .on('scroll_element.xdsoft_scroller', function (event, percentage) { + if (!parentHeight) { + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]); + } + percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage; + + scroller.css('margin-top', maximumOffset * percentage); + + setTimeout(function () { + timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10)); + }, 10); + }) + .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) { + var percent, sh; + parentHeight = timeboxparent[0].clientHeight; + height = timebox[0].offsetHeight; + percent = parentHeight / height; + sh = percent * scrollbar[0].offsetHeight; + if (percent > 1) { + scroller.hide(); + } else { + scroller.show(); + scroller.css('height', parseInt(sh > 10 ? sh : 10, 10)); + maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight; + if (noTriggerScroll !== true) { + timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]); + } + } + }); + + timeboxparent.on('mousewheel', function (event) { + var top = Math.abs(parseInt(timebox.css('marginTop'), 10)); + + top = top - (event.deltaY * 20); + if (top < 0) { + top = 0; + } + + timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]); + event.stopPropagation(); + return false; + }); + + timeboxparent.on('touchstart', function (event) { + start = pointerEventToXY(event); + startTop = Math.abs(parseInt(timebox.css('marginTop'), 10)); + }); + + timeboxparent.on('touchmove', function (event) { + if (start) { + event.preventDefault(); + var coord = pointerEventToXY(event); + timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]); + } + }); + + timeboxparent.on('touchend touchcancel', function () { + start = false; + startTop = 0; + }); + } + timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]); + }); + }; + + $.fn.datetimepicker = function (opt, opt2) { + var result = this, + KEY0 = 48, + KEY9 = 57, + _KEY0 = 96, + _KEY9 = 105, + CTRLKEY = 17, + DEL = 46, + ENTER = 13, + ESC = 27, + BACKSPACE = 8, + ARROWLEFT = 37, + ARROWUP = 38, + ARROWRIGHT = 39, + ARROWDOWN = 40, + TAB = 9, + F5 = 116, + AKEY = 65, + CKEY = 67, + VKEY = 86, + ZKEY = 90, + YKEY = 89, + ctrlDown = false, + options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options), + + lazyInitTimer = 0, + createDateTimePicker, + destroyDateTimePicker, + + lazyInit = function (input) { + input + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function initOnActionCallback() { + if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) { + return; + } + clearTimeout(lazyInitTimer); + lazyInitTimer = setTimeout(function () { + + if (!input.data('xdsoft_datetimepicker')) { + createDateTimePicker(input); + } + input + .off('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', initOnActionCallback) + .trigger('open.xdsoft'); + }, 100); + }); + }; + + createDateTimePicker = function (input) { + var datetimepicker = $('
    '), + xdsoft_copyright = $(''), + datepicker = $('
    '), + month_picker = $('
    ' + + '
    ' + + '
    ' + + '
    '), + calendar = $('
    '), + timepicker = $('
    '), + timeboxparent = timepicker.find('.xdsoft_time_box').eq(0), + timebox = $('
    '), + applyButton = $(''), + + monthselect = $('
    '), + yearselect = $('
    '), + triggerAfterOpen = false, + XDSoft_datetime, + + xchangeTimer, + timerclick, + current_time_index, + setPos, + timer = 0, + _xdsoft_datetime, + forEachAncestorOf; + + if (options.id) { + datetimepicker.attr('id', options.id); + } + if (options.style) { + datetimepicker.attr('style', options.style); + } + if (options.weeks) { + datetimepicker.addClass('xdsoft_showweeks'); + } + if (options.rtl) { + datetimepicker.addClass('xdsoft_rtl'); + } + + datetimepicker.addClass('xdsoft_' + options.theme); + datetimepicker.addClass(options.className); + + month_picker + .find('.xdsoft_month span') + .after(monthselect); + month_picker + .find('.xdsoft_year span') + .after(yearselect); + + month_picker + .find('.xdsoft_month,.xdsoft_year') + .on('touchstart mousedown.xdsoft', function (event) { + var select = $(this).find('.xdsoft_select').eq(0), + val = 0, + top = 0, + visible = select.is(':visible'), + items, + i; + + month_picker + .find('.xdsoft_select') + .hide(); + if (_xdsoft_datetime.currentTime) { + val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear'](); + } + + select[visible ? 'hide' : 'show'](); + for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) { + if (items.eq(i).data('value') === val) { + break; + } else { + top += items[0].offsetHeight; + } + } + + select.xdsoftScroller(options, top / (select.children()[0].offsetHeight - (select[0].clientHeight))); + event.stopPropagation(); + return false; + }); + + var handleTouchMoved = function (event) { + var evt = event.originalEvent; + var touchPosition = evt.touches ? evt.touches[0] : evt; + this.touchStartPosition = this.touchStartPosition || touchPosition; + var xMovement = Math.abs(this.touchStartPosition.clientX - touchPosition.clientX); + var yMovement = Math.abs(this.touchStartPosition.clientY - touchPosition.clientY); + var distance = Math.sqrt(xMovement * xMovement + yMovement * yMovement); + if(distance > options.touchMovedThreshold) { + this.touchMoved = true; + } + } + + month_picker + .find('.xdsoft_select') + .xdsoftScroller(options) + .on('touchstart mousedown.xdsoft', function (event) { + var evt = event.originalEvent; + this.touchMoved = false; + this.touchStartPosition = evt.touches ? evt.touches[0] : evt; + event.stopPropagation(); + event.preventDefault(); + }) + .on('touchmove', '.xdsoft_option', handleTouchMoved) + .on('touchend mousedown.xdsoft', '.xdsoft_option', function () { + if (!this.touchMoved) { + if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + } + + var year = _xdsoft_datetime.currentTime.getFullYear(); + if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { + _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); + } + + $(this).parent().parent().hide(); + + datetimepicker.trigger('xchange.xdsoft'); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + } + }); + + datetimepicker.getValue = function () { + return _xdsoft_datetime.getCurrentTime(); + }; + + datetimepicker.setOptions = function (_options) { + var highlightedDates = {}; + + options = $.extend(true, {}, options, _options); + + if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) { + options.allowTimes = $.extend(true, [], _options.allowTimes); + } + + if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) { + options.weekends = $.extend(true, [], _options.weekends); + } + + if (_options.allowDates && $.isArray(_options.allowDates) && _options.allowDates.length) { + options.allowDates = $.extend(true, [], _options.allowDates); + } + + if (_options.allowDateRe && Object.prototype.toString.call(_options.allowDateRe)==="[object String]") { + options.allowDateRe = new RegExp(_options.allowDateRe); + } + + if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) { + $.each(_options.highlightedDates, function (index, value) { + var splitData = $.map(value.split(','), $.trim), + exDesc, + hDate = new HighlightedDate(dateHelper.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style + keyDate = dateHelper.formatDate(hDate.date, options.formatDate); + if (highlightedDates[keyDate] !== undefined) { + exDesc = highlightedDates[keyDate].desc; + if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { + highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; + } + } else { + highlightedDates[keyDate] = hDate; + } + }); + + options.highlightedDates = $.extend(true, [], highlightedDates); + } + + if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) { + highlightedDates = $.extend(true, [], options.highlightedDates); + $.each(_options.highlightedPeriods, function (index, value) { + var dateTest, // start date + dateEnd, + desc, + hDate, + keyDate, + exDesc, + style; + if ($.isArray(value)) { + dateTest = value[0]; + dateEnd = value[1]; + desc = value[2]; + style = value[3]; + } + else { + var splitData = $.map(value.split(','), $.trim); + dateTest = dateHelper.parseDate(splitData[0], options.formatDate); + dateEnd = dateHelper.parseDate(splitData[1], options.formatDate); + desc = splitData[2]; + style = splitData[3]; + } + + while (dateTest <= dateEnd) { + hDate = new HighlightedDate(dateTest, desc, style); + keyDate = dateHelper.formatDate(dateTest, options.formatDate); + dateTest.setDate(dateTest.getDate() + 1); + if (highlightedDates[keyDate] !== undefined) { + exDesc = highlightedDates[keyDate].desc; + if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { + highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; + } + } else { + highlightedDates[keyDate] = hDate; + } + } + }); + + options.highlightedDates = $.extend(true, [], highlightedDates); + } + + if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) { + options.disabledDates = $.extend(true, [], _options.disabledDates); + } + + if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) { + options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays); + } + + if ((options.open || options.opened) && (!options.inline)) { + input.trigger('open.xdsoft'); + } + + if (options.inline) { + triggerAfterOpen = true; + datetimepicker.addClass('xdsoft_inline'); + input.after(datetimepicker).hide(); + } + + if (options.inverseButton) { + options.next = 'xdsoft_prev'; + options.prev = 'xdsoft_next'; + } + + if (options.datepicker) { + datepicker.addClass('active'); + } else { + datepicker.removeClass('active'); + } + + if (options.timepicker) { + timepicker.addClass('active'); + } else { + timepicker.removeClass('active'); + } + + if (options.value) { + _xdsoft_datetime.setCurrentTime(options.value); + if (input && input.val) { + input.val(_xdsoft_datetime.str); + } + } + + if (isNaN(options.dayOfWeekStart)) { + options.dayOfWeekStart = 0; + } else { + options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7; + } + + if (!options.timepickerScrollbar) { + timeboxparent.xdsoftScroller(options, 'hide'); + } + + if (options.minDate && /^[\+\-](.*)$/.test(options.minDate)) { + options.minDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.minDate), options.formatDate); + } + + if (options.maxDate && /^[\+\-](.*)$/.test(options.maxDate)) { + options.maxDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.maxDate), options.formatDate); + } + + if (options.minDateTime && /^\+(.*)$/.test(options.minDateTime)) { + options.minDateTime = _xdsoft_datetime.strToDateTime(options.minDateTime).dateFormat(options.formatDate); + } + + if (options.maxDateTime && /^\+(.*)$/.test(options.maxDateTime)) { + options.maxDateTime = _xdsoft_datetime.strToDateTime(options.maxDateTime).dateFormat(options.formatDate); + } + + applyButton.toggle(options.showApplyButton); + + month_picker + .find('.xdsoft_today_button') + .css('visibility', !options.todayButton ? 'hidden' : 'visible'); + + month_picker + .find('.' + options.prev) + .css('visibility', !options.prevButton ? 'hidden' : 'visible'); + + month_picker + .find('.' + options.next) + .css('visibility', !options.nextButton ? 'hidden' : 'visible'); + + setMask(options); + + if (options.validateOnBlur) { + input + .off('blur.xdsoft') + .on('blur.xdsoft', function () { + if (options.allowBlank && (!$.trim($(this).val()).length || + (typeof options.mask === "string" && $.trim($(this).val()) === options.mask.replace(/[0-9]/g, '_')))) { + $(this).val(null); + datetimepicker.data('xdsoft_datetime').empty(); + } else { + var d = dateHelper.parseDate($(this).val(), options.format); + if (d) { // parseDate() may skip some invalid parts like date or time, so make it clear for user: show parsed date/time + $(this).val(dateHelper.formatDate(d, options.format)); + } else { + var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')), + splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join('')); + + // parse the numbers as 0312 => 03:12 + if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) { + $(this).val([splittedHours, splittedMinutes].map(function (item) { + return item > 9 ? item : '0' + item; + }).join(':')); + } else { + $(this).val(dateHelper.formatDate(_xdsoft_datetime.now(), options.format)); + } + } + datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); + } + + datetimepicker.trigger('changedatetime.xdsoft'); + datetimepicker.trigger('close.xdsoft'); + }); + } + options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1; + + datetimepicker + .trigger('xchange.xdsoft') + .trigger('afterOpen.xdsoft'); + }; + + datetimepicker + .data('options', options) + .on('touchstart mousedown.xdsoft', function (event) { + event.stopPropagation(); + event.preventDefault(); + yearselect.hide(); + monthselect.hide(); + return false; + }); + + //scroll_element = timepicker.find('.xdsoft_time_box'); + timeboxparent.append(timebox); + timeboxparent.xdsoftScroller(options); + + datetimepicker.on('afterOpen.xdsoft', function () { + timeboxparent.xdsoftScroller(options); + }); + + datetimepicker + .append(datepicker) + .append(timepicker); + + if (options.withoutCopyright !== true) { + datetimepicker + .append(xdsoft_copyright); + } + + datepicker + .append(month_picker) + .append(calendar) + .append(applyButton); + + $(options.parentID) + .append(datetimepicker); + + XDSoft_datetime = function () { + var _this = this; + _this.now = function (norecursion) { + var d = new Date(), + date, + time; + + if (!norecursion && options.defaultDate) { + date = _this.strToDateTime(options.defaultDate); + d.setFullYear(date.getFullYear()); + d.setMonth(date.getMonth()); + d.setDate(date.getDate()); + } + + d.setFullYear(d.getFullYear()); + + if (!norecursion && options.defaultTime) { + time = _this.strtotime(options.defaultTime); + d.setHours(time.getHours()); + d.setMinutes(time.getMinutes()); + d.setSeconds(time.getSeconds()); + d.setMilliseconds(time.getMilliseconds()); + } + return d; + }; + + _this.isValidDate = function (d) { + if (Object.prototype.toString.call(d) !== "[object Date]") { + return false; + } + return !isNaN(d.getTime()); + }; + + _this.setCurrentTime = function (dTime, requireValidDate) { + if (typeof dTime === 'string') { + _this.currentTime = _this.strToDateTime(dTime); + } + else if (_this.isValidDate(dTime)) { + _this.currentTime = dTime; + } + else if (!dTime && !requireValidDate && options.allowBlank && !options.inline) { + _this.currentTime = null; + } + else { + _this.currentTime = _this.now(); + } + + datetimepicker.trigger('xchange.xdsoft'); + }; + + _this.empty = function () { + _this.currentTime = null; + }; + + _this.getCurrentTime = function () { + return _this.currentTime; + }; + + _this.nextMonth = function () { + + if (_this.currentTime === undefined || _this.currentTime === null) { + _this.currentTime = _this.now(); + } + + var month = _this.currentTime.getMonth() + 1, + year; + if (month === 12) { + _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1); + month = 0; + } + + year = _this.currentTime.getFullYear(); + + _this.currentTime.setDate( + Math.min( + new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), + _this.currentTime.getDate() + ) + ); + _this.currentTime.setMonth(month); + + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + datetimepicker.trigger('xchange.xdsoft'); + return month; + }; + + _this.prevMonth = function () { + + if (_this.currentTime === undefined || _this.currentTime === null) { + _this.currentTime = _this.now(); + } + + var month = _this.currentTime.getMonth() - 1; + if (month === -1) { + _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1); + month = 11; + } + _this.currentTime.setDate( + Math.min( + new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(), + _this.currentTime.getDate() + ) + ); + _this.currentTime.setMonth(month); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + datetimepicker.trigger('xchange.xdsoft'); + return month; + }; + + _this.getWeekOfYear = function (datetime) { + if (options.onGetWeekOfYear && $.isFunction(options.onGetWeekOfYear)) { + var week = options.onGetWeekOfYear.call(datetimepicker, datetime); + if (typeof week !== 'undefined') { + return week; + } + } + var onejan = new Date(datetime.getFullYear(), 0, 1); + + //First week of the year is th one with the first Thursday according to ISO8601 + if (onejan.getDay() !== 4) { + onejan.setMonth(0, 1 + ((4 - onejan.getDay()+ 7) % 7)); + } + + return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7); + }; + + _this.strToDateTime = function (sDateTime) { + var tmpDate = [], timeOffset, currentTime; + + if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) { + return sDateTime; + } + + tmpDate = /^([+-]{1})(.*)$/.exec(sDateTime); + + if (tmpDate) { + tmpDate[2] = dateHelper.parseDate(tmpDate[2], options.formatDate); + } + + if (tmpDate && tmpDate[2]) { + timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000; + currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); + } else { + currentTime = sDateTime ? dateHelper.parseDate(sDateTime, options.format) : _this.now(); + } + + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(); + } + + return currentTime; + }; + + _this.strToDate = function (sDate) { + if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) { + return sDate; + } + + var currentTime = sDate ? dateHelper.parseDate(sDate, options.formatDate) : _this.now(true); + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(true); + } + return currentTime; + }; + + _this.strtotime = function (sTime) { + if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) { + return sTime; + } + var currentTime = sTime ? dateHelper.parseDate(sTime, options.formatTime) : _this.now(true); + if (!_this.isValidDate(currentTime)) { + currentTime = _this.now(true); + } + return currentTime; + }; + + _this.str = function () { + var format = options.format; + if (options.yearOffset) { + format = format.replace('Y', _this.currentTime.getFullYear() + options.yearOffset); + format = format.replace('y', String(_this.currentTime.getFullYear() + options.yearOffset).substring(2, 4)); + } + return dateHelper.formatDate(_this.currentTime, format); + }; + _this.currentTime = this.now(); + }; + + _xdsoft_datetime = new XDSoft_datetime(); + + applyButton.on('touchend click', function (e) {//pathbrite + e.preventDefault(); + datetimepicker.data('changed', true); + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + input.val(_xdsoft_datetime.str()); + datetimepicker.trigger('close.xdsoft'); + }); + month_picker + .find('.xdsoft_today_button') + .on('touchend mousedown.xdsoft', function () { + datetimepicker.data('changed', true); + _xdsoft_datetime.setCurrentTime(0, true); + datetimepicker.trigger('afterOpen.xdsoft'); + }).on('dblclick.xdsoft', function () { + var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate; + currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); + minDate = _xdsoft_datetime.strToDate(options.minDate); + minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); + if (currentDate < minDate) { + return; + } + maxDate = _xdsoft_datetime.strToDate(options.maxDate); + maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()); + if (currentDate > maxDate) { + return; + } + input.val(_xdsoft_datetime.str()); + input.trigger('change'); + datetimepicker.trigger('close.xdsoft'); + }); + month_picker + .find('.xdsoft_prev,.xdsoft_next') + .on('touchend mousedown.xdsoft', function () { + var $this = $(this), + timer = 0, + stop = false; + + (function arguments_callee1(v) { + if ($this.hasClass(options.next)) { + _xdsoft_datetime.nextMonth(); + } else if ($this.hasClass(options.prev)) { + _xdsoft_datetime.prevMonth(); + } + if (options.monthChangeSpinner) { + if (!stop) { + timer = setTimeout(arguments_callee1, v || 100); + } + } + }(500)); + + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee2() { + clearTimeout(timer); + stop = true; + $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft', arguments_callee2); + }); + }); + + timepicker + .find('.xdsoft_prev,.xdsoft_next') + .on('touchend mousedown.xdsoft', function () { + var $this = $(this), + timer = 0, + stop = false, + period = 110; + (function arguments_callee4(v) { + var pheight = timeboxparent[0].clientHeight, + height = timebox[0].offsetHeight, + top = Math.abs(parseInt(timebox.css('marginTop'), 10)); + if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) { + timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px'); + } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) { + timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px'); + } + /** + * Fixed bug: + * When using css3 transition, it will cause a bug that you cannot scroll the timepicker list. + * The reason is that the transition-duration time, if you set it to 0, all things fine, otherwise, this + * would cause a bug when you use jquery.css method. + * Let's say: * { transition: all .5s ease; } + * jquery timebox.css('marginTop') will return the original value which is before you clicking the next/prev button, + * meanwhile the timebox[0].style.marginTop will return the right value which is after you clicking the + * next/prev button. + * + * What we should do: + * Replace timebox.css('marginTop') with timebox[0].style.marginTop. + */ + timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox[0].style.marginTop, 10) / (height - pheight))]); + period = (period > 10) ? 10 : period - 10; + if (!stop) { + timer = setTimeout(arguments_callee4, v || period); + } + }(500)); + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee5() { + clearTimeout(timer); + stop = true; + $([options.ownerDocument.body, options.contentWindow]) + .off('touchend mouseup.xdsoft', arguments_callee5); + }); + }); + + xchangeTimer = 0; + // base handler - generating a calendar and timepicker + datetimepicker + .on('xchange.xdsoft', function (event) { + clearTimeout(xchangeTimer); + xchangeTimer = setTimeout(function () { + + if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + } + + var table = '', + start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0), + i = 0, + j, + today = _xdsoft_datetime.now(), + maxDate = false, + minDate = false, + minDateTime = false, + maxDateTime = false, + hDate, + day, + d, + y, + m, + w, + classes = [], + customDateSettings, + newRow = true, + time = '', + h, + line_time, + description; + + while (start.getDay() !== options.dayOfWeekStart) { + start.setDate(start.getDate() - 1); + } + + table += ''; + + if (options.weeks) { + table += ''; + } + + for (j = 0; j < 7; j += 1) { + table += ''; + } + + table += ''; + table += ''; + + if (options.maxDate !== false) { + maxDate = _xdsoft_datetime.strToDate(options.maxDate); + maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999); + } + + if (options.minDate !== false) { + minDate = _xdsoft_datetime.strToDate(options.minDate); + minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); + } + + if (options.minDateTime !== false) { + minDateTime = _xdsoft_datetime.strToDate(options.minDateTime); + minDateTime = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), minDateTime.getHours(), minDateTime.getMinutes(), minDateTime.getSeconds()); + } + + if (options.maxDateTime !== false) { + maxDateTime = _xdsoft_datetime.strToDate(options.maxDateTime); + maxDateTime = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), maxDateTime.getHours(), maxDateTime.getMinutes(), maxDateTime.getSeconds()); + } + + var maxDateTimeDay; + if (maxDateTime !== false) { + maxDateTimeDay = ((maxDateTime.getFullYear() * 12) + maxDateTime.getMonth()) * 31 + maxDateTime.getDate(); + } + + while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) { + classes = []; + i += 1; + + day = start.getDay(); + d = start.getDate(); + y = start.getFullYear(); + m = start.getMonth(); + w = _xdsoft_datetime.getWeekOfYear(start); + description = ''; + + classes.push('xdsoft_date'); + + if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) { + customDateSettings = options.beforeShowDay.call(datetimepicker, start); + } else { + customDateSettings = null; + } + + if(options.allowDateRe && Object.prototype.toString.call(options.allowDateRe) === "[object RegExp]"){ + if(!options.allowDateRe.test(dateHelper.formatDate(start, options.formatDate))){ + classes.push('xdsoft_disabled'); + } + } + + if(options.allowDates && options.allowDates.length>0){ + if(options.allowDates.indexOf(dateHelper.formatDate(start, options.formatDate)) === -1){ + classes.push('xdsoft_disabled'); + } + } + + var currentDay = ((start.getFullYear() * 12) + start.getMonth()) * 31 + start.getDate(); + if ((maxDate !== false && start > maxDate) || (minDateTime !== false && start < minDateTime) || (minDate !== false && start < minDate) || (maxDateTime !== false && currentDay > maxDateTimeDay) || (customDateSettings && customDateSettings[0] === false)) { + classes.push('xdsoft_disabled'); + } + + if (options.disabledDates.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { + classes.push('xdsoft_disabled'); + } + + if (options.disabledWeekDays.indexOf(day) !== -1) { + classes.push('xdsoft_disabled'); + } + + if (input.is('[disabled]')) { + classes.push('xdsoft_disabled'); + } + + if (customDateSettings && customDateSettings[1] !== "") { + classes.push(customDateSettings[1]); + } + + if (_xdsoft_datetime.currentTime.getMonth() !== m) { + classes.push('xdsoft_other_month'); + } + + if ((options.defaultSelect || datetimepicker.data('changed')) && dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { + classes.push('xdsoft_current'); + } + + if (dateHelper.formatDate(today, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { + classes.push('xdsoft_today'); + } + + if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { + classes.push('xdsoft_weekend'); + } + + if (options.highlightedDates[dateHelper.formatDate(start, options.formatDate)] !== undefined) { + hDate = options.highlightedDates[dateHelper.formatDate(start, options.formatDate)]; + classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style); + description = hDate.desc === undefined ? '' : hDate.desc; + } + + if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) { + classes.push(options.beforeShowDay(start)); + } + + if (newRow) { + table += ''; + newRow = false; + if (options.weeks) { + table += ''; + } + } + + table += ''; + + if (start.getDay() === options.dayOfWeekStartPrev) { + table += ''; + newRow = true; + } + + start.setDate(d + 1); + } + table += '
    ' + options.i18n[globalLocale].dayOfWeekShort[(j + options.dayOfWeekStart) % 7] + '
    ' + w + '' + + '
    ' + d + '
    ' + + '
    '; + + calendar.html(table); + + month_picker.find('.xdsoft_label span').eq(0).text(options.i18n[globalLocale].months[_xdsoft_datetime.currentTime.getMonth()]); + month_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear() + options.yearOffset); + + // generate timebox + time = ''; + h = ''; + m = ''; + + var minTimeMinutesOfDay = 0; + if (options.minTime !== false) { + var t = _xdsoft_datetime.strtotime(options.minTime); + minTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); + } + var maxTimeMinutesOfDay = 24 * 60; + if (options.maxTime !== false) { + var t = _xdsoft_datetime.strtotime(options.maxTime); + maxTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); + } + + if (options.minDateTime !== false) { + var t = _xdsoft_datetime.strToDateTime(options.minDateTime); + var currentDayIsMinDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); + if (currentDayIsMinDateTimeDay) { + var m = 60 * t.getHours() + t.getMinutes(); + if (m > minTimeMinutesOfDay) minTimeMinutesOfDay = m; + } + } + + if (options.maxDateTime !== false) { + var t = _xdsoft_datetime.strToDateTime(options.maxDateTime); + var currentDayIsMaxDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); + if (currentDayIsMaxDateTimeDay) { + var m = 60 * t.getHours() + t.getMinutes(); + if (m < maxTimeMinutesOfDay) maxTimeMinutesOfDay = m; + } + } + + line_time = function line_time(h, m) { + var now = _xdsoft_datetime.now(), current_time, + isALlowTimesInit = options.allowTimes && $.isArray(options.allowTimes) && options.allowTimes.length; + now.setHours(h); + h = parseInt(now.getHours(), 10); + now.setMinutes(m); + m = parseInt(now.getMinutes(), 10); + classes = []; + var currentMinutesOfDay = 60 * h + m; + if (input.is('[disabled]') || (currentMinutesOfDay >= maxTimeMinutesOfDay) || (currentMinutesOfDay < minTimeMinutesOfDay)) { + classes.push('xdsoft_disabled'); + } + + current_time = new Date(_xdsoft_datetime.currentTime); + current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10)); + + if (!isALlowTimesInit) { + current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step); + } + + if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && ((!isALlowTimesInit && options.step > 59) || current_time.getMinutes() === parseInt(m, 10))) { + if (options.defaultSelect || datetimepicker.data('changed')) { + classes.push('xdsoft_current'); + } else if (options.initTime) { + classes.push('xdsoft_init_time'); + } + } + if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) { + classes.push('xdsoft_today'); + } + time += '
    ' + dateHelper.formatDate(now, options.formatTime) + '
    '; + }; + + if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) { + for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) { + for (j = 0; j < 60; j += options.step) { + var currentMinutesOfDay = i * 60 + j; + if (currentMinutesOfDay < minTimeMinutesOfDay) continue; + if (currentMinutesOfDay >= maxTimeMinutesOfDay) continue; + h = (i < 10 ? '0' : '') + i; + m = (j < 10 ? '0' : '') + j; + line_time(h, m); + } + } + } else { + for (i = 0; i < options.allowTimes.length; i += 1) { + h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours(); + m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes(); + line_time(h, m); + } + } + + timebox.html(time); + + opt = ''; + + for (i = parseInt(options.yearStart, 10); i <= parseInt(options.yearEnd, 10); i += 1) { + opt += '
    ' + (i + options.yearOffset) + '
    '; + } + yearselect.children().eq(0) + .html(opt); + + for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) { + opt += '
    ' + options.i18n[globalLocale].months[i] + '
    '; + } + monthselect.children().eq(0).html(opt); + $(datetimepicker) + .trigger('generate.xdsoft'); + }, 10); + event.stopPropagation(); + }) + .on('afterOpen.xdsoft', function () { + if (options.timepicker) { + var classType, pheight, height, top; + if (timebox.find('.xdsoft_current').length) { + classType = '.xdsoft_current'; + } else if (timebox.find('.xdsoft_init_time').length) { + classType = '.xdsoft_init_time'; + } + if (classType) { + pheight = timeboxparent[0].clientHeight; + height = timebox[0].offsetHeight; + top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1; + if ((height - pheight) < top) { + top = height - pheight; + } + timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]); + } else { + timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]); + } + } + }); + + timerclick = 0; + calendar + .on('touchend click.xdsoft', 'td', function (xdevent) { + xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap + timerclick += 1; + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; + + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } + + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + + currentTime.setDate(1); + currentTime.setFullYear($this.data('year')); + currentTime.setMonth($this.data('month')); + currentTime.setDate($this.data('date')); + + datetimepicker.trigger('select.xdsoft', [currentTime]); + + input.val(_xdsoft_datetime.str()); + + if (options.onSelectDate && $.isFunction(options.onSelectDate)) { + options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); + if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) { + datetimepicker.trigger('close.xdsoft'); + } + setTimeout(function () { + timerclick = 0; + }, 200); + }); + + timebox + .on('touchstart', 'div', function (xdevent) { + this.touchMoved = false; + }) + .on('touchmove', 'div', handleTouchMoved) + .on('touchend click.xdsoft', 'div', function (xdevent) { + if (!this.touchMoved) { + xdevent.stopPropagation(); + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; + + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } + + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + currentTime.setHours($this.data('hour')); + currentTime.setMinutes($this.data('minute')); + datetimepicker.trigger('select.xdsoft', [currentTime]); + + datetimepicker.data('input').val(_xdsoft_datetime.str()); + + if (options.onSelectTime && $.isFunction(options.onSelectTime)) { + options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); + if (options.inline !== true && options.closeOnTimeSelect === true) { + datetimepicker.trigger('close.xdsoft'); + } + } + }); + + datepicker + .on('mousewheel.xdsoft', function (event) { + if (!options.scrollMonth) { + return true; + } + if (event.deltaY < 0) { + _xdsoft_datetime.nextMonth(); + } else { + _xdsoft_datetime.prevMonth(); + } + return false; + }); + + input + .on('mousewheel.xdsoft', function (event) { + if (!options.scrollInput) { + return true; + } + if (!options.datepicker && options.timepicker) { + current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0; + if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) { + current_time_index += event.deltaY; + } + if (timebox.children().eq(current_time_index).length) { + timebox.children().eq(current_time_index).trigger('mousedown'); + } + return false; + } + if (options.datepicker && !options.timepicker) { + datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]); + if (input.val) { + input.val(_xdsoft_datetime.str()); + } + datetimepicker.trigger('changedatetime.xdsoft'); + return false; + } + }); + + datetimepicker + .on('changedatetime.xdsoft', function (event) { + if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) { + var $input = datetimepicker.data('input'); + options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event); + delete options.value; + $input.trigger('change'); + } + }) + .on('generate.xdsoft', function () { + if (options.onGenerate && $.isFunction(options.onGenerate)) { + options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + if (triggerAfterOpen) { + datetimepicker.trigger('afterOpen.xdsoft'); + triggerAfterOpen = false; + } + }) + .on('click.xdsoft', function (xdevent) { + xdevent.stopPropagation(); + }); + + current_time_index = 0; + + /** + * Runs the callback for each of the specified node's ancestors. + * + * Return FALSE from the callback to stop ascending. + * + * @param {DOMNode} node + * @param {Function} callback + * @returns {undefined} + */ + forEachAncestorOf = function (node, callback) { + do { + node = node.parentNode; + + if (!node || callback(node) === false) { + break; + } + } while (node.nodeName !== 'HTML'); + }; + + /** + * Sets the position of the picker. + * + * @returns {undefined} + */ + setPos = function () { + var dateInputOffset, + dateInputElem, + verticalPosition, + left, + position, + datetimepickerElem, + dateInputHasFixedAncestor, + $dateInput, + windowWidth, + verticalAnchorEdge, + datetimepickerCss, + windowHeight, + windowScrollTop; + + $dateInput = datetimepicker.data('input'); + dateInputOffset = $dateInput.offset(); + dateInputElem = $dateInput[0]; + + verticalAnchorEdge = 'top'; + verticalPosition = (dateInputOffset.top + dateInputElem.offsetHeight) - 1; + left = dateInputOffset.left; + position = "absolute"; + + windowWidth = $(options.contentWindow).width(); + windowHeight = $(options.contentWindow).height(); + windowScrollTop = $(options.contentWindow).scrollTop(); + + if ((options.ownerDocument.documentElement.clientWidth - dateInputOffset.left) < datepicker.parent().outerWidth(true)) { + var diff = datepicker.parent().outerWidth(true) - dateInputElem.offsetWidth; + left = left - diff; + } + + if ($dateInput.parent().css('direction') === 'rtl') { + left -= (datetimepicker.outerWidth() - $dateInput.outerWidth()); + } + + if (options.fixed) { + verticalPosition -= windowScrollTop; + left -= $(options.contentWindow).scrollLeft(); + position = "fixed"; + } else { + dateInputHasFixedAncestor = false; + + forEachAncestorOf(dateInputElem, function (ancestorNode) { + if (ancestorNode === null) { + return false; + } + + if (options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position') === 'fixed') { + dateInputHasFixedAncestor = true; + return false; + } + }); + + if (dateInputHasFixedAncestor) { + position = 'fixed'; + + //If the picker won't fit entirely within the viewport then display it above the date input. + if (verticalPosition + datetimepicker.outerHeight() > windowHeight + windowScrollTop) { + verticalAnchorEdge = 'bottom'; + verticalPosition = (windowHeight + windowScrollTop) - dateInputOffset.top; + } else { + verticalPosition -= windowScrollTop; + } + } else { + if (verticalPosition + datetimepicker[0].offsetHeight > windowHeight + windowScrollTop) { + verticalPosition = dateInputOffset.top - datetimepicker[0].offsetHeight + 1; + } + } + + if (verticalPosition < 0) { + verticalPosition = 0; + } + + if (left + dateInputElem.offsetWidth > windowWidth) { + left = windowWidth - dateInputElem.offsetWidth; + } + } + + datetimepickerElem = datetimepicker[0]; + + forEachAncestorOf(datetimepickerElem, function (ancestorNode) { + var ancestorNodePosition; + + ancestorNodePosition = options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position'); + + if (ancestorNodePosition === 'relative' && windowWidth >= ancestorNode.offsetWidth) { + left = left - ((windowWidth - ancestorNode.offsetWidth) / 2); + return false; + } + }); + + datetimepickerCss = { + position: position, + left: left, + top: '', //Initialize to prevent previous values interfering with new ones. + bottom: '' //Initialize to prevent previous values interfering with new ones. + }; + + datetimepickerCss[verticalAnchorEdge] = verticalPosition; + + datetimepicker.css(datetimepickerCss); + }; + + datetimepicker + .on('open.xdsoft', function (event) { + var onShow = true; + if (options.onShow && $.isFunction(options.onShow)) { + onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); + } + if (onShow !== false) { + datetimepicker.show(); + setPos(); + $(options.contentWindow) + .off('resize.xdsoft', setPos) + .on('resize.xdsoft', setPos); + + if (options.closeOnWithoutClick) { + $([options.ownerDocument.body, options.contentWindow]).on('touchstart mousedown.xdsoft', function arguments_callee6() { + datetimepicker.trigger('close.xdsoft'); + $([options.ownerDocument.body, options.contentWindow]).off('touchstart mousedown.xdsoft', arguments_callee6); + }); + } + } + }) + .on('close.xdsoft', function (event) { + var onClose = true; + month_picker + .find('.xdsoft_month,.xdsoft_year') + .find('.xdsoft_select') + .hide(); + if (options.onClose && $.isFunction(options.onClose)) { + onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event); + } + if (onClose !== false && !options.opened && !options.inline) { + datetimepicker.hide(); + } + event.stopPropagation(); + }) + .on('toggle.xdsoft', function () { + if (datetimepicker.is(':visible')) { + datetimepicker.trigger('close.xdsoft'); + } else { + datetimepicker.trigger('open.xdsoft'); + } + }) + .data('input', input); + + timer = 0; + + datetimepicker.data('xdsoft_datetime', _xdsoft_datetime); + datetimepicker.setOptions(options); + + function getCurrentValue() { + var ct = false, time; + + if (options.startDate) { + ct = _xdsoft_datetime.strToDate(options.startDate); + } else { + ct = options.value || ((input && input.val && input.val()) ? input.val() : ''); + if (ct) { + ct = _xdsoft_datetime.strToDateTime(ct); + if (options.yearOffset) { + ct = new Date(ct.getFullYear() - options.yearOffset, ct.getMonth(), ct.getDate(), ct.getHours(), ct.getMinutes(), ct.getSeconds(), ct.getMilliseconds()); + } + } else if (options.defaultDate) { + ct = _xdsoft_datetime.strToDateTime(options.defaultDate); + if (options.defaultTime) { + time = _xdsoft_datetime.strtotime(options.defaultTime); + ct.setHours(time.getHours()); + ct.setMinutes(time.getMinutes()); + } + } + } + + if (ct && _xdsoft_datetime.isValidDate(ct)) { + datetimepicker.data('changed', true); + } else { + ct = ''; + } + + return ct || 0; + } + + function setMask(options) { + + var isValidValue = function (mask, value) { + var reg = mask + .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') + .replace(/_/g, '{digit+}') + .replace(/([0-9]{1})/g, '{digit$1}') + .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') + .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); + return (new RegExp(reg)).test(value); + }, + getCaretPos = function (input) { + try { + if (options.ownerDocument.selection && options.ownerDocument.selection.createRange) { + var range = options.ownerDocument.selection.createRange(); + return range.getBookmark().charCodeAt(2) - 2; + } + if (input.setSelectionRange) { + return input.selectionStart; + } + } catch (e) { + return 0; + } + }, + setCaretPos = function (node, pos) { + node = (typeof node === "string" || node instanceof String) ? options.ownerDocument.getElementById(node) : node; + if (!node) { + return false; + } + if (node.createTextRange) { + var textRange = node.createTextRange(); + textRange.collapse(true); + textRange.moveEnd('character', pos); + textRange.moveStart('character', pos); + textRange.select(); + return true; + } + if (node.setSelectionRange) { + node.setSelectionRange(pos, pos); + return true; + } + return false; + }; + + if(options.mask) { + input.off('keydown.xdsoft'); + } + + if (options.mask === true) { + if (dateHelper.formatMask) { + options.mask = dateHelper.formatMask(options.format) + } else { + options.mask = options.format + .replace(/Y/g, '9999') + .replace(/F/g, '9999') + .replace(/m/g, '19') + .replace(/d/g, '39') + .replace(/H/g, '29') + .replace(/i/g, '59') + .replace(/s/g, '59'); + } + } + + if ($.type(options.mask) === 'string') { + if (!isValidValue(options.mask, input.val())) { + input.val(options.mask.replace(/[0-9]/g, '_')); + setCaretPos(input[0], 0); + } + + input.on('paste.xdsoft', function (event) { + // couple options here + // 1. return false - tell them they can't paste + // 2. insert over current characters - minimal validation + // 3. full fledged parsing and validation + // let's go option 2 for now + + // fires multiple times for some reason + + // https://stackoverflow.com/a/30496488/1366033 + var clipboardData = event.clipboardData || event.originalEvent.clipboardData || window.clipboardData, + pastedData = clipboardData.getData('text'), + val = this.value, + pos = this.selectionStart + + var valueBeforeCursor = val.substr(0, pos); + var valueAfterPaste = val.substr(pos + pastedData.length); + + val = valueBeforeCursor + pastedData + valueAfterPaste; + pos += pastedData.length; + + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + + event.preventDefault(); + return false; + }); + + input.on('keydown.xdsoft', function (event) { + var val = this.value, + key = event.which, + pos = this.selectionStart, + selEnd = this.selectionEnd, + hasSel = pos !== selEnd, + digit; + + // only alow these characters + if (((key >= KEY0 && key <= KEY9) || + (key >= _KEY0 && key <= _KEY9)) || + (key === BACKSPACE || key === DEL)) { + + // get char to insert which is new character or placeholder ('_') + digit = (key === BACKSPACE || key === DEL) ? '_' : + String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key); + + // we're deleting something, we're not at the start, and have normal cursor, move back one + // if we have a selection length, cursor actually sits behind deletable char, not in front + if (key === BACKSPACE && pos && !hasSel) { + pos -= 1; + } + + // don't stop on a separator, continue whatever direction you were going + // value char - keep incrementing position while on separator char and we still have room + // del char - keep decrementing position while on separator char and we still have room + while (true) { + var maskValueAtCurPos = options.mask.substr(pos, 1); + var posShorterThanMaskLength = pos < options.mask.length; + var posGreaterThanZero = pos > 0; + var notNumberOrPlaceholder = /[^0-9_]/; + var curPosOnSep = notNumberOrPlaceholder.test(maskValueAtCurPos); + var continueMovingPosition = curPosOnSep && posShorterThanMaskLength && posGreaterThanZero + + // if we hit a real char, stay where we are + if (!continueMovingPosition) break; + + // hitting backspace in a selection, you can possibly go back any further - go forward + pos += (key === BACKSPACE && !hasSel) ? -1 : 1; + + } + + + if (hasSel) { + // pos might have moved so re-calc length + var selLength = selEnd - pos + + // if we have a selection length we will wipe out entire selection and replace with default template for that range + var defaultBlank = options.mask.replace(/[0-9]/g, '_'); + var defaultBlankSelectionReplacement = defaultBlank.substr(pos, selLength); + var selReplacementRemainder = defaultBlankSelectionReplacement.substr(1) // might be empty + + var valueBeforeSel = val.substr(0, pos); + var insertChars = digit + selReplacementRemainder; + var charsAfterSelection = val.substr(pos + selLength); + + val = valueBeforeSel + insertChars + charsAfterSelection + + } else { + var valueBeforeCursor = val.substr(0, pos); + var insertChar = digit; + var valueAfterNextChar = val.substr(pos + 1); + + val = valueBeforeCursor + insertChar + valueAfterNextChar + } + + if ($.trim(val) === '') { + // if empty, set to default + val = defaultBlank + } else { + // if at the last character don't need to do anything + if (pos === options.mask.length) { + event.preventDefault(); + return false; + } + } + + // resume cursor location + pos += (key === BACKSPACE) ? 0 : 1; + // don't stop on a separator, continue whatever direction you were going + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE) ? 0 : 1; + } + + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + } else { + if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { + return true; + } + } + + event.preventDefault(); + return false; + }); + } + } + + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + + input + .data('xdsoft_datetimepicker', datetimepicker) + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function () { + if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { + return; + } + if (!options.openOnFocus) { + return; + } + clearTimeout(timer); + timer = setTimeout(function () { + if (input.is(':disabled')) { + return; + } + + triggerAfterOpen = true; + _xdsoft_datetime.setCurrentTime(getCurrentValue(), true); + if(options.mask) { + setMask(options); + } + datetimepicker.trigger('open.xdsoft'); + }, 100); + }) + .on('keydown.xdsoft', function (event) { + var elementSelector, + key = event.which; + if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) { + elementSelector = $("input:visible,textarea:visible,button:visible,a:visible"); + datetimepicker.trigger('close.xdsoft'); + elementSelector.eq(elementSelector.index(this) + 1).focus(); + return false; + } + if ([TAB].indexOf(key) !== -1) { + datetimepicker.trigger('close.xdsoft'); + return true; + } + }) + .on('blur.xdsoft', function () { + datetimepicker.trigger('close.xdsoft'); + }); + }; + destroyDateTimePicker = function (input) { + var datetimepicker = input.data('xdsoft_datetimepicker'); + if (datetimepicker) { + datetimepicker.data('xdsoft_datetime', null); + datetimepicker.remove(); + input + .data('xdsoft_datetimepicker', null) + .off('.xdsoft'); + $(options.contentWindow).off('resize.xdsoft'); + $([options.contentWindow, options.ownerDocument.body]).off('mousedown.xdsoft touchstart'); + if (input.unmousewheel) { + input.unmousewheel(); + } + } + }; + $(options.ownerDocument) + .off('keydown.xdsoftctrl keyup.xdsoftctrl') + .on('keydown.xdsoftctrl', function (e) { + if (e.keyCode === CTRLKEY) { + ctrlDown = true; + } + }) + .on('keyup.xdsoftctrl', function (e) { + if (e.keyCode === CTRLKEY) { + ctrlDown = false; + } + }); + + this.each(function () { + var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input; + if (datetimepicker) { + if ($.type(opt) === 'string') { + switch (opt) { + case 'show': + $(this).select().focus(); + datetimepicker.trigger('open.xdsoft'); + break; + case 'hide': + datetimepicker.trigger('close.xdsoft'); + break; + case 'toggle': + datetimepicker.trigger('toggle.xdsoft'); + break; + case 'destroy': + destroyDateTimePicker($(this)); + break; + case 'reset': + this.value = this.defaultValue; + if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(dateHelper.parseDate(this.value, options.format))) { + datetimepicker.data('changed', false); + } + datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value); + break; + case 'validate': + $input = datetimepicker.data('input'); + $input.trigger('blur.xdsoft'); + break; + default: + if (datetimepicker[opt] && $.isFunction(datetimepicker[opt])) { + result = datetimepicker[opt](opt2); + } + } + } else { + datetimepicker + .setOptions(opt); + } + return 0; + } + if ($.type(opt) !== 'string') { + if (!options.lazyInit || options.open || options.inline) { + createDateTimePicker($(this)); + } else { + lazyInit($(this)); + } + } + }); + + return result; + }; + + $.fn.datetimepicker.defaults = default_options; + + function HighlightedDate(date, desc, style) { + "use strict"; + this.date = date; + this.desc = desc; + this.style = style; + } +}; +;(function (factory) { + if ( typeof define === 'function' && define.amd ) { + // AMD. Register as an anonymous module. + define(['jquery', 'jquery-mousewheel'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS style for Browserify + module.exports = factory(require('jquery'));; + } else { + // Browser globals + factory(jQuery); + } +}(datetimepickerFactory)); + + From acd783206f329415b11efbda042f4a18e0c8f8a5 Mon Sep 17 00:00:00 2001 From: heathdutton Date: Thu, 29 Nov 2018 15:04:36 -0500 Subject: [PATCH 048/240] Downgrade xdan/datetimepicker to 2.3.8. --- .../Assets/js/libraries/7.datetimepicker.js | 1758 ++++------------- 1 file changed, 372 insertions(+), 1386 deletions(-) diff --git a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js index 318c79d6ed9..149bfb1bdb5 100644 --- a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js +++ b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js @@ -1,184 +1,153 @@ /** - * @preserve jQuery DateTimePicker + * @preserve jQuery DateTimePicker plugin v2.3.8 * @homepage http://xdsoft.net/jqplugins/datetimepicker/ - * @author Chupurnov Valeriy () + * (c) 2014, Chupurnov Valeriy. */ - -/** - * @param {jQuery} $ - */ -var datetimepickerFactory = function ($) { +(function ($) { 'use strict'; - var default_options = { i18n: { ar: { // Arabic months: [ "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول" ], - dayOfWeekShort: [ + dayOfWeek: [ "ن", "ث", "ع", "خ", "ج", "س", "ح" - ], - dayOfWeek: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"] + ] }, ro: { // Romanian months: [ - "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie" - ], - dayOfWeekShort: [ - "Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ" + "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie" ], - dayOfWeek: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă"] + dayOfWeek: [ + "l", "ma", "mi", "j", "v", "s", "d" + ] }, id: { // Indonesian months: [ "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" ], - dayOfWeekShort: [ - "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab" - ], - dayOfWeek: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"] - }, - is: { // Icelandic - months: [ - "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember" - ], - dayOfWeekShort: [ - "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau" - ], - dayOfWeek: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur"] + dayOfWeek: [ + "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min" + ] }, bg: { // Bulgarian months: [ "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември" ], - dayOfWeekShort: [ + dayOfWeek: [ "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ], - dayOfWeek: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"] + ] }, fa: { // Persian/Farsi months: [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' ], - dayOfWeekShort: [ + dayOfWeek: [ 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه' - ], - dayOfWeek: ["یک‌شنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنج‌شنبه", "جمعه", "شنبه", "یک‌شنبه"] + ] }, ru: { // Russian months: [ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' ], - dayOfWeekShort: [ - "Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ], - dayOfWeek: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"] + dayOfWeek: [ + "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" + ] }, uk: { // Ukrainian months: [ 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень' ], - dayOfWeekShort: [ + dayOfWeek: [ "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт" - ], - dayOfWeek: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота"] + ] }, en: { // English months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - dayOfWeekShort: [ + dayOfWeek: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ], - dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + ] }, el: { // Ελληνικά months: [ "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος" ], - dayOfWeekShort: [ + dayOfWeek: [ "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ" - ], - dayOfWeek: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"] + ] }, de: { // German months: [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ], - dayOfWeekShort: [ + dayOfWeek: [ "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" - ], - dayOfWeek: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"] + ] }, nl: { // Dutch months: [ "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december" ], - dayOfWeekShort: [ + dayOfWeek: [ "zo", "ma", "di", "wo", "do", "vr", "za" - ], - dayOfWeek: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"] + ] }, tr: { // Turkish months: [ "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık" ], - dayOfWeekShort: [ + dayOfWeek: [ "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts" - ], - dayOfWeek: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"] + ] }, fr: { //French months: [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" ], - dayOfWeekShort: [ + dayOfWeek: [ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" - ], - dayOfWeek: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] + ] }, es: { // Spanish months: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ], - dayOfWeekShort: [ + dayOfWeek: [ "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb" - ], - dayOfWeek: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"] + ] }, th: { // Thai months: [ 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม' ], - dayOfWeekShort: [ + dayOfWeek: [ 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.' - ], - dayOfWeek: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"] + ] }, pl: { // Polish months: [ "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" ], - dayOfWeekShort: [ + dayOfWeek: [ "nd", "pn", "wt", "śr", "cz", "pt", "sb" - ], - dayOfWeek: ["niedziela", "poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota"] + ] }, pt: { // Portuguese months: [ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" ], - dayOfWeekShort: [ + dayOfWeek: [ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" - ], - dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"] + ] }, ch: { // Simplified Chinese months: [ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" ], - dayOfWeekShort: [ + dayOfWeek: [ "日", "一", "二", "三", "四", "五", "六" ] }, @@ -186,85 +155,71 @@ var datetimepickerFactory = function ($) { months: [ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" ], - dayOfWeekShort: [ + dayOfWeek: [ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" ] }, - km: { // Khmer (ភាសាខ្មែរ) - months: [ - "មករា​", "កុម្ភៈ", "មិនា​", "មេសា​", "ឧសភា​", "មិថុនា​", "កក្កដា​", "សីហា​", "កញ្ញា​", "តុលា​", "វិច្ឆិកា", "ធ្នូ​" - ], - dayOfWeekShort: ["អាទិ​", "ច័ន្ទ​", "អង្គារ​", "ពុធ​", "ព្រហ​​", "សុក្រ​", "សៅរ៍"], - dayOfWeek: ["អាទិត្យ​", "ច័ន្ទ​", "អង្គារ​", "ពុធ​", "ព្រហស្បតិ៍​", "សុក្រ​", "សៅរ៍"] - }, kr: { // Korean months: [ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" ], - dayOfWeekShort: [ + dayOfWeek: [ "일", "월", "화", "수", "목", "금", "토" - ], - dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"] + ] }, it: { // Italian months: [ "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" ], - dayOfWeekShort: [ + dayOfWeek: [ "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" - ], - dayOfWeek: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"] + ] }, da: { // Dansk months: [ - "Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December" + "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December" ], - dayOfWeekShort: [ + dayOfWeek: [ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ], - dayOfWeek: ["søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag"] + ] }, no: { // Norwegian months: [ "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember" ], - dayOfWeekShort: [ + dayOfWeek: [ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ], - dayOfWeek: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'] + ] }, ja: { // Japanese months: [ "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" ], - dayOfWeekShort: [ + dayOfWeek: [ "日", "月", "火", "水", "木", "金", "土" - ], - dayOfWeek: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜"] + ] }, vi: { // Vietnamese months: [ "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12" ], - dayOfWeekShort: [ + dayOfWeek: [ "CN", "T2", "T3", "T4", "T5", "T6", "T7" - ], - dayOfWeek: ["Chủ nhật", "Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy"] + ] }, sl: { // Slovenščina months: [ "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December" ], - dayOfWeekShort: [ + dayOfWeek: [ "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob" - ], - dayOfWeek: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota"] + ] }, cs: { // Čeština months: [ "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec" ], - dayOfWeekShort: [ + dayOfWeek: [ "Ne", "Po", "Út", "St", "Čt", "Pá", "So" ] }, @@ -272,273 +227,13 @@ var datetimepickerFactory = function ($) { months: [ "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December" ], - dayOfWeekShort: [ - "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo" - ], - dayOfWeek: ["vasárnap", "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat"] - }, - az: { //Azerbaijanian (Azeri) - months: [ - "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr" - ], - dayOfWeekShort: [ - "B", "Be", "Ça", "Ç", "Ca", "C", "Ş" - ], - dayOfWeek: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"] - }, - bs: { //Bosanski - months: [ - "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" - ], - dayOfWeekShort: [ - "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" - ], - dayOfWeek: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"] - }, - ca: { //Català - months: [ - "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre" - ], - dayOfWeekShort: [ - "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds" - ], - dayOfWeek: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte"] - }, - 'en-GB': { //English (British) - months: [ - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ], - dayOfWeekShort: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ], - dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] - }, - et: { //"Eesti" - months: [ - "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember" - ], - dayOfWeekShort: [ - "P", "E", "T", "K", "N", "R", "L" - ], - dayOfWeek: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev"] - }, - eu: { //Euskara - months: [ - "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua" - ], - dayOfWeekShort: [ - "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La." - ], - dayOfWeek: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata'] - }, - fi: { //Finnish (Suomi) - months: [ - "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu" - ], - dayOfWeekShort: [ - "Su", "Ma", "Ti", "Ke", "To", "Pe", "La" - ], - dayOfWeek: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"] - }, - gl: { //Galego - months: [ - "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec" - ], - dayOfWeekShort: [ - "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab" - ], - dayOfWeek: ["Domingo", "Luns", "Martes", "Mércores", "Xoves", "Venres", "Sábado"] - }, - hr: { //Hrvatski - months: [ - "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac" - ], - dayOfWeekShort: [ - "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" - ], - dayOfWeek: ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"] - }, - ko: { //Korean (한국어) - months: [ - "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" - ], - dayOfWeekShort: [ - "일", "월", "화", "수", "목", "금", "토" - ], - dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"] - }, - lt: { //Lithuanian (lietuvių) - months: [ - "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio" - ], - dayOfWeekShort: [ - "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš" - ], - dayOfWeek: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis"] - }, - lv: { //Latvian (Latviešu) - months: [ - "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris" - ], - dayOfWeekShort: [ - "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St" - ], - dayOfWeek: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena"] - }, - mk: { //Macedonian (Македонски) - months: [ - "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември" - ], - dayOfWeekShort: [ - "нед", "пон", "вто", "сре", "чет", "пет", "саб" - ], - dayOfWeek: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"] - }, - mn: { //Mongolian (Монгол) - months: [ - "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар" - ], - dayOfWeekShort: [ - "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням" - ], - dayOfWeek: ["Даваа", "Мягмар", "Лхагва", "Пүрэв", "Баасан", "Бямба", "Ням"] - }, - 'pt-BR': { //Português(Brasil) - months: [ - "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" - ], - dayOfWeekShort: [ - "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb" - ], - dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"] - }, - sk: { //Slovenčina - months: [ - "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December" - ], - dayOfWeekShort: [ - "Ne", "Po", "Ut", "St", "Št", "Pi", "So" - ], - dayOfWeek: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"] - }, - sq: { //Albanian (Shqip) - months: [ - "Janar", "Shkurt", "Mars", "Prill", "Maj", "Qershor", "Korrik", "Gusht", "Shtator", "Tetor", "Nëntor", "Dhjetor" - ], - dayOfWeekShort: [ - "Die", "Hën", "Mar", "Mër", "Enj", "Pre", "Shtu" - ], - dayOfWeek: ["E Diel", "E Hënë", "E Martē", "E Mërkurë", "E Enjte", "E Premte", "E Shtunë"] - }, - 'sr-YU': { //Serbian (Srpski) - months: [ - "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" - ], - dayOfWeekShort: [ - "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub" - ], - dayOfWeek: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"] - }, - sr: { //Serbian Cyrillic (Српски) - months: [ - "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар" - ], - dayOfWeekShort: [ - "нед", "пон", "уто", "сре", "чет", "пет", "суб" - ], - dayOfWeek: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"] - }, - sv: { //Svenska - months: [ - "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" - ], - dayOfWeekShort: [ - "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" - ], - dayOfWeek: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"] - }, - 'zh-TW': { //Traditional Chinese (繁體中文) - months: [ - "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" - ], - dayOfWeekShort: [ - "日", "一", "二", "三", "四", "五", "六" - ], - dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"] - }, - zh: { //Simplified Chinese (简体中文) - months: [ - "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" - ], - dayOfWeekShort: [ - "日", "一", "二", "三", "四", "五", "六" - ], - dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"] - }, - ug:{ // Uyghur(ئۇيغۇرچە) - months: [ - "1-ئاي","2-ئاي","3-ئاي","4-ئاي","5-ئاي","6-ئاي","7-ئاي","8-ئاي","9-ئاي","10-ئاي","11-ئاي","12-ئاي" - ], dayOfWeek: [ - "يەكشەنبە", "دۈشەنبە","سەيشەنبە","چارشەنبە","پەيشەنبە","جۈمە","شەنبە" - ] - }, - he: { //Hebrew (עברית) - months: [ - 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר' - ], - dayOfWeekShort: [ - 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת' - ], - dayOfWeek: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"] - }, - hy: { // Armenian - months: [ - "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր" - ], - dayOfWeekShort: [ - "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ" - ], - dayOfWeek: ["Կիրակի", "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ"] - }, - kg: { // Kyrgyz - months: [ - 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы' - ], - dayOfWeekShort: [ - "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише" - ], - dayOfWeek: [ - "Жекшемб", "Дүйшөмб", "Шейшемб", "Шаршемб", "Бейшемби", "Жума", "Ишенб" - ] - }, - rm: { // Romansh - months: [ - "Schaner", "Favrer", "Mars", "Avrigl", "Matg", "Zercladur", "Fanadur", "Avust", "Settember", "October", "November", "December" - ], - dayOfWeekShort: [ - "Du", "Gli", "Ma", "Me", "Gie", "Ve", "So" - ], - dayOfWeek: [ - "Dumengia", "Glindesdi", "Mardi", "Mesemna", "Gievgia", "Venderdi", "Sonda" + "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo" ] - }, - ka: { // Georgian - months: [ - 'იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი' - ], - dayOfWeekShort: [ - "კვ", "ორშ", "სამშ", "ოთხ", "ხუთ", "პარ", "შაბ" - ], - dayOfWeek: ["კვირა", "ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი"] } }, - - ownerDocument: document, - contentWindow: window, - value: '', - rtl: false, + lang: 'en', format: 'Y/m/d H:i', formatTime: 'H:i', @@ -549,10 +244,8 @@ var datetimepickerFactory = function ($) { monthChangeSpinner: true, closeOnDateSelect: false, - closeOnTimeSelect: true, closeOnWithoutClick: true, closeOnInputClick: true, - openOnFocus: true, timepicker: true, datepicker: true, @@ -565,20 +258,16 @@ var datetimepickerFactory = function ($) { maxDate: false, minTime: false, maxTime: false, - minDateTime: false, - maxDateTime: false, allowTimes: [], opened: false, initTime: true, inline: false, theme: '', - touchMovedThreshold: 5, onSelectDate: function () {}, onSelectTime: function () {}, onChangeMonth: function () {}, - onGetWeekOfYear: function () {}, onChangeYear: function () {}, onChangeDateTime: function () {}, onShow: function () {}, @@ -588,15 +277,13 @@ var datetimepickerFactory = function ($) { withoutCopyright: true, inverseButton: false, hours12: false, - next: 'xdsoft_next', + next: 'xdsoft_next', prev : 'xdsoft_prev', dayOfWeekStart: 0, parentID: 'body', timeHeightInTimePicker: 25, timepickerScrollbar: true, todayButton: true, - prevButton: true, - nextButton: true, defaultSelect: true, scrollMonth: true, @@ -609,152 +296,16 @@ var datetimepickerFactory = function ($) { allowBlank: true, yearStart: 1950, yearEnd: 2050, - monthStart: 0, - monthEnd: 11, style: '', id: '', fixed: false, roundTime: 'round', // ceil, floor className: '', weekends: [], - highlightedDates: [], - highlightedPeriods: [], - allowDates : [], - allowDateRe : null, - disabledDates : [], - disabledWeekDays: [], yearOffset: 0, - beforeShowDay: null, - - enterLikeTab: true, - showApplyButton: false - }; - - var dateHelper = null, - defaultDateHelper = null, - globalLocaleDefault = 'en', - globalLocale = 'en'; - - var dateFormatterOptionsDefault = { - meridiem: ['AM', 'PM'] + beforeShowDay: null }; - - var initDateFormatter = function(){ - var locale = default_options.i18n[globalLocale], - opts = { - days: locale.dayOfWeek, - daysShort: locale.dayOfWeekShort, - months: locale.months, - monthsShort: $.map(locale.months, function(n){ return n.substring(0, 3) }) - }; - - if (typeof DateFormatter === 'function') { - dateHelper = defaultDateHelper = new DateFormatter({ - dateSettings: $.extend({}, dateFormatterOptionsDefault, opts) - }); - } - }; - - var dateFormatters = { - moment: { - default_options:{ - format: 'YYYY/MM/DD HH:mm', - formatDate: 'YYYY/MM/DD', - formatTime: 'HH:mm', - }, - formatter: { - parseDate: function (date, format) { - if(isFormatStandard(format)){ - return defaultDateHelper.parseDate(date, format); - } - var d = moment(date, format); - return d.isValid() ? d.toDate() : false; - }, - - formatDate: function (date, format) { - if(isFormatStandard(format)){ - return defaultDateHelper.formatDate(date, format); - } - return moment(date).format(format); - }, - - formatMask: function(format){ - return format - .replace(/Y{4}/g, '9999') - .replace(/Y{2}/g, '99') - .replace(/M{2}/g, '19') - .replace(/D{2}/g, '39') - .replace(/H{2}/g, '29') - .replace(/m{2}/g, '59') - .replace(/s{2}/g, '59'); - }, - } - } - } - - // for locale settings - $.datetimepicker = { - setLocale: function(locale){ - var newLocale = default_options.i18n[locale] ? locale : globalLocaleDefault; - if (globalLocale !== newLocale) { - globalLocale = newLocale; - // reinit date formatter - initDateFormatter(); - } - }, - - setDateFormatter: function(dateFormatter) { - if(typeof dateFormatter === 'string' && dateFormatters.hasOwnProperty(dateFormatter)){ - var df = dateFormatters[dateFormatter]; - $.extend(default_options, df.default_options); - dateHelper = df.formatter; - } - else { - dateHelper = dateFormatter; - } - }, - }; - - var standardFormats = { - RFC_2822: 'D, d M Y H:i:s O', - ATOM: 'Y-m-d\TH:i:sP', - ISO_8601: 'Y-m-d\TH:i:sO', - RFC_822: 'D, d M y H:i:s O', - RFC_850: 'l, d-M-y H:i:s T', - RFC_1036: 'D, d M y H:i:s O', - RFC_1123: 'D, d M Y H:i:s O', - RSS: 'D, d M Y H:i:s O', - W3C: 'Y-m-d\TH:i:sP' - } - - var isFormatStandard = function(format){ - return Object.values(standardFormats).indexOf(format) === -1 ? false : true; - } - - $.extend($.datetimepicker, standardFormats); - - // first init date formatter - initDateFormatter(); - // fix for ie8 - if (!window.getComputedStyle) { - window.getComputedStyle = function (el) { - this.el = el; - this.getPropertyValue = function (prop) { - var re = /(-([a-z]))/g; - if (prop === 'float') { - prop = 'styleFloat'; - } - if (re.test(prop)) { - prop = prop.replace(re, function (a, b, c) { - return c.toUpperCase(); - }); - } - return el.currentStyle[prop] || null; - }; - return this; - }; - } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (obj, start) { var i, j; @@ -764,12 +315,10 @@ var datetimepickerFactory = function ($) { return -1; }; } - Date.prototype.countDaysInMonth = function () { return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate(); }; - - $.fn.xdsoftScroller = function (options, percent) { + $.fn.xdsoftScroller = function (percent) { return this.each(function () { var timeboxparent = $(this), pointerEventToXY = function (e) { @@ -785,6 +334,7 @@ var datetimepickerFactory = function ($) { } return out; }, + move = 0, timebox, parentHeight, height, @@ -799,11 +349,6 @@ var datetimepickerFactory = function ($) { startTopScroll = 0, calcOffset = function () {}; - if (percent === 'hide') { - timeboxparent.find('.xdsoft_scrollbar').hide(); - return; - } - if (!$(this).hasClass('xdsoft_scroller_box')) { timebox = timeboxparent.children().eq(0); parentHeight = timeboxparent[0].clientHeight; @@ -834,16 +379,16 @@ var datetimepickerFactory = function ($) { startTopScroll = parseInt(scroller.css('margin-top'), 10); h1 = scrollbar[0].offsetHeight; - if (event.type === 'mousedown' || event.type === 'touchstart') { - if (options.ownerDocument) { - $(options.ownerDocument.body).addClass('xdsoft_noselect'); + if (event.type === 'mousedown') { + if (document) { + $(document.body).addClass('xdsoft_noselect'); } - $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft_scroller', function arguments_callee() { - $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft_scroller', arguments_callee) + $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() { + $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee) .off('mousemove.xdsoft_scroller', calcOffset) .removeClass('xdsoft_noselect'); }); - $(options.ownerDocument.body).on('mousemove.xdsoft_scroller', calcOffset); + $(document.body).on('mousemove.xdsoft_scroller', calcOffset); } else { touchStart = true; event.stopPropagation(); @@ -856,7 +401,7 @@ var datetimepickerFactory = function ($) { calcOffset(event); } }) - .on('touchend touchcancel', function () { + .on('touchend touchcancel', function (event) { touchStart = false; startTopScroll = 0; }); @@ -918,7 +463,7 @@ var datetimepickerFactory = function ($) { } }); - timeboxparent.on('touchend touchcancel', function () { + timeboxparent.on('touchend touchcancel', function (event) { start = false; startTop = 0; }); @@ -927,9 +472,8 @@ var datetimepickerFactory = function ($) { }); }; - $.fn.datetimepicker = function (opt, opt2) { - var result = this, - KEY0 = 48, + $.fn.datetimepicker = function (opt) { + var KEY0 = 48, KEY9 = 57, _KEY0 = 96, _KEY9 = 105, @@ -955,11 +499,12 @@ var datetimepickerFactory = function ($) { lazyInitTimer = 0, createDateTimePicker, destroyDateTimePicker, + _xdsoft_datetime, lazyInit = function (input) { input - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function initOnActionCallback() { - if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) { + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || input.data('xdsoft_datetimepicker')) { return; } clearTimeout(lazyInitTimer); @@ -969,17 +514,17 @@ var datetimepickerFactory = function ($) { createDateTimePicker(input); } input - .off('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', initOnActionCallback) + .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback) .trigger('open.xdsoft'); }, 100); }); }; createDateTimePicker = function (input) { - var datetimepicker = $('
    '), + var datetimepicker = $('
    '), xdsoft_copyright = $(''), datepicker = $('
    '), - month_picker = $('
    ' + + mounth_picker = $('
    ' + '
    ' + '
    ' + '
    '), @@ -987,47 +532,30 @@ var datetimepickerFactory = function ($) { timepicker = $('
    '), timeboxparent = timepicker.find('.xdsoft_time_box').eq(0), timebox = $('
    '), - applyButton = $(''), - + scrollbar = $('
    '), + scroller = $('
    '), monthselect = $('
    '), yearselect = $('
    '), triggerAfterOpen = false, XDSoft_datetime, - + //scroll_element, xchangeTimer, timerclick, current_time_index, setPos, timer = 0, - _xdsoft_datetime, - forEachAncestorOf; - - if (options.id) { - datetimepicker.attr('id', options.id); - } - if (options.style) { - datetimepicker.attr('style', options.style); - } - if (options.weeks) { - datetimepicker.addClass('xdsoft_showweeks'); - } - if (options.rtl) { - datetimepicker.addClass('xdsoft_rtl'); - } - - datetimepicker.addClass('xdsoft_' + options.theme); - datetimepicker.addClass(options.className); + timer1 = 0; - month_picker + mounth_picker .find('.xdsoft_month span') .after(monthselect); - month_picker + mounth_picker .find('.xdsoft_year span') .after(yearselect); - month_picker + mounth_picker .find('.xdsoft_month,.xdsoft_year') - .on('touchstart mousedown.xdsoft', function (event) { + .on('mousedown.xdsoft', function (event) { var select = $(this).find('.xdsoft_select').eq(0), val = 0, top = 0, @@ -1035,7 +563,7 @@ var datetimepickerFactory = function ($) { items, i; - month_picker + mounth_picker .find('.xdsoft_select') .hide(); if (_xdsoft_datetime.currentTime) { @@ -1051,65 +579,37 @@ var datetimepickerFactory = function ($) { } } - select.xdsoftScroller(options, top / (select.children()[0].offsetHeight - (select[0].clientHeight))); + select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight))); event.stopPropagation(); return false; }); - var handleTouchMoved = function (event) { - var evt = event.originalEvent; - var touchPosition = evt.touches ? evt.touches[0] : evt; - this.touchStartPosition = this.touchStartPosition || touchPosition; - var xMovement = Math.abs(this.touchStartPosition.clientX - touchPosition.clientX); - var yMovement = Math.abs(this.touchStartPosition.clientY - touchPosition.clientY); - var distance = Math.sqrt(xMovement * xMovement + yMovement * yMovement); - if(distance > options.touchMovedThreshold) { - this.touchMoved = true; - } - } - - month_picker + mounth_picker .find('.xdsoft_select') - .xdsoftScroller(options) - .on('touchstart mousedown.xdsoft', function (event) { - var evt = event.originalEvent; - this.touchMoved = false; - this.touchStartPosition = evt.touches ? evt.touches[0] : evt; + .xdsoftScroller() + .on('mousedown.xdsoft', function (event) { event.stopPropagation(); event.preventDefault(); }) - .on('touchmove', '.xdsoft_option', handleTouchMoved) - .on('touchend mousedown.xdsoft', '.xdsoft_option', function () { - if (!this.touchMoved) { - if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - } - - var year = _xdsoft_datetime.currentTime.getFullYear(); - if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { - _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); - } + .on('mousedown.xdsoft', '.xdsoft_option', function (event) { + var year = _xdsoft_datetime.currentTime.getFullYear(); + if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { + _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); + } - $(this).parent().parent().hide(); + $(this).parent().parent().hide(); - datetimepicker.trigger('xchange.xdsoft'); - if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { - options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } + datetimepicker.trigger('xchange.xdsoft'); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } - if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { - options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } + if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); } }); - datetimepicker.getValue = function () { - return _xdsoft_datetime.getCurrentTime(); - }; - datetimepicker.setOptions = function (_options) { - var highlightedDates = {}; - options = $.extend(true, {}, options, _options); if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) { @@ -1120,83 +620,6 @@ var datetimepickerFactory = function ($) { options.weekends = $.extend(true, [], _options.weekends); } - if (_options.allowDates && $.isArray(_options.allowDates) && _options.allowDates.length) { - options.allowDates = $.extend(true, [], _options.allowDates); - } - - if (_options.allowDateRe && Object.prototype.toString.call(_options.allowDateRe)==="[object String]") { - options.allowDateRe = new RegExp(_options.allowDateRe); - } - - if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) { - $.each(_options.highlightedDates, function (index, value) { - var splitData = $.map(value.split(','), $.trim), - exDesc, - hDate = new HighlightedDate(dateHelper.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style - keyDate = dateHelper.formatDate(hDate.date, options.formatDate); - if (highlightedDates[keyDate] !== undefined) { - exDesc = highlightedDates[keyDate].desc; - if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { - highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; - } - } else { - highlightedDates[keyDate] = hDate; - } - }); - - options.highlightedDates = $.extend(true, [], highlightedDates); - } - - if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) { - highlightedDates = $.extend(true, [], options.highlightedDates); - $.each(_options.highlightedPeriods, function (index, value) { - var dateTest, // start date - dateEnd, - desc, - hDate, - keyDate, - exDesc, - style; - if ($.isArray(value)) { - dateTest = value[0]; - dateEnd = value[1]; - desc = value[2]; - style = value[3]; - } - else { - var splitData = $.map(value.split(','), $.trim); - dateTest = dateHelper.parseDate(splitData[0], options.formatDate); - dateEnd = dateHelper.parseDate(splitData[1], options.formatDate); - desc = splitData[2]; - style = splitData[3]; - } - - while (dateTest <= dateEnd) { - hDate = new HighlightedDate(dateTest, desc, style); - keyDate = dateHelper.formatDate(dateTest, options.formatDate); - dateTest.setDate(dateTest.getDate() + 1); - if (highlightedDates[keyDate] !== undefined) { - exDesc = highlightedDates[keyDate].desc; - if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { - highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; - } - } else { - highlightedDates[keyDate] = hDate; - } - } - }); - - options.highlightedDates = $.extend(true, [], highlightedDates); - } - - if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) { - options.disabledDates = $.extend(true, [], _options.disabledDates); - } - - if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) { - options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays); - } - if ((options.open || options.opened) && (!options.inline)) { input.trigger('open.xdsoft'); } @@ -1225,10 +648,10 @@ var datetimepickerFactory = function ($) { } if (options.value) { - _xdsoft_datetime.setCurrentTime(options.value); if (input && input.val) { - input.val(_xdsoft_datetime.str); + input.val(options.value); } + _xdsoft_datetime.setCurrentTime(options.value); } if (isNaN(options.dayOfWeekStart)) { @@ -1238,71 +661,149 @@ var datetimepickerFactory = function ($) { } if (!options.timepickerScrollbar) { - timeboxparent.xdsoftScroller(options, 'hide'); + scrollbar.hide(); } - if (options.minDate && /^[\+\-](.*)$/.test(options.minDate)) { - options.minDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.minDate), options.formatDate); + if (options.minDate && /^-(.*)$/.test(options.minDate)) { + options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate); } - if (options.maxDate && /^[\+\-](.*)$/.test(options.maxDate)) { - options.maxDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.maxDate), options.formatDate); + if (options.maxDate && /^\+(.*)$/.test(options.maxDate)) { + options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate); } - if (options.minDateTime && /^\+(.*)$/.test(options.minDateTime)) { - options.minDateTime = _xdsoft_datetime.strToDateTime(options.minDateTime).dateFormat(options.formatDate); - } + mounth_picker + .find('.xdsoft_today_button') + .css('visibility', !options.todayButton ? 'hidden' : 'visible'); - if (options.maxDateTime && /^\+(.*)$/.test(options.maxDateTime)) { - options.maxDateTime = _xdsoft_datetime.strToDateTime(options.maxDateTime).dateFormat(options.formatDate); - } + if (options.mask) { + var e, + getCaretPos = function (input) { + try { + if (document.selection && document.selection.createRange) { + var range = document.selection.createRange(); + return range.getBookmark().charCodeAt(2) - 2; + } + if (input.setSelectionRange) { + return input.selectionStart; + } + } catch (e) { + return 0; + } + }, + setCaretPos = function (node, pos) { + node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node; + if (!node) { + return false; + } + if (node.createTextRange) { + var textRange = node.createTextRange(); + textRange.collapse(true); + textRange.moveEnd('character', pos); + textRange.moveStart('character', pos); + textRange.select(); + return true; + } + if (node.setSelectionRange) { + node.setSelectionRange(pos, pos); + return true; + } + return false; + }, + isValidValue = function (mask, value) { + var reg = mask + .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') + .replace(/_/g, '{digit+}') + .replace(/([0-9]{1})/g, '{digit$1}') + .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') + .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); + return (new RegExp(reg)).test(value); + }; + input.off('keydown.xdsoft'); - applyButton.toggle(options.showApplyButton); + if (options.mask === true) { + options.mask = options.format + .replace(/Y/g, '9999') + .replace(/F/g, '9999') + .replace(/m/g, '19') + .replace(/d/g, '39') + .replace(/H/g, '29') + .replace(/i/g, '59') + .replace(/s/g, '59'); + } + + if ($.type(options.mask) === 'string') { + if (!isValidValue(options.mask, input.val())) { + input.val(options.mask.replace(/[0-9]/g, '_')); + } + + input.on('keydown.xdsoft', function (event) { + var val = this.value, + key = event.which, + pos, + digit; + + if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) { + pos = getCaretPos(this); + digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_'; + + if ((key === BACKSPACE || key === DEL) && pos) { + pos -= 1; + digit = '_'; + } - month_picker - .find('.xdsoft_today_button') - .css('visibility', !options.todayButton ? 'hidden' : 'visible'); + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE || key === DEL) ? -1 : 1; + } - month_picker - .find('.' + options.prev) - .css('visibility', !options.prevButton ? 'hidden' : 'visible'); + val = val.substr(0, pos) + digit + val.substr(pos + 1); + if ($.trim(val) === '') { + val = options.mask.replace(/[0-9]/g, '_'); + } else { + if (pos === options.mask.length) { + event.preventDefault(); + return false; + } + } - month_picker - .find('.' + options.next) - .css('visibility', !options.nextButton ? 'hidden' : 'visible'); + pos += (key === BACKSPACE || key === DEL) ? 0 : 1; + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE || key === DEL) ? -1 : 1; + } - setMask(options); + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + } else { + if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { + return true; + } + } + event.preventDefault(); + return false; + }); + } + } if (options.validateOnBlur) { input .off('blur.xdsoft') .on('blur.xdsoft', function () { - if (options.allowBlank && (!$.trim($(this).val()).length || - (typeof options.mask === "string" && $.trim($(this).val()) === options.mask.replace(/[0-9]/g, '_')))) { + if (options.allowBlank && !$.trim($(this).val()).length) { $(this).val(null); datetimepicker.data('xdsoft_datetime').empty(); + } else if (!Date.parseDate($(this).val(), options.format)) { + $(this).val((_xdsoft_datetime.now()).dateFormat(options.format)); + datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } else { - var d = dateHelper.parseDate($(this).val(), options.format); - if (d) { // parseDate() may skip some invalid parts like date or time, so make it clear for user: show parsed date/time - $(this).val(dateHelper.formatDate(d, options.format)); - } else { - var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')), - splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join('')); - - // parse the numbers as 0312 => 03:12 - if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) { - $(this).val([splittedHours, splittedMinutes].map(function (item) { - return item > 9 ? item : '0' + item; - }).join(':')); - } else { - $(this).val(dateHelper.formatDate(_xdsoft_datetime.now(), options.format)); - } - } datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } - datetimepicker.trigger('changedatetime.xdsoft'); - datetimepicker.trigger('close.xdsoft'); }); } options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1; @@ -1314,7 +815,7 @@ var datetimepickerFactory = function ($) { datetimepicker .data('options', options) - .on('touchstart mousedown.xdsoft', function (event) { + .on('mousedown.xdsoft', function (event) { event.stopPropagation(); event.preventDefault(); yearselect.hide(); @@ -1324,10 +825,10 @@ var datetimepickerFactory = function ($) { //scroll_element = timepicker.find('.xdsoft_time_box'); timeboxparent.append(timebox); - timeboxparent.xdsoftScroller(options); + timeboxparent.xdsoftScroller(); datetimepicker.on('afterOpen.xdsoft', function () { - timeboxparent.xdsoftScroller(options); + timeboxparent.xdsoftScroller(); }); datetimepicker @@ -1340,9 +841,8 @@ var datetimepickerFactory = function ($) { } datepicker - .append(month_picker) - .append(calendar) - .append(applyButton); + .append(mounth_picker) + .append(calendar); $(options.parentID) .append(datetimepicker); @@ -1355,21 +855,22 @@ var datetimepickerFactory = function ($) { time; if (!norecursion && options.defaultDate) { - date = _this.strToDateTime(options.defaultDate); + date = _this.strToDate(options.defaultDate); d.setFullYear(date.getFullYear()); d.setMonth(date.getMonth()); d.setDate(date.getDate()); } - d.setFullYear(d.getFullYear()); + if (options.yearOffset) { + d.setFullYear(d.getFullYear() + options.yearOffset); + } if (!norecursion && options.defaultTime) { time = _this.strtotime(options.defaultTime); d.setHours(time.getHours()); d.setMinutes(time.getMinutes()); - d.setSeconds(time.getSeconds()); - d.setMilliseconds(time.getMilliseconds()); } + return d; }; @@ -1380,20 +881,8 @@ var datetimepickerFactory = function ($) { return !isNaN(d.getTime()); }; - _this.setCurrentTime = function (dTime, requireValidDate) { - if (typeof dTime === 'string') { - _this.currentTime = _this.strToDateTime(dTime); - } - else if (_this.isValidDate(dTime)) { - _this.currentTime = dTime; - } - else if (!dTime && !requireValidDate && options.allowBlank && !options.inline) { - _this.currentTime = null; - } - else { - _this.currentTime = _this.now(); - } - + _this.setCurrentTime = function (dTime) { + _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now(); datetimepicker.trigger('xchange.xdsoft'); }; @@ -1401,16 +890,11 @@ var datetimepickerFactory = function ($) { _this.currentTime = null; }; - _this.getCurrentTime = function () { + _this.getCurrentTime = function (dTime) { return _this.currentTime; }; _this.nextMonth = function () { - - if (_this.currentTime === undefined || _this.currentTime === null) { - _this.currentTime = _this.now(); - } - var month = _this.currentTime.getMonth() + 1, year; if (month === 12) { @@ -1441,11 +925,6 @@ var datetimepickerFactory = function ($) { }; _this.prevMonth = function () { - - if (_this.currentTime === undefined || _this.currentTime === null) { - _this.currentTime = _this.now(); - } - var month = _this.currentTime.getMonth() - 1; if (month === -1) { _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1); @@ -1466,19 +945,7 @@ var datetimepickerFactory = function ($) { }; _this.getWeekOfYear = function (datetime) { - if (options.onGetWeekOfYear && $.isFunction(options.onGetWeekOfYear)) { - var week = options.onGetWeekOfYear.call(datetimepicker, datetime); - if (typeof week !== 'undefined') { - return week; - } - } var onejan = new Date(datetime.getFullYear(), 0, 1); - - //First week of the year is th one with the first Thursday according to ISO8601 - if (onejan.getDay() !== 4) { - onejan.setMonth(0, 1 + ((4 - onejan.getDay()+ 7) % 7)); - } - return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7); }; @@ -1489,17 +956,15 @@ var datetimepickerFactory = function ($) { return sDateTime; } - tmpDate = /^([+-]{1})(.*)$/.exec(sDateTime); - + tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime); if (tmpDate) { - tmpDate[2] = dateHelper.parseDate(tmpDate[2], options.formatDate); + tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate); } - if (tmpDate && tmpDate[2]) { timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000; - currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); + currentTime = new Date((_xdsoft_datetime.now()).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); } else { - currentTime = sDateTime ? dateHelper.parseDate(sDateTime, options.format) : _this.now(); + currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now(); } if (!_this.isValidDate(currentTime)) { @@ -1514,7 +979,7 @@ var datetimepickerFactory = function ($) { return sDate; } - var currentTime = sDate ? dateHelper.parseDate(sDate, options.formatDate) : _this.now(true); + var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true); if (!_this.isValidDate(currentTime)) { currentTime = _this.now(true); } @@ -1525,7 +990,7 @@ var datetimepickerFactory = function ($) { if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) { return sTime; } - var currentTime = sTime ? dateHelper.parseDate(sTime, options.formatTime) : _this.now(true); + var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true); if (!_this.isValidDate(currentTime)) { currentTime = _this.now(true); } @@ -1533,56 +998,32 @@ var datetimepickerFactory = function ($) { }; _this.str = function () { - var format = options.format; - if (options.yearOffset) { - format = format.replace('Y', _this.currentTime.getFullYear() + options.yearOffset); - format = format.replace('y', String(_this.currentTime.getFullYear() + options.yearOffset).substring(2, 4)); - } - return dateHelper.formatDate(_this.currentTime, format); + return _this.currentTime.dateFormat(options.format); }; _this.currentTime = this.now(); }; _xdsoft_datetime = new XDSoft_datetime(); - applyButton.on('touchend click', function (e) {//pathbrite - e.preventDefault(); - datetimepicker.data('changed', true); - _xdsoft_datetime.setCurrentTime(getCurrentValue()); - input.val(_xdsoft_datetime.str()); - datetimepicker.trigger('close.xdsoft'); - }); - month_picker + mounth_picker .find('.xdsoft_today_button') - .on('touchend mousedown.xdsoft', function () { + .on('mousedown.xdsoft', function () { datetimepicker.data('changed', true); - _xdsoft_datetime.setCurrentTime(0, true); + _xdsoft_datetime.setCurrentTime(0); datetimepicker.trigger('afterOpen.xdsoft'); }).on('dblclick.xdsoft', function () { - var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate; - currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); - minDate = _xdsoft_datetime.strToDate(options.minDate); - minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); - if (currentDate < minDate) { - return; - } - maxDate = _xdsoft_datetime.strToDate(options.maxDate); - maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()); - if (currentDate > maxDate) { - return; - } input.val(_xdsoft_datetime.str()); - input.trigger('change'); datetimepicker.trigger('close.xdsoft'); }); - month_picker + mounth_picker .find('.xdsoft_prev,.xdsoft_next') - .on('touchend mousedown.xdsoft', function () { + .on('mousedown.xdsoft', function () { var $this = $(this), timer = 0, stop = false; (function arguments_callee1(v) { + var month = _xdsoft_datetime.currentTime.getMonth(); if ($this.hasClass(options.next)) { _xdsoft_datetime.nextMonth(); } else if ($this.hasClass(options.prev)) { @@ -1595,16 +1036,16 @@ var datetimepickerFactory = function ($) { } }(500)); - $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee2() { + $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() { clearTimeout(timer); stop = true; - $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft', arguments_callee2); + $([document.body, window]).off('mouseup.xdsoft', arguments_callee2); }); }); timepicker .find('.xdsoft_prev,.xdsoft_next') - .on('touchend mousedown.xdsoft', function () { + .on('mousedown.xdsoft', function () { var $this = $(this), timer = 0, stop = false, @@ -1618,30 +1059,17 @@ var datetimepickerFactory = function ($) { } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) { timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px'); } - /** - * Fixed bug: - * When using css3 transition, it will cause a bug that you cannot scroll the timepicker list. - * The reason is that the transition-duration time, if you set it to 0, all things fine, otherwise, this - * would cause a bug when you use jquery.css method. - * Let's say: * { transition: all .5s ease; } - * jquery timebox.css('marginTop') will return the original value which is before you clicking the next/prev button, - * meanwhile the timebox[0].style.marginTop will return the right value which is after you clicking the - * next/prev button. - * - * What we should do: - * Replace timebox.css('marginTop') with timebox[0].style.marginTop. - */ - timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox[0].style.marginTop, 10) / (height - pheight))]); + timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]); period = (period > 10) ? 10 : period - 10; if (!stop) { timer = setTimeout(arguments_callee4, v || period); } }(500)); - $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee5() { + $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() { clearTimeout(timer); stop = true; - $([options.ownerDocument.body, options.contentWindow]) - .off('touchend mouseup.xdsoft', arguments_callee5); + $([document.body, window]) + .off('mouseup.xdsoft', arguments_callee5); }); }); @@ -1651,11 +1079,6 @@ var datetimepickerFactory = function ($) { .on('xchange.xdsoft', function (event) { clearTimeout(xchangeTimer); xchangeTimer = setTimeout(function () { - - if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - } - var table = '', start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0), i = 0, @@ -1663,10 +1086,6 @@ var datetimepickerFactory = function ($) { today = _xdsoft_datetime.now(), maxDate = false, minDate = false, - minDateTime = false, - maxDateTime = false, - hDate, - day, d, y, m, @@ -1675,9 +1094,8 @@ var datetimepickerFactory = function ($) { customDateSettings, newRow = true, time = '', - h, - line_time, - description; + h = '', + line_time; while (start.getDay() !== options.dayOfWeekStart) { start.setDate(start.getDate() - 1); @@ -1690,7 +1108,7 @@ var datetimepickerFactory = function ($) { } for (j = 0; j < 7; j += 1) { - table += '' + options.i18n[globalLocale].dayOfWeekShort[(j + options.dayOfWeekStart) % 7] + ''; + table += '' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + ''; } table += ''; @@ -1706,31 +1124,14 @@ var datetimepickerFactory = function ($) { minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); } - if (options.minDateTime !== false) { - minDateTime = _xdsoft_datetime.strToDate(options.minDateTime); - minDateTime = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), minDateTime.getHours(), minDateTime.getMinutes(), minDateTime.getSeconds()); - } - - if (options.maxDateTime !== false) { - maxDateTime = _xdsoft_datetime.strToDate(options.maxDateTime); - maxDateTime = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), maxDateTime.getHours(), maxDateTime.getMinutes(), maxDateTime.getSeconds()); - } - - var maxDateTimeDay; - if (maxDateTime !== false) { - maxDateTimeDay = ((maxDateTime.getFullYear() * 12) + maxDateTime.getMonth()) * 31 + maxDateTime.getDate(); - } - while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) { classes = []; i += 1; - day = start.getDay(); d = start.getDate(); y = start.getFullYear(); m = start.getMonth(); w = _xdsoft_datetime.getWeekOfYear(start); - description = ''; classes.push('xdsoft_date'); @@ -1740,32 +1141,7 @@ var datetimepickerFactory = function ($) { customDateSettings = null; } - if(options.allowDateRe && Object.prototype.toString.call(options.allowDateRe) === "[object RegExp]"){ - if(!options.allowDateRe.test(dateHelper.formatDate(start, options.formatDate))){ - classes.push('xdsoft_disabled'); - } - } - - if(options.allowDates && options.allowDates.length>0){ - if(options.allowDates.indexOf(dateHelper.formatDate(start, options.formatDate)) === -1){ - classes.push('xdsoft_disabled'); - } - } - - var currentDay = ((start.getFullYear() * 12) + start.getMonth()) * 31 + start.getDate(); - if ((maxDate !== false && start > maxDate) || (minDateTime !== false && start < minDateTime) || (minDate !== false && start < minDate) || (maxDateTime !== false && currentDay > maxDateTimeDay) || (customDateSettings && customDateSettings[0] === false)) { - classes.push('xdsoft_disabled'); - } - - if (options.disabledDates.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { - classes.push('xdsoft_disabled'); - } - - if (options.disabledWeekDays.indexOf(day) !== -1) { - classes.push('xdsoft_disabled'); - } - - if (input.is('[disabled]')) { + if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) { classes.push('xdsoft_disabled'); } @@ -1777,24 +1153,18 @@ var datetimepickerFactory = function ($) { classes.push('xdsoft_other_month'); } - if ((options.defaultSelect || datetimepicker.data('changed')) && dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { + if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { classes.push('xdsoft_current'); } - if (dateHelper.formatDate(today, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { + if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { classes.push('xdsoft_today'); } - if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { + if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) === -1) { classes.push('xdsoft_weekend'); } - if (options.highlightedDates[dateHelper.formatDate(start, options.formatDate)] !== undefined) { - hDate = options.highlightedDates[dateHelper.formatDate(start, options.formatDate)]; - classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style); - description = hDate.desc === undefined ? '' : hDate.desc; - } - if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) { classes.push(options.beforeShowDay(start)); } @@ -1807,7 +1177,7 @@ var datetimepickerFactory = function ($) { } } - table += '' + + table += '' + '
    ' + d + '
    ' + ''; @@ -1822,64 +1192,25 @@ var datetimepickerFactory = function ($) { calendar.html(table); - month_picker.find('.xdsoft_label span').eq(0).text(options.i18n[globalLocale].months[_xdsoft_datetime.currentTime.getMonth()]); - month_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear() + options.yearOffset); + mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]); + mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear()); // generate timebox time = ''; h = ''; m = ''; - - var minTimeMinutesOfDay = 0; - if (options.minTime !== false) { - var t = _xdsoft_datetime.strtotime(options.minTime); - minTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); - } - var maxTimeMinutesOfDay = 24 * 60; - if (options.maxTime !== false) { - var t = _xdsoft_datetime.strtotime(options.maxTime); - maxTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); - } - - if (options.minDateTime !== false) { - var t = _xdsoft_datetime.strToDateTime(options.minDateTime); - var currentDayIsMinDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); - if (currentDayIsMinDateTimeDay) { - var m = 60 * t.getHours() + t.getMinutes(); - if (m > minTimeMinutesOfDay) minTimeMinutesOfDay = m; - } - } - - if (options.maxDateTime !== false) { - var t = _xdsoft_datetime.strToDateTime(options.maxDateTime); - var currentDayIsMaxDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); - if (currentDayIsMaxDateTimeDay) { - var m = 60 * t.getHours() + t.getMinutes(); - if (m < maxTimeMinutesOfDay) maxTimeMinutesOfDay = m; - } - } - line_time = function line_time(h, m) { - var now = _xdsoft_datetime.now(), current_time, - isALlowTimesInit = options.allowTimes && $.isArray(options.allowTimes) && options.allowTimes.length; + var now = _xdsoft_datetime.now(); now.setHours(h); h = parseInt(now.getHours(), 10); now.setMinutes(m); m = parseInt(now.getMinutes(), 10); + classes = []; - var currentMinutesOfDay = 60 * h + m; - if (input.is('[disabled]') || (currentMinutesOfDay >= maxTimeMinutesOfDay) || (currentMinutesOfDay < minTimeMinutesOfDay)) { + if ((options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) { classes.push('xdsoft_disabled'); } - - current_time = new Date(_xdsoft_datetime.currentTime); - current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10)); - - if (!isALlowTimesInit) { - current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step); - } - - if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && ((!isALlowTimesInit && options.step > 59) || current_time.getMinutes() === parseInt(m, 10))) { + if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && parseInt(_xdsoft_datetime.currentTime.getHours(), 10) === parseInt(h, 10) && (options.step > 59 || Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step === parseInt(m, 10))) { if (options.defaultSelect || datetimepicker.data('changed')) { classes.push('xdsoft_current'); } else if (options.initTime) { @@ -1889,15 +1220,12 @@ var datetimepickerFactory = function ($) { if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) { classes.push('xdsoft_today'); } - time += '
    ' + dateHelper.formatDate(now, options.formatTime) + '
    '; + time += '
    ' + now.dateFormat(options.formatTime) + '
    '; }; if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) { for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) { for (j = 0; j < 60; j += options.step) { - var currentMinutesOfDay = i * 60 + j; - if (currentMinutesOfDay < minTimeMinutesOfDay) continue; - if (currentMinutesOfDay >= maxTimeMinutesOfDay) continue; h = (i < 10 ? '0' : '') + i; m = (j < 10 ? '0' : '') + j; line_time(h, m); @@ -1914,15 +1242,16 @@ var datetimepickerFactory = function ($) { timebox.html(time); opt = ''; + i = 0; - for (i = parseInt(options.yearStart, 10); i <= parseInt(options.yearEnd, 10); i += 1) { - opt += '
    ' + (i + options.yearOffset) + '
    '; + for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) { + opt += '
    ' + i + '
    '; } yearselect.children().eq(0) .html(opt); - for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) { - opt += '
    ' + options.i18n[globalLocale].months[i] + '
    '; + for (i = 0, opt = ''; i <= 11; i += 1) { + opt += '
    ' + options.i18n[options.lang].months[i] + '
    '; } monthselect.children().eq(0).html(opt); $(datetimepicker) @@ -1954,7 +1283,7 @@ var datetimepickerFactory = function ($) { timerclick = 0; calendar - .on('touchend click.xdsoft', 'td', function (xdevent) { + .on('click.xdsoft', 'td', function (xdevent) { xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap timerclick += 1; var $this = $(this), @@ -1977,6 +1306,9 @@ var datetimepickerFactory = function ($) { datetimepicker.trigger('select.xdsoft', [currentTime]); input.val(_xdsoft_datetime.str()); + if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === 0 && !options.timepicker))) && !options.inline) { + datetimepicker.trigger('close.xdsoft'); + } if (options.onSelectDate && $.isFunction(options.onSelectDate)) { options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); @@ -1985,51 +1317,43 @@ var datetimepickerFactory = function ($) { datetimepicker.data('changed', true); datetimepicker.trigger('xchange.xdsoft'); datetimepicker.trigger('changedatetime.xdsoft'); - if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) { - datetimepicker.trigger('close.xdsoft'); - } setTimeout(function () { timerclick = 0; }, 200); }); timebox - .on('touchstart', 'div', function (xdevent) { - this.touchMoved = false; - }) - .on('touchmove', 'div', handleTouchMoved) - .on('touchend click.xdsoft', 'div', function (xdevent) { - if (!this.touchMoved) { - xdevent.stopPropagation(); - var $this = $(this), - currentTime = _xdsoft_datetime.currentTime; - - if (currentTime === undefined || currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - currentTime = _xdsoft_datetime.currentTime; - } + .on('click.xdsoft', 'div', function (xdevent) { + xdevent.stopPropagation(); + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; - if ($this.hasClass('xdsoft_disabled')) { - return false; - } - currentTime.setHours($this.data('hour')); - currentTime.setMinutes($this.data('minute')); - datetimepicker.trigger('select.xdsoft', [currentTime]); + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } - datetimepicker.data('input').val(_xdsoft_datetime.str()); + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + currentTime.setHours($this.data('hour')); + currentTime.setMinutes($this.data('minute')); + datetimepicker.trigger('select.xdsoft', [currentTime]); - if (options.onSelectTime && $.isFunction(options.onSelectTime)) { - options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); - } - datetimepicker.data('changed', true); - datetimepicker.trigger('xchange.xdsoft'); - datetimepicker.trigger('changedatetime.xdsoft'); - if (options.inline !== true && options.closeOnTimeSelect === true) { - datetimepicker.trigger('close.xdsoft'); - } + datetimepicker.data('input').val(_xdsoft_datetime.str()); + if (!options.inline) { + datetimepicker.trigger('close.xdsoft'); } + + if (options.onSelectTime && $.isFunction(options.onSelectTime)) { + options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); }); + datepicker .on('mousewheel.xdsoft', function (event) { if (!options.scrollMonth) { @@ -2092,135 +1416,29 @@ var datetimepickerFactory = function ($) { current_time_index = 0; - /** - * Runs the callback for each of the specified node's ancestors. - * - * Return FALSE from the callback to stop ascending. - * - * @param {DOMNode} node - * @param {Function} callback - * @returns {undefined} - */ - forEachAncestorOf = function (node, callback) { - do { - node = node.parentNode; - - if (!node || callback(node) === false) { - break; - } - } while (node.nodeName !== 'HTML'); - }; - - /** - * Sets the position of the picker. - * - * @returns {undefined} - */ setPos = function () { - var dateInputOffset, - dateInputElem, - verticalPosition, - left, - position, - datetimepickerElem, - dateInputHasFixedAncestor, - $dateInput, - windowWidth, - verticalAnchorEdge, - datetimepickerCss, - windowHeight, - windowScrollTop; - - $dateInput = datetimepicker.data('input'); - dateInputOffset = $dateInput.offset(); - dateInputElem = $dateInput[0]; - - verticalAnchorEdge = 'top'; - verticalPosition = (dateInputOffset.top + dateInputElem.offsetHeight) - 1; - left = dateInputOffset.left; - position = "absolute"; - - windowWidth = $(options.contentWindow).width(); - windowHeight = $(options.contentWindow).height(); - windowScrollTop = $(options.contentWindow).scrollTop(); - - if ((options.ownerDocument.documentElement.clientWidth - dateInputOffset.left) < datepicker.parent().outerWidth(true)) { - var diff = datepicker.parent().outerWidth(true) - dateInputElem.offsetWidth; - left = left - diff; - } - - if ($dateInput.parent().css('direction') === 'rtl') { - left -= (datetimepicker.outerWidth() - $dateInput.outerWidth()); - } - + var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute"; if (options.fixed) { - verticalPosition -= windowScrollTop; - left -= $(options.contentWindow).scrollLeft(); + top -= $(window).scrollTop(); + left -= $(window).scrollLeft(); position = "fixed"; } else { - dateInputHasFixedAncestor = false; - - forEachAncestorOf(dateInputElem, function (ancestorNode) { - if (ancestorNode === null) { - return false; - } - - if (options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position') === 'fixed') { - dateInputHasFixedAncestor = true; - return false; - } - }); - - if (dateInputHasFixedAncestor) { - position = 'fixed'; - - //If the picker won't fit entirely within the viewport then display it above the date input. - if (verticalPosition + datetimepicker.outerHeight() > windowHeight + windowScrollTop) { - verticalAnchorEdge = 'bottom'; - verticalPosition = (windowHeight + windowScrollTop) - dateInputOffset.top; - } else { - verticalPosition -= windowScrollTop; - } - } else { - if (verticalPosition + datetimepicker[0].offsetHeight > windowHeight + windowScrollTop) { - verticalPosition = dateInputOffset.top - datetimepicker[0].offsetHeight + 1; - } + if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) { + top = offset.top - datetimepicker[0].offsetHeight + 1; } - - if (verticalPosition < 0) { - verticalPosition = 0; + if (top < 0) { + top = 0; } - - if (left + dateInputElem.offsetWidth > windowWidth) { - left = windowWidth - dateInputElem.offsetWidth; + if (left + datetimepicker[0].offsetWidth > $(window).width()) { + left = $(window).width() - datetimepicker[0].offsetWidth; } } - - datetimepickerElem = datetimepicker[0]; - - forEachAncestorOf(datetimepickerElem, function (ancestorNode) { - var ancestorNodePosition; - - ancestorNodePosition = options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position'); - - if (ancestorNodePosition === 'relative' && windowWidth >= ancestorNode.offsetWidth) { - left = left - ((windowWidth - ancestorNode.offsetWidth) / 2); - return false; - } - }); - - datetimepickerCss = { - position: position, + datetimepicker.css({ left: left, - top: '', //Initialize to prevent previous values interfering with new ones. - bottom: '' //Initialize to prevent previous values interfering with new ones. - }; - - datetimepickerCss[verticalAnchorEdge] = verticalPosition; - - datetimepicker.css(datetimepickerCss); + top: top, + position: position + }); }; - datetimepicker .on('open.xdsoft', function (event) { var onShow = true; @@ -2230,21 +1448,21 @@ var datetimepickerFactory = function ($) { if (onShow !== false) { datetimepicker.show(); setPos(); - $(options.contentWindow) + $(window) .off('resize.xdsoft', setPos) .on('resize.xdsoft', setPos); if (options.closeOnWithoutClick) { - $([options.ownerDocument.body, options.contentWindow]).on('touchstart mousedown.xdsoft', function arguments_callee6() { + $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() { datetimepicker.trigger('close.xdsoft'); - $([options.ownerDocument.body, options.contentWindow]).off('touchstart mousedown.xdsoft', arguments_callee6); + $([document.body, window]).off('mousedown.xdsoft', arguments_callee6); }); } } }) .on('close.xdsoft', function (event) { var onClose = true; - month_picker + mounth_picker .find('.xdsoft_month,.xdsoft_year') .find('.xdsoft_select') .hide(); @@ -2256,21 +1474,16 @@ var datetimepickerFactory = function ($) { } event.stopPropagation(); }) - .on('toggle.xdsoft', function () { - if (datetimepicker.is(':visible')) { - datetimepicker.trigger('close.xdsoft'); - } else { - datetimepicker.trigger('open.xdsoft'); - } - }) .data('input', input); timer = 0; + timer1 = 0; datetimepicker.data('xdsoft_datetime', _xdsoft_datetime); datetimepicker.setOptions(options); function getCurrentValue() { + var ct = false, time; if (options.startDate) { @@ -2279,11 +1492,8 @@ var datetimepickerFactory = function ($) { ct = options.value || ((input && input.val && input.val()) ? input.val() : ''); if (ct) { ct = _xdsoft_datetime.strToDateTime(ct); - if (options.yearOffset) { - ct = new Date(ct.getFullYear() - options.yearOffset, ct.getMonth(), ct.getDate(), ct.getHours(), ct.getMinutes(), ct.getSeconds(), ct.getMilliseconds()); - } } else if (options.defaultDate) { - ct = _xdsoft_datetime.strToDateTime(options.defaultDate); + ct = _xdsoft_datetime.strToDate(options.defaultDate); if (options.defaultTime) { time = _xdsoft_datetime.strtotime(options.defaultTime); ct.setHours(time.getHours()); @@ -2301,243 +1511,31 @@ var datetimepickerFactory = function ($) { return ct || 0; } - function setMask(options) { - - var isValidValue = function (mask, value) { - var reg = mask - .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') - .replace(/_/g, '{digit+}') - .replace(/([0-9]{1})/g, '{digit$1}') - .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') - .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); - return (new RegExp(reg)).test(value); - }, - getCaretPos = function (input) { - try { - if (options.ownerDocument.selection && options.ownerDocument.selection.createRange) { - var range = options.ownerDocument.selection.createRange(); - return range.getBookmark().charCodeAt(2) - 2; - } - if (input.setSelectionRange) { - return input.selectionStart; - } - } catch (e) { - return 0; - } - }, - setCaretPos = function (node, pos) { - node = (typeof node === "string" || node instanceof String) ? options.ownerDocument.getElementById(node) : node; - if (!node) { - return false; - } - if (node.createTextRange) { - var textRange = node.createTextRange(); - textRange.collapse(true); - textRange.moveEnd('character', pos); - textRange.moveStart('character', pos); - textRange.select(); - return true; - } - if (node.setSelectionRange) { - node.setSelectionRange(pos, pos); - return true; - } - return false; - }; - - if(options.mask) { - input.off('keydown.xdsoft'); - } - - if (options.mask === true) { - if (dateHelper.formatMask) { - options.mask = dateHelper.formatMask(options.format) - } else { - options.mask = options.format - .replace(/Y/g, '9999') - .replace(/F/g, '9999') - .replace(/m/g, '19') - .replace(/d/g, '39') - .replace(/H/g, '29') - .replace(/i/g, '59') - .replace(/s/g, '59'); - } - } - - if ($.type(options.mask) === 'string') { - if (!isValidValue(options.mask, input.val())) { - input.val(options.mask.replace(/[0-9]/g, '_')); - setCaretPos(input[0], 0); - } - - input.on('paste.xdsoft', function (event) { - // couple options here - // 1. return false - tell them they can't paste - // 2. insert over current characters - minimal validation - // 3. full fledged parsing and validation - // let's go option 2 for now - - // fires multiple times for some reason - - // https://stackoverflow.com/a/30496488/1366033 - var clipboardData = event.clipboardData || event.originalEvent.clipboardData || window.clipboardData, - pastedData = clipboardData.getData('text'), - val = this.value, - pos = this.selectionStart - - var valueBeforeCursor = val.substr(0, pos); - var valueAfterPaste = val.substr(pos + pastedData.length); - - val = valueBeforeCursor + pastedData + valueAfterPaste; - pos += pastedData.length; - - if (isValidValue(options.mask, val)) { - this.value = val; - setCaretPos(this, pos); - } else if ($.trim(val) === '') { - this.value = options.mask.replace(/[0-9]/g, '_'); - } else { - input.trigger('error_input.xdsoft'); - } - - event.preventDefault(); - return false; - }); - - input.on('keydown.xdsoft', function (event) { - var val = this.value, - key = event.which, - pos = this.selectionStart, - selEnd = this.selectionEnd, - hasSel = pos !== selEnd, - digit; - - // only alow these characters - if (((key >= KEY0 && key <= KEY9) || - (key >= _KEY0 && key <= _KEY9)) || - (key === BACKSPACE || key === DEL)) { - - // get char to insert which is new character or placeholder ('_') - digit = (key === BACKSPACE || key === DEL) ? '_' : - String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key); - - // we're deleting something, we're not at the start, and have normal cursor, move back one - // if we have a selection length, cursor actually sits behind deletable char, not in front - if (key === BACKSPACE && pos && !hasSel) { - pos -= 1; - } - - // don't stop on a separator, continue whatever direction you were going - // value char - keep incrementing position while on separator char and we still have room - // del char - keep decrementing position while on separator char and we still have room - while (true) { - var maskValueAtCurPos = options.mask.substr(pos, 1); - var posShorterThanMaskLength = pos < options.mask.length; - var posGreaterThanZero = pos > 0; - var notNumberOrPlaceholder = /[^0-9_]/; - var curPosOnSep = notNumberOrPlaceholder.test(maskValueAtCurPos); - var continueMovingPosition = curPosOnSep && posShorterThanMaskLength && posGreaterThanZero - - // if we hit a real char, stay where we are - if (!continueMovingPosition) break; - - // hitting backspace in a selection, you can possibly go back any further - go forward - pos += (key === BACKSPACE && !hasSel) ? -1 : 1; - - } - - - if (hasSel) { - // pos might have moved so re-calc length - var selLength = selEnd - pos - - // if we have a selection length we will wipe out entire selection and replace with default template for that range - var defaultBlank = options.mask.replace(/[0-9]/g, '_'); - var defaultBlankSelectionReplacement = defaultBlank.substr(pos, selLength); - var selReplacementRemainder = defaultBlankSelectionReplacement.substr(1) // might be empty - - var valueBeforeSel = val.substr(0, pos); - var insertChars = digit + selReplacementRemainder; - var charsAfterSelection = val.substr(pos + selLength); - - val = valueBeforeSel + insertChars + charsAfterSelection - - } else { - var valueBeforeCursor = val.substr(0, pos); - var insertChar = digit; - var valueAfterNextChar = val.substr(pos + 1); - - val = valueBeforeCursor + insertChar + valueAfterNextChar - } - - if ($.trim(val) === '') { - // if empty, set to default - val = defaultBlank - } else { - // if at the last character don't need to do anything - if (pos === options.mask.length) { - event.preventDefault(); - return false; - } - } - - // resume cursor location - pos += (key === BACKSPACE) ? 0 : 1; - // don't stop on a separator, continue whatever direction you were going - while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { - pos += (key === BACKSPACE) ? 0 : 1; - } - - if (isValidValue(options.mask, val)) { - this.value = val; - setCaretPos(this, pos); - } else if ($.trim(val) === '') { - this.value = options.mask.replace(/[0-9]/g, '_'); - } else { - input.trigger('error_input.xdsoft'); - } - } else { - if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { - return true; - } - } - - event.preventDefault(); - return false; - }); - } - } - _xdsoft_datetime.setCurrentTime(getCurrentValue()); input .data('xdsoft_datetimepicker', datetimepicker) - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function () { - if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { - return; - } - if (!options.openOnFocus) { + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { return; } clearTimeout(timer); timer = setTimeout(function () { - if (input.is(':disabled')) { + if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible')) { return; } triggerAfterOpen = true; - _xdsoft_datetime.setCurrentTime(getCurrentValue(), true); - if(options.mask) { - setMask(options); - } + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + datetimepicker.trigger('open.xdsoft'); }, 100); }) .on('keydown.xdsoft', function (event) { - var elementSelector, + var val = this.value, elementSelector, key = event.which; - if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) { - elementSelector = $("input:visible,textarea:visible,button:visible,a:visible"); + if ([ENTER].indexOf(key) !== -1) { + elementSelector = $("input:visible,textarea:visible"); datetimepicker.trigger('close.xdsoft'); elementSelector.eq(elementSelector.index(this) + 1).focus(); return false; @@ -2546,9 +1544,6 @@ var datetimepickerFactory = function ($) { datetimepicker.trigger('close.xdsoft'); return true; } - }) - .on('blur.xdsoft', function () { - datetimepicker.trigger('close.xdsoft'); }); }; destroyDateTimePicker = function (input) { @@ -2559,14 +1554,14 @@ var datetimepickerFactory = function ($) { input .data('xdsoft_datetimepicker', null) .off('.xdsoft'); - $(options.contentWindow).off('resize.xdsoft'); - $([options.contentWindow, options.ownerDocument.body]).off('mousedown.xdsoft touchstart'); + $(window).off('resize.xdsoft'); + $([window, document.body]).off('mousedown.xdsoft'); if (input.unmousewheel) { input.unmousewheel(); } } }; - $(options.ownerDocument) + $(document) .off('keydown.xdsoftctrl keyup.xdsoftctrl') .on('keydown.xdsoftctrl', function (e) { if (e.keyCode === CTRLKEY) { @@ -2578,9 +1573,8 @@ var datetimepickerFactory = function ($) { ctrlDown = false; } }); - - this.each(function () { - var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input; + return this.each(function () { + var datetimepicker = $(this).data('xdsoft_datetimepicker'); if (datetimepicker) { if ($.type(opt) === 'string') { switch (opt) { @@ -2591,27 +1585,16 @@ var datetimepickerFactory = function ($) { case 'hide': datetimepicker.trigger('close.xdsoft'); break; - case 'toggle': - datetimepicker.trigger('toggle.xdsoft'); - break; case 'destroy': destroyDateTimePicker($(this)); break; case 'reset': this.value = this.defaultValue; - if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(dateHelper.parseDate(this.value, options.format))) { + if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) { datetimepicker.data('changed', false); } datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value); break; - case 'validate': - $input = datetimepicker.data('input'); - $input.trigger('blur.xdsoft'); - break; - default: - if (datetimepicker[opt] && $.isFunction(datetimepicker[opt])) { - result = datetimepicker[opt](opt2); - } } } else { datetimepicker @@ -2627,30 +1610,33 @@ var datetimepickerFactory = function ($) { } } }); - - return result; }; - $.fn.datetimepicker.defaults = default_options; - - function HighlightedDate(date, desc, style) { - "use strict"; - this.date = date; - this.desc = desc; - this.style = style; - } -}; -;(function (factory) { - if ( typeof define === 'function' && define.amd ) { - // AMD. Register as an anonymous module. - define(['jquery', 'jquery-mousewheel'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS style for Browserify - module.exports = factory(require('jquery'));; - } else { - // Browser globals - factory(jQuery); - } -}(datetimepickerFactory)); - - +}(jQuery)); +(function () { + + /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) + * Licensed under the MIT License (LICENSE.txt). + * + * Version: 3.1.12 + * + * Requires: jQuery 1.2.2+ + */ + !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); + +// Parse and Format Library +//http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/ + /* + * Copyright (C) 2004 Baron Schwartz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 2.1. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var code="Date.prototype."+funcName+" = function() {return ";var special=false;var ch="";for(var i=0;i 0) {";var regex="";var special=false;var ch="";for(var i=0;i 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$");eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b Date: Thu, 29 Nov 2018 15:11:45 -0500 Subject: [PATCH 049/240] Update xdan/datetimepicker to 2.5.20 full. --- .../Assets/js/libraries/7.datetimepicker.js | 1990 ++++++++++++++--- 1 file changed, 1617 insertions(+), 373 deletions(-) diff --git a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js index 149bfb1bdb5..a89ce23b08e 100644 --- a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js +++ b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js @@ -1,153 +1,193 @@ -/** - * @preserve jQuery DateTimePicker plugin v2.3.8 +/*! + * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2016 + * @version 1.3.4 + * + * Date formatter utility library that allows formatting date/time variables or Date objects using PHP DateTime format. + * @see http://php.net/manual/en/function.date.php + * + * For more JQuery plugins visit http://plugins.krajee.com + * For more Yii related demos visit http://demos.krajee.com + */var DateFormatter;!function(){"use strict";var t,e,r,n,a,u,i;u=864e5,i=3600,t=function(t,e){return"string"==typeof t&&"string"==typeof e&&t.toLowerCase()===e.toLowerCase()},e=function(t,r,n){var a=n||"0",u=t.toString();return u.lengths?"20":"19")+i):s,g=!0;break;case"m":case"n":case"M":case"F":if(isNaN(s)){if(o=d.getMonth(i),!(o>0))return null;y.month=o}else{if(!(s>=1&&12>=s))return null;y.month=s}g=!0;break;case"d":case"j":if(!(s>=1&&31>=s))return null;y.day=s,g=!0;break;case"g":case"h":if(c=n.indexOf("a")>-1?n.indexOf("a"):n.indexOf("A")>-1?n.indexOf("A"):-1,h=a[c],c>-1)f=t(h,p.meridiem[0])?0:t(h,p.meridiem[1])?12:-1,s>=1&&12>=s&&f>-1?y.hour=s+f-1:s>=0&&23>=s&&(y.hour=s);else{if(!(s>=0&&23>=s))return null;y.hour=s}m=!0;break;case"G":case"H":if(!(s>=0&&23>=s))return null;y.hour=s,m=!0;break;case"i":if(!(s>=0&&59>=s))return null;y.min=s,m=!0;break;case"s":if(!(s>=0&&59>=s))return null;y.sec=s,m=!0}if(g===!0&&y.year&&y.month&&y.day)y.date=new Date(y.year,y.month-1,y.day,y.hour,y.min,y.sec,0);else{if(m!==!0)return null;y.date=new Date(0,0,0,y.hour,y.min,y.sec,0)}return y.date},guessDate:function(t,e){if("string"!=typeof t)return t;var r,n,a,u,i,s,o=this,c=t.replace(o.separators,"\x00").split("\x00"),f=/^[djmn]/g,l=e.match(o.validParts),h=new Date,d=0;if(!f.test(l[0]))return t;for(a=0;ar?r:4,n=parseInt(4>r?n.toString().substr(0,4-r)+i:i.substr(0,4)),!n)return null;h.setFullYear(n);break;case 3:h.setHours(s);break;case 4:h.setMinutes(s);break;case 5:h.setSeconds(s)}u=i.substr(d),u.length>0&&c.splice(a+1,0,u)}return h},parseFormat:function(t,r){var n,a=this,s=a.dateSettings,o=/\\?(.?)/gi,c=function(t,e){return n[t]?n[t]():e};return n={d:function(){return e(n.j(),2)},D:function(){return s.daysShort[n.w()]},j:function(){return r.getDate()},l:function(){return s.days[n.w()]},N:function(){return n.w()||7},w:function(){return r.getDay()},z:function(){var t=new Date(n.Y(),n.n()-1,n.j()),e=new Date(n.Y(),0,1);return Math.round((t-e)/u)},W:function(){var t=new Date(n.Y(),n.n()-1,n.j()-n.N()+3),r=new Date(t.getFullYear(),0,4);return e(1+Math.round((t-r)/u/7),2)},F:function(){return s.months[r.getMonth()]},m:function(){return e(n.n(),2)},M:function(){return s.monthsShort[r.getMonth()]},n:function(){return r.getMonth()+1},t:function(){return new Date(n.Y(),n.n(),0).getDate()},L:function(){var t=n.Y();return t%4===0&&t%100!==0||t%400===0?1:0},o:function(){var t=n.n(),e=n.W(),r=n.Y();return r+(12===t&&9>e?1:1===t&&e>9?-1:0)},Y:function(){return r.getFullYear()},y:function(){return n.Y().toString().slice(-2)},a:function(){return n.A().toLowerCase()},A:function(){var t=n.G()<12?0:1;return s.meridiem[t]},B:function(){var t=r.getUTCHours()*i,n=60*r.getUTCMinutes(),a=r.getUTCSeconds();return e(Math.floor((t+n+a+i)/86.4)%1e3,3)},g:function(){return n.G()%12||12},G:function(){return r.getHours()},h:function(){return e(n.g(),2)},H:function(){return e(n.G(),2)},i:function(){return e(r.getMinutes(),2)},s:function(){return e(r.getSeconds(),2)},u:function(){return e(1e3*r.getMilliseconds(),6)},e:function(){var t=/\((.*)\)/.exec(String(r))[1];return t||"Coordinated Universal Time"},I:function(){var t=new Date(n.Y(),0),e=Date.UTC(n.Y(),0),r=new Date(n.Y(),6),a=Date.UTC(n.Y(),6);return t-e!==r-a?1:0},O:function(){var t=r.getTimezoneOffset(),n=Math.abs(t);return(t>0?"-":"+")+e(100*Math.floor(n/60)+n%60,4)},P:function(){var t=n.O();return t.substr(0,3)+":"+t.substr(3,2)},T:function(){var t=(String(r).match(a.tzParts)||[""]).pop().replace(a.tzClip,"");return t||"UTC"},Z:function(){return 60*-r.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(o,c)},r:function(){return"D, d M Y H:i:s O".replace(o,c)},U:function(){return r.getTime()/1e3||0}},c(t,t)},formatDate:function(t,e){var r,n,a,u,i,s=this,o="",c="\\";if("string"==typeof t&&(t=s.parseDate(t,e),!t))return null;if(t instanceof Date){for(a=e.length,r=0;a>r;r++)i=e.charAt(r),"S"!==i&&i!==c&&(r>0&&e.charAt(r-1)===c?o+=i:(u=s.parseFormat(i,t),r!==a-1&&s.intParts.test(i)&&"S"===e.charAt(r+1)&&(n=parseInt(u)||0,u+=s.dateSettings.ordinal(n)),o+=u));return o}return""}}}();/** + * @preserve jQuery DateTimePicker * @homepage http://xdsoft.net/jqplugins/datetimepicker/ - * (c) 2014, Chupurnov Valeriy. + * @author Chupurnov Valeriy () + */ + +/** + * @param {jQuery} $ */ -(function ($) { +var datetimepickerFactory = function ($) { 'use strict'; + var default_options = { i18n: { ar: { // Arabic months: [ "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول" ], - dayOfWeek: [ + dayOfWeekShort: [ "ن", "ث", "ع", "خ", "ج", "س", "ح" - ] + ], + dayOfWeek: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"] }, ro: { // Romanian months: [ - "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie" + "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie" ], - dayOfWeek: [ - "l", "ma", "mi", "j", "v", "s", "d" - ] + dayOfWeekShort: [ + "Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ" + ], + dayOfWeek: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă"] }, id: { // Indonesian months: [ "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" ], - dayOfWeek: [ - "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min" - ] + dayOfWeekShort: [ + "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab" + ], + dayOfWeek: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"] + }, + is: { // Icelandic + months: [ + "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember" + ], + dayOfWeekShort: [ + "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau" + ], + dayOfWeek: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur"] }, bg: { // Bulgarian months: [ "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември" ], - dayOfWeek: [ + dayOfWeekShort: [ "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ] + ], + dayOfWeek: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"] }, fa: { // Persian/Farsi months: [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' ], - dayOfWeek: [ + dayOfWeekShort: [ 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه' - ] + ], + dayOfWeek: ["یک‌شنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنج‌شنبه", "جمعه", "شنبه", "یک‌شنبه"] }, ru: { // Russian months: [ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' ], - dayOfWeek: [ - "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" - ] + dayOfWeekShort: [ + "Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" + ], + dayOfWeek: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"] }, uk: { // Ukrainian months: [ 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень' ], - dayOfWeek: [ + dayOfWeekShort: [ "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт" - ] + ], + dayOfWeek: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота"] }, en: { // English months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - dayOfWeek: [ + dayOfWeekShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - ] + ], + dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] }, el: { // Ελληνικά months: [ "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος" ], - dayOfWeek: [ + dayOfWeekShort: [ "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ" - ] + ], + dayOfWeek: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"] }, de: { // German months: [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ], - dayOfWeek: [ + dayOfWeekShort: [ "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" - ] + ], + dayOfWeek: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"] }, nl: { // Dutch months: [ "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december" ], - dayOfWeek: [ + dayOfWeekShort: [ "zo", "ma", "di", "wo", "do", "vr", "za" - ] + ], + dayOfWeek: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"] }, tr: { // Turkish months: [ "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık" ], - dayOfWeek: [ + dayOfWeekShort: [ "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts" - ] + ], + dayOfWeek: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"] }, fr: { //French months: [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" ], - dayOfWeek: [ + dayOfWeekShort: [ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam" - ] + ], + dayOfWeek: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] }, es: { // Spanish months: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ], - dayOfWeek: [ + dayOfWeekShort: [ "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb" - ] + ], + dayOfWeek: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"] }, th: { // Thai months: [ 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม' ], - dayOfWeek: [ + dayOfWeekShort: [ 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.' - ] + ], + dayOfWeek: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"] }, pl: { // Polish months: [ "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" ], - dayOfWeek: [ + dayOfWeekShort: [ "nd", "pn", "wt", "śr", "cz", "pt", "sb" - ] + ], + dayOfWeek: ["niedziela", "poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota"] }, pt: { // Portuguese months: [ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" ], - dayOfWeek: [ + dayOfWeekShort: [ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" - ] + ], + dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"] }, ch: { // Simplified Chinese months: [ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" ], - dayOfWeek: [ + dayOfWeekShort: [ "日", "一", "二", "三", "四", "五", "六" ] }, @@ -155,71 +195,85 @@ months: [ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" ], - dayOfWeek: [ + dayOfWeekShort: [ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" ] }, + km: { // Khmer (ភាសាខ្មែរ) + months: [ + "មករា​", "កុម្ភៈ", "មិនា​", "មេសា​", "ឧសភា​", "មិថុនា​", "កក្កដា​", "សីហា​", "កញ្ញា​", "តុលា​", "វិច្ឆិកា", "ធ្នូ​" + ], + dayOfWeekShort: ["អាទិ​", "ច័ន្ទ​", "អង្គារ​", "ពុធ​", "ព្រហ​​", "សុក្រ​", "សៅរ៍"], + dayOfWeek: ["អាទិត្យ​", "ច័ន្ទ​", "អង្គារ​", "ពុធ​", "ព្រហស្បតិ៍​", "សុក្រ​", "សៅរ៍"] + }, kr: { // Korean months: [ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" ], - dayOfWeek: [ + dayOfWeekShort: [ "일", "월", "화", "수", "목", "금", "토" - ] + ], + dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"] }, it: { // Italian months: [ "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" ], - dayOfWeek: [ + dayOfWeekShort: [ "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" - ] + ], + dayOfWeek: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"] }, da: { // Dansk months: [ - "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December" + "Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December" ], - dayOfWeek: [ + dayOfWeekShort: [ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ] + ], + dayOfWeek: ["søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag"] }, no: { // Norwegian months: [ "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember" ], - dayOfWeek: [ + dayOfWeekShort: [ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør" - ] + ], + dayOfWeek: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'] }, ja: { // Japanese months: [ "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" ], - dayOfWeek: [ + dayOfWeekShort: [ "日", "月", "火", "水", "木", "金", "土" - ] + ], + dayOfWeek: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜"] }, vi: { // Vietnamese months: [ "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12" ], - dayOfWeek: [ + dayOfWeekShort: [ "CN", "T2", "T3", "T4", "T5", "T6", "T7" - ] + ], + dayOfWeek: ["Chủ nhật", "Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy"] }, sl: { // Slovenščina months: [ "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December" ], - dayOfWeek: [ + dayOfWeekShort: [ "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob" - ] + ], + dayOfWeek: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota"] }, cs: { // Čeština months: [ "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec" ], - dayOfWeek: [ + dayOfWeekShort: [ "Ne", "Po", "Út", "St", "Čt", "Pá", "So" ] }, @@ -227,13 +281,273 @@ months: [ "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December" ], - dayOfWeek: [ + dayOfWeekShort: [ "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo" + ], + dayOfWeek: ["vasárnap", "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat"] + }, + az: { //Azerbaijanian (Azeri) + months: [ + "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr" + ], + dayOfWeekShort: [ + "B", "Be", "Ça", "Ç", "Ca", "C", "Ş" + ], + dayOfWeek: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"] + }, + bs: { //Bosanski + months: [ + "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" + ], + dayOfWeekShort: [ + "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" + ], + dayOfWeek: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"] + }, + ca: { //Català + months: [ + "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre" + ], + dayOfWeekShort: [ + "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds" + ], + dayOfWeek: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte"] + }, + 'en-GB': { //English (British) + months: [ + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + ], + dayOfWeekShort: [ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + ], + dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + }, + et: { //"Eesti" + months: [ + "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember" + ], + dayOfWeekShort: [ + "P", "E", "T", "K", "N", "R", "L" + ], + dayOfWeek: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev"] + }, + eu: { //Euskara + months: [ + "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua" + ], + dayOfWeekShort: [ + "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La." + ], + dayOfWeek: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata'] + }, + fi: { //Finnish (Suomi) + months: [ + "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu" + ], + dayOfWeekShort: [ + "Su", "Ma", "Ti", "Ke", "To", "Pe", "La" + ], + dayOfWeek: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"] + }, + gl: { //Galego + months: [ + "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec" + ], + dayOfWeekShort: [ + "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab" + ], + dayOfWeek: ["Domingo", "Luns", "Martes", "Mércores", "Xoves", "Venres", "Sábado"] + }, + hr: { //Hrvatski + months: [ + "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac" + ], + dayOfWeekShort: [ + "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub" + ], + dayOfWeek: ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"] + }, + ko: { //Korean (한국어) + months: [ + "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" + ], + dayOfWeekShort: [ + "일", "월", "화", "수", "목", "금", "토" + ], + dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"] + }, + lt: { //Lithuanian (lietuvių) + months: [ + "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio" + ], + dayOfWeekShort: [ + "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš" + ], + dayOfWeek: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis"] + }, + lv: { //Latvian (Latviešu) + months: [ + "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris" + ], + dayOfWeekShort: [ + "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St" + ], + dayOfWeek: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena"] + }, + mk: { //Macedonian (Македонски) + months: [ + "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември" + ], + dayOfWeekShort: [ + "нед", "пон", "вто", "сре", "чет", "пет", "саб" + ], + dayOfWeek: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"] + }, + mn: { //Mongolian (Монгол) + months: [ + "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар" + ], + dayOfWeekShort: [ + "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням" + ], + dayOfWeek: ["Даваа", "Мягмар", "Лхагва", "Пүрэв", "Баасан", "Бямба", "Ням"] + }, + 'pt-BR': { //Português(Brasil) + months: [ + "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" + ], + dayOfWeekShort: [ + "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb" + ], + dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"] + }, + sk: { //Slovenčina + months: [ + "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December" + ], + dayOfWeekShort: [ + "Ne", "Po", "Ut", "St", "Št", "Pi", "So" + ], + dayOfWeek: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"] + }, + sq: { //Albanian (Shqip) + months: [ + "Janar", "Shkurt", "Mars", "Prill", "Maj", "Qershor", "Korrik", "Gusht", "Shtator", "Tetor", "Nëntor", "Dhjetor" + ], + dayOfWeekShort: [ + "Die", "Hën", "Mar", "Mër", "Enj", "Pre", "Shtu" + ], + dayOfWeek: ["E Diel", "E Hënë", "E Martē", "E Mërkurë", "E Enjte", "E Premte", "E Shtunë"] + }, + 'sr-YU': { //Serbian (Srpski) + months: [ + "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar" + ], + dayOfWeekShort: [ + "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub" + ], + dayOfWeek: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"] + }, + sr: { //Serbian Cyrillic (Српски) + months: [ + "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар" + ], + dayOfWeekShort: [ + "нед", "пон", "уто", "сре", "чет", "пет", "суб" + ], + dayOfWeek: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"] + }, + sv: { //Svenska + months: [ + "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December" + ], + dayOfWeekShort: [ + "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör" + ], + dayOfWeek: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"] + }, + 'zh-TW': { //Traditional Chinese (繁體中文) + months: [ + "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" + ], + dayOfWeekShort: [ + "日", "一", "二", "三", "四", "五", "六" + ], + dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"] + }, + zh: { //Simplified Chinese (简体中文) + months: [ + "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" + ], + dayOfWeekShort: [ + "日", "一", "二", "三", "四", "五", "六" + ], + dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"] + }, + ug:{ // Uyghur(ئۇيغۇرچە) + months: [ + "1-ئاي","2-ئاي","3-ئاي","4-ئاي","5-ئاي","6-ئاي","7-ئاي","8-ئاي","9-ئاي","10-ئاي","11-ئاي","12-ئاي" + ], + dayOfWeek: [ + "يەكشەنبە", "دۈشەنبە","سەيشەنبە","چارشەنبە","پەيشەنبە","جۈمە","شەنبە" + ] + }, + he: { //Hebrew (עברית) + months: [ + 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר' + ], + dayOfWeekShort: [ + 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת' + ], + dayOfWeek: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"] + }, + hy: { // Armenian + months: [ + "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր" + ], + dayOfWeekShort: [ + "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ" + ], + dayOfWeek: ["Կիրակի", "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ"] + }, + kg: { // Kyrgyz + months: [ + 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы' + ], + dayOfWeekShort: [ + "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише" + ], + dayOfWeek: [ + "Жекшемб", "Дүйшөмб", "Шейшемб", "Шаршемб", "Бейшемби", "Жума", "Ишенб" + ] + }, + rm: { // Romansh + months: [ + "Schaner", "Favrer", "Mars", "Avrigl", "Matg", "Zercladur", "Fanadur", "Avust", "Settember", "October", "November", "December" + ], + dayOfWeekShort: [ + "Du", "Gli", "Ma", "Me", "Gie", "Ve", "So" + ], + dayOfWeek: [ + "Dumengia", "Glindesdi", "Mardi", "Mesemna", "Gievgia", "Venderdi", "Sonda" ] + }, + ka: { // Georgian + months: [ + 'იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი' + ], + dayOfWeekShort: [ + "კვ", "ორშ", "სამშ", "ოთხ", "ხუთ", "პარ", "შაბ" + ], + dayOfWeek: ["კვირა", "ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი"] } }, + + ownerDocument: document, + contentWindow: window, + value: '', - lang: 'en', + rtl: false, format: 'Y/m/d H:i', formatTime: 'H:i', @@ -244,8 +558,10 @@ monthChangeSpinner: true, closeOnDateSelect: false, + closeOnTimeSelect: true, closeOnWithoutClick: true, closeOnInputClick: true, + openOnFocus: true, timepicker: true, datepicker: true, @@ -258,16 +574,20 @@ maxDate: false, minTime: false, maxTime: false, + minDateTime: false, + maxDateTime: false, allowTimes: [], opened: false, initTime: true, inline: false, theme: '', + touchMovedThreshold: 5, onSelectDate: function () {}, onSelectTime: function () {}, onChangeMonth: function () {}, + onGetWeekOfYear: function () {}, onChangeYear: function () {}, onChangeDateTime: function () {}, onShow: function () {}, @@ -277,13 +597,15 @@ withoutCopyright: true, inverseButton: false, hours12: false, - next: 'xdsoft_next', + next: 'xdsoft_next', prev : 'xdsoft_prev', dayOfWeekStart: 0, parentID: 'body', timeHeightInTimePicker: 25, timepickerScrollbar: true, todayButton: true, + prevButton: true, + nextButton: true, defaultSelect: true, scrollMonth: true, @@ -296,16 +618,152 @@ allowBlank: true, yearStart: 1950, yearEnd: 2050, + monthStart: 0, + monthEnd: 11, style: '', id: '', fixed: false, roundTime: 'round', // ceil, floor className: '', weekends: [], + highlightedDates: [], + highlightedPeriods: [], + allowDates : [], + allowDateRe : null, + disabledDates : [], + disabledWeekDays: [], yearOffset: 0, - beforeShowDay: null + beforeShowDay: null, + + enterLikeTab: true, + showApplyButton: false + }; + + var dateHelper = null, + defaultDateHelper = null, + globalLocaleDefault = 'en', + globalLocale = 'en'; + + var dateFormatterOptionsDefault = { + meridiem: ['AM', 'PM'] + }; + + var initDateFormatter = function(){ + var locale = default_options.i18n[globalLocale], + opts = { + days: locale.dayOfWeek, + daysShort: locale.dayOfWeekShort, + months: locale.months, + monthsShort: $.map(locale.months, function(n){ return n.substring(0, 3) }) + }; + + if (typeof DateFormatter === 'function') { + dateHelper = defaultDateHelper = new DateFormatter({ + dateSettings: $.extend({}, dateFormatterOptionsDefault, opts) + }); + } + }; + + var dateFormatters = { + moment: { + default_options:{ + format: 'YYYY/MM/DD HH:mm', + formatDate: 'YYYY/MM/DD', + formatTime: 'HH:mm', + }, + formatter: { + parseDate: function (date, format) { + if(isFormatStandard(format)){ + return defaultDateHelper.parseDate(date, format); + } + var d = moment(date, format); + return d.isValid() ? d.toDate() : false; + }, + + formatDate: function (date, format) { + if(isFormatStandard(format)){ + return defaultDateHelper.formatDate(date, format); + } + return moment(date).format(format); + }, + + formatMask: function(format){ + return format + .replace(/Y{4}/g, '9999') + .replace(/Y{2}/g, '99') + .replace(/M{2}/g, '19') + .replace(/D{2}/g, '39') + .replace(/H{2}/g, '29') + .replace(/m{2}/g, '59') + .replace(/s{2}/g, '59'); + }, + } + } + } + + // for locale settings + $.datetimepicker = { + setLocale: function(locale){ + var newLocale = default_options.i18n[locale] ? locale : globalLocaleDefault; + if (globalLocale !== newLocale) { + globalLocale = newLocale; + // reinit date formatter + initDateFormatter(); + } + }, + + setDateFormatter: function(dateFormatter) { + if(typeof dateFormatter === 'string' && dateFormatters.hasOwnProperty(dateFormatter)){ + var df = dateFormatters[dateFormatter]; + $.extend(default_options, df.default_options); + dateHelper = df.formatter; + } + else { + dateHelper = dateFormatter; + } + }, }; + + var standardFormats = { + RFC_2822: 'D, d M Y H:i:s O', + ATOM: 'Y-m-d\TH:i:sP', + ISO_8601: 'Y-m-d\TH:i:sO', + RFC_822: 'D, d M y H:i:s O', + RFC_850: 'l, d-M-y H:i:s T', + RFC_1036: 'D, d M y H:i:s O', + RFC_1123: 'D, d M Y H:i:s O', + RSS: 'D, d M Y H:i:s O', + W3C: 'Y-m-d\TH:i:sP' + } + + var isFormatStandard = function(format){ + return Object.values(standardFormats).indexOf(format) === -1 ? false : true; + } + + $.extend($.datetimepicker, standardFormats); + + // first init date formatter + initDateFormatter(); + // fix for ie8 + if (!window.getComputedStyle) { + window.getComputedStyle = function (el) { + this.el = el; + this.getPropertyValue = function (prop) { + var re = /(-([a-z]))/g; + if (prop === 'float') { + prop = 'styleFloat'; + } + if (re.test(prop)) { + prop = prop.replace(re, function (a, b, c) { + return c.toUpperCase(); + }); + } + return el.currentStyle[prop] || null; + }; + return this; + }; + } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (obj, start) { var i, j; @@ -315,10 +773,12 @@ return -1; }; } + Date.prototype.countDaysInMonth = function () { return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate(); }; - $.fn.xdsoftScroller = function (percent) { + + $.fn.xdsoftScroller = function (options, percent) { return this.each(function () { var timeboxparent = $(this), pointerEventToXY = function (e) { @@ -334,7 +794,6 @@ } return out; }, - move = 0, timebox, parentHeight, height, @@ -349,6 +808,11 @@ startTopScroll = 0, calcOffset = function () {}; + if (percent === 'hide') { + timeboxparent.find('.xdsoft_scrollbar').hide(); + return; + } + if (!$(this).hasClass('xdsoft_scroller_box')) { timebox = timeboxparent.children().eq(0); parentHeight = timeboxparent[0].clientHeight; @@ -379,16 +843,16 @@ startTopScroll = parseInt(scroller.css('margin-top'), 10); h1 = scrollbar[0].offsetHeight; - if (event.type === 'mousedown') { - if (document) { - $(document.body).addClass('xdsoft_noselect'); + if (event.type === 'mousedown' || event.type === 'touchstart') { + if (options.ownerDocument) { + $(options.ownerDocument.body).addClass('xdsoft_noselect'); } - $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() { - $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee) + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft_scroller', function arguments_callee() { + $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft_scroller', arguments_callee) .off('mousemove.xdsoft_scroller', calcOffset) .removeClass('xdsoft_noselect'); }); - $(document.body).on('mousemove.xdsoft_scroller', calcOffset); + $(options.ownerDocument.body).on('mousemove.xdsoft_scroller', calcOffset); } else { touchStart = true; event.stopPropagation(); @@ -401,7 +865,7 @@ calcOffset(event); } }) - .on('touchend touchcancel', function (event) { + .on('touchend touchcancel', function () { touchStart = false; startTopScroll = 0; }); @@ -463,7 +927,7 @@ } }); - timeboxparent.on('touchend touchcancel', function (event) { + timeboxparent.on('touchend touchcancel', function () { start = false; startTop = 0; }); @@ -472,8 +936,9 @@ }); }; - $.fn.datetimepicker = function (opt) { - var KEY0 = 48, + $.fn.datetimepicker = function (opt, opt2) { + var result = this, + KEY0 = 48, KEY9 = 57, _KEY0 = 96, _KEY9 = 105, @@ -499,12 +964,11 @@ lazyInitTimer = 0, createDateTimePicker, destroyDateTimePicker, - _xdsoft_datetime, lazyInit = function (input) { input - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) { - if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || input.data('xdsoft_datetimepicker')) { + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function initOnActionCallback() { + if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) { return; } clearTimeout(lazyInitTimer); @@ -514,17 +978,17 @@ createDateTimePicker(input); } input - .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback) + .off('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', initOnActionCallback) .trigger('open.xdsoft'); }, 100); }); }; createDateTimePicker = function (input) { - var datetimepicker = $('
    '), + var datetimepicker = $('
    '), xdsoft_copyright = $(''), datepicker = $('
    '), - mounth_picker = $('
    ' + + month_picker = $('
    ' + '
    ' + '
    ' + '
    '), @@ -532,30 +996,47 @@ timepicker = $('
    '), timeboxparent = timepicker.find('.xdsoft_time_box').eq(0), timebox = $('
    '), - scrollbar = $('
    '), - scroller = $('
    '), + applyButton = $(''), + monthselect = $('
    '), yearselect = $('
    '), triggerAfterOpen = false, XDSoft_datetime, - //scroll_element, + xchangeTimer, timerclick, current_time_index, setPos, timer = 0, - timer1 = 0; + _xdsoft_datetime, + forEachAncestorOf; + + if (options.id) { + datetimepicker.attr('id', options.id); + } + if (options.style) { + datetimepicker.attr('style', options.style); + } + if (options.weeks) { + datetimepicker.addClass('xdsoft_showweeks'); + } + if (options.rtl) { + datetimepicker.addClass('xdsoft_rtl'); + } + + datetimepicker.addClass('xdsoft_' + options.theme); + datetimepicker.addClass(options.className); - mounth_picker + month_picker .find('.xdsoft_month span') .after(monthselect); - mounth_picker + month_picker .find('.xdsoft_year span') .after(yearselect); - mounth_picker + month_picker .find('.xdsoft_month,.xdsoft_year') - .on('mousedown.xdsoft', function (event) { + .on('touchstart mousedown.xdsoft', function (event) { var select = $(this).find('.xdsoft_select').eq(0), val = 0, top = 0, @@ -563,7 +1044,7 @@ items, i; - mounth_picker + month_picker .find('.xdsoft_select') .hide(); if (_xdsoft_datetime.currentTime) { @@ -579,37 +1060,65 @@ } } - select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight))); + select.xdsoftScroller(options, top / (select.children()[0].offsetHeight - (select[0].clientHeight))); event.stopPropagation(); return false; }); - mounth_picker + var handleTouchMoved = function (event) { + var evt = event.originalEvent; + var touchPosition = evt.touches ? evt.touches[0] : evt; + this.touchStartPosition = this.touchStartPosition || touchPosition; + var xMovement = Math.abs(this.touchStartPosition.clientX - touchPosition.clientX); + var yMovement = Math.abs(this.touchStartPosition.clientY - touchPosition.clientY); + var distance = Math.sqrt(xMovement * xMovement + yMovement * yMovement); + if(distance > options.touchMovedThreshold) { + this.touchMoved = true; + } + } + + month_picker .find('.xdsoft_select') - .xdsoftScroller() - .on('mousedown.xdsoft', function (event) { + .xdsoftScroller(options) + .on('touchstart mousedown.xdsoft', function (event) { + var evt = event.originalEvent; + this.touchMoved = false; + this.touchStartPosition = evt.touches ? evt.touches[0] : evt; event.stopPropagation(); event.preventDefault(); }) - .on('mousedown.xdsoft', '.xdsoft_option', function (event) { - var year = _xdsoft_datetime.currentTime.getFullYear(); - if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { - _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); - } + .on('touchmove', '.xdsoft_option', handleTouchMoved) + .on('touchend mousedown.xdsoft', '.xdsoft_option', function () { + if (!this.touchMoved) { + if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + } - $(this).parent().parent().hide(); + var year = _xdsoft_datetime.currentTime.getFullYear(); + if (_xdsoft_datetime && _xdsoft_datetime.currentTime) { + _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value')); + } - datetimepicker.trigger('xchange.xdsoft'); - if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { - options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); - } + $(this).parent().parent().hide(); - if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { - options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + datetimepicker.trigger('xchange.xdsoft'); + if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) { + options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } + + if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) { + options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input')); + } } }); + datetimepicker.getValue = function () { + return _xdsoft_datetime.getCurrentTime(); + }; + datetimepicker.setOptions = function (_options) { + var highlightedDates = {}; + options = $.extend(true, {}, options, _options); if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) { @@ -620,6 +1129,83 @@ options.weekends = $.extend(true, [], _options.weekends); } + if (_options.allowDates && $.isArray(_options.allowDates) && _options.allowDates.length) { + options.allowDates = $.extend(true, [], _options.allowDates); + } + + if (_options.allowDateRe && Object.prototype.toString.call(_options.allowDateRe)==="[object String]") { + options.allowDateRe = new RegExp(_options.allowDateRe); + } + + if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) { + $.each(_options.highlightedDates, function (index, value) { + var splitData = $.map(value.split(','), $.trim), + exDesc, + hDate = new HighlightedDate(dateHelper.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style + keyDate = dateHelper.formatDate(hDate.date, options.formatDate); + if (highlightedDates[keyDate] !== undefined) { + exDesc = highlightedDates[keyDate].desc; + if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { + highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; + } + } else { + highlightedDates[keyDate] = hDate; + } + }); + + options.highlightedDates = $.extend(true, [], highlightedDates); + } + + if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) { + highlightedDates = $.extend(true, [], options.highlightedDates); + $.each(_options.highlightedPeriods, function (index, value) { + var dateTest, // start date + dateEnd, + desc, + hDate, + keyDate, + exDesc, + style; + if ($.isArray(value)) { + dateTest = value[0]; + dateEnd = value[1]; + desc = value[2]; + style = value[3]; + } + else { + var splitData = $.map(value.split(','), $.trim); + dateTest = dateHelper.parseDate(splitData[0], options.formatDate); + dateEnd = dateHelper.parseDate(splitData[1], options.formatDate); + desc = splitData[2]; + style = splitData[3]; + } + + while (dateTest <= dateEnd) { + hDate = new HighlightedDate(dateTest, desc, style); + keyDate = dateHelper.formatDate(dateTest, options.formatDate); + dateTest.setDate(dateTest.getDate() + 1); + if (highlightedDates[keyDate] !== undefined) { + exDesc = highlightedDates[keyDate].desc; + if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) { + highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc; + } + } else { + highlightedDates[keyDate] = hDate; + } + } + }); + + options.highlightedDates = $.extend(true, [], highlightedDates); + } + + if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) { + options.disabledDates = $.extend(true, [], _options.disabledDates); + } + + if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) { + options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays); + } + if ((options.open || options.opened) && (!options.inline)) { input.trigger('open.xdsoft'); } @@ -648,10 +1234,10 @@ } if (options.value) { + _xdsoft_datetime.setCurrentTime(options.value); if (input && input.val) { - input.val(options.value); + input.val(_xdsoft_datetime.str); } - _xdsoft_datetime.setCurrentTime(options.value); } if (isNaN(options.dayOfWeekStart)) { @@ -661,149 +1247,71 @@ } if (!options.timepickerScrollbar) { - scrollbar.hide(); + timeboxparent.xdsoftScroller(options, 'hide'); } - if (options.minDate && /^-(.*)$/.test(options.minDate)) { - options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate); + if (options.minDate && /^[\+\-](.*)$/.test(options.minDate)) { + options.minDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.minDate), options.formatDate); } - if (options.maxDate && /^\+(.*)$/.test(options.maxDate)) { - options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate); + if (options.maxDate && /^[\+\-](.*)$/.test(options.maxDate)) { + options.maxDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.maxDate), options.formatDate); } - mounth_picker - .find('.xdsoft_today_button') - .css('visibility', !options.todayButton ? 'hidden' : 'visible'); - - if (options.mask) { - var e, - getCaretPos = function (input) { - try { - if (document.selection && document.selection.createRange) { - var range = document.selection.createRange(); - return range.getBookmark().charCodeAt(2) - 2; - } - if (input.setSelectionRange) { - return input.selectionStart; - } - } catch (e) { - return 0; - } - }, - setCaretPos = function (node, pos) { - node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node; - if (!node) { - return false; - } - if (node.createTextRange) { - var textRange = node.createTextRange(); - textRange.collapse(true); - textRange.moveEnd('character', pos); - textRange.moveStart('character', pos); - textRange.select(); - return true; - } - if (node.setSelectionRange) { - node.setSelectionRange(pos, pos); - return true; - } - return false; - }, - isValidValue = function (mask, value) { - var reg = mask - .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') - .replace(/_/g, '{digit+}') - .replace(/([0-9]{1})/g, '{digit$1}') - .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') - .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); - return (new RegExp(reg)).test(value); - }; - input.off('keydown.xdsoft'); - - if (options.mask === true) { - options.mask = options.format - .replace(/Y/g, '9999') - .replace(/F/g, '9999') - .replace(/m/g, '19') - .replace(/d/g, '39') - .replace(/H/g, '29') - .replace(/i/g, '59') - .replace(/s/g, '59'); - } - - if ($.type(options.mask) === 'string') { - if (!isValidValue(options.mask, input.val())) { - input.val(options.mask.replace(/[0-9]/g, '_')); - } - - input.on('keydown.xdsoft', function (event) { - var val = this.value, - key = event.which, - pos, - digit; + if (options.minDateTime && /^\+(.*)$/.test(options.minDateTime)) { + options.minDateTime = _xdsoft_datetime.strToDateTime(options.minDateTime).dateFormat(options.formatDate); + } - if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) { - pos = getCaretPos(this); - digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_'; + if (options.maxDateTime && /^\+(.*)$/.test(options.maxDateTime)) { + options.maxDateTime = _xdsoft_datetime.strToDateTime(options.maxDateTime).dateFormat(options.formatDate); + } - if ((key === BACKSPACE || key === DEL) && pos) { - pos -= 1; - digit = '_'; - } + applyButton.toggle(options.showApplyButton); - while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { - pos += (key === BACKSPACE || key === DEL) ? -1 : 1; - } + month_picker + .find('.xdsoft_today_button') + .css('visibility', !options.todayButton ? 'hidden' : 'visible'); - val = val.substr(0, pos) + digit + val.substr(pos + 1); - if ($.trim(val) === '') { - val = options.mask.replace(/[0-9]/g, '_'); - } else { - if (pos === options.mask.length) { - event.preventDefault(); - return false; - } - } + month_picker + .find('.' + options.prev) + .css('visibility', !options.prevButton ? 'hidden' : 'visible'); - pos += (key === BACKSPACE || key === DEL) ? 0 : 1; - while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { - pos += (key === BACKSPACE || key === DEL) ? -1 : 1; - } + month_picker + .find('.' + options.next) + .css('visibility', !options.nextButton ? 'hidden' : 'visible'); - if (isValidValue(options.mask, val)) { - this.value = val; - setCaretPos(this, pos); - } else if ($.trim(val) === '') { - this.value = options.mask.replace(/[0-9]/g, '_'); - } else { - input.trigger('error_input.xdsoft'); - } - } else { - if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { - return true; - } - } + setMask(options); - event.preventDefault(); - return false; - }); - } - } if (options.validateOnBlur) { input .off('blur.xdsoft') .on('blur.xdsoft', function () { - if (options.allowBlank && !$.trim($(this).val()).length) { + if (options.allowBlank && (!$.trim($(this).val()).length || + (typeof options.mask === "string" && $.trim($(this).val()) === options.mask.replace(/[0-9]/g, '_')))) { $(this).val(null); datetimepicker.data('xdsoft_datetime').empty(); - } else if (!Date.parseDate($(this).val(), options.format)) { - $(this).val((_xdsoft_datetime.now()).dateFormat(options.format)); - datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } else { + var d = dateHelper.parseDate($(this).val(), options.format); + if (d) { // parseDate() may skip some invalid parts like date or time, so make it clear for user: show parsed date/time + $(this).val(dateHelper.formatDate(d, options.format)); + } else { + var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')), + splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join('')); + + // parse the numbers as 0312 => 03:12 + if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) { + $(this).val([splittedHours, splittedMinutes].map(function (item) { + return item > 9 ? item : '0' + item; + }).join(':')); + } else { + $(this).val(dateHelper.formatDate(_xdsoft_datetime.now(), options.format)); + } + } datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } + datetimepicker.trigger('changedatetime.xdsoft'); + datetimepicker.trigger('close.xdsoft'); }); } options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1; @@ -815,7 +1323,7 @@ datetimepicker .data('options', options) - .on('mousedown.xdsoft', function (event) { + .on('touchstart mousedown.xdsoft', function (event) { event.stopPropagation(); event.preventDefault(); yearselect.hide(); @@ -825,10 +1333,10 @@ //scroll_element = timepicker.find('.xdsoft_time_box'); timeboxparent.append(timebox); - timeboxparent.xdsoftScroller(); + timeboxparent.xdsoftScroller(options); datetimepicker.on('afterOpen.xdsoft', function () { - timeboxparent.xdsoftScroller(); + timeboxparent.xdsoftScroller(options); }); datetimepicker @@ -841,8 +1349,9 @@ } datepicker - .append(mounth_picker) - .append(calendar); + .append(month_picker) + .append(calendar) + .append(applyButton); $(options.parentID) .append(datetimepicker); @@ -855,22 +1364,21 @@ time; if (!norecursion && options.defaultDate) { - date = _this.strToDate(options.defaultDate); + date = _this.strToDateTime(options.defaultDate); d.setFullYear(date.getFullYear()); d.setMonth(date.getMonth()); d.setDate(date.getDate()); } - if (options.yearOffset) { - d.setFullYear(d.getFullYear() + options.yearOffset); - } + d.setFullYear(d.getFullYear()); if (!norecursion && options.defaultTime) { time = _this.strtotime(options.defaultTime); d.setHours(time.getHours()); d.setMinutes(time.getMinutes()); + d.setSeconds(time.getSeconds()); + d.setMilliseconds(time.getMilliseconds()); } - return d; }; @@ -881,8 +1389,20 @@ return !isNaN(d.getTime()); }; - _this.setCurrentTime = function (dTime) { - _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now(); + _this.setCurrentTime = function (dTime, requireValidDate) { + if (typeof dTime === 'string') { + _this.currentTime = _this.strToDateTime(dTime); + } + else if (_this.isValidDate(dTime)) { + _this.currentTime = dTime; + } + else if (!dTime && !requireValidDate && options.allowBlank && !options.inline) { + _this.currentTime = null; + } + else { + _this.currentTime = _this.now(); + } + datetimepicker.trigger('xchange.xdsoft'); }; @@ -890,11 +1410,16 @@ _this.currentTime = null; }; - _this.getCurrentTime = function (dTime) { + _this.getCurrentTime = function () { return _this.currentTime; }; _this.nextMonth = function () { + + if (_this.currentTime === undefined || _this.currentTime === null) { + _this.currentTime = _this.now(); + } + var month = _this.currentTime.getMonth() + 1, year; if (month === 12) { @@ -925,6 +1450,11 @@ }; _this.prevMonth = function () { + + if (_this.currentTime === undefined || _this.currentTime === null) { + _this.currentTime = _this.now(); + } + var month = _this.currentTime.getMonth() - 1; if (month === -1) { _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1); @@ -945,7 +1475,19 @@ }; _this.getWeekOfYear = function (datetime) { + if (options.onGetWeekOfYear && $.isFunction(options.onGetWeekOfYear)) { + var week = options.onGetWeekOfYear.call(datetimepicker, datetime); + if (typeof week !== 'undefined') { + return week; + } + } var onejan = new Date(datetime.getFullYear(), 0, 1); + + //First week of the year is th one with the first Thursday according to ISO8601 + if (onejan.getDay() !== 4) { + onejan.setMonth(0, 1 + ((4 - onejan.getDay()+ 7) % 7)); + } + return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7); }; @@ -956,15 +1498,17 @@ return sDateTime; } - tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime); + tmpDate = /^([+-]{1})(.*)$/.exec(sDateTime); + if (tmpDate) { - tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate); + tmpDate[2] = dateHelper.parseDate(tmpDate[2], options.formatDate); } + if (tmpDate && tmpDate[2]) { timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000; - currentTime = new Date((_xdsoft_datetime.now()).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); + currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset); } else { - currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now(); + currentTime = sDateTime ? dateHelper.parseDate(sDateTime, options.format) : _this.now(); } if (!_this.isValidDate(currentTime)) { @@ -979,7 +1523,7 @@ return sDate; } - var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true); + var currentTime = sDate ? dateHelper.parseDate(sDate, options.formatDate) : _this.now(true); if (!_this.isValidDate(currentTime)) { currentTime = _this.now(true); } @@ -990,7 +1534,7 @@ if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) { return sTime; } - var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true); + var currentTime = sTime ? dateHelper.parseDate(sTime, options.formatTime) : _this.now(true); if (!_this.isValidDate(currentTime)) { currentTime = _this.now(true); } @@ -998,32 +1542,56 @@ }; _this.str = function () { - return _this.currentTime.dateFormat(options.format); + var format = options.format; + if (options.yearOffset) { + format = format.replace('Y', _this.currentTime.getFullYear() + options.yearOffset); + format = format.replace('y', String(_this.currentTime.getFullYear() + options.yearOffset).substring(2, 4)); + } + return dateHelper.formatDate(_this.currentTime, format); }; _this.currentTime = this.now(); }; _xdsoft_datetime = new XDSoft_datetime(); - mounth_picker + applyButton.on('touchend click', function (e) {//pathbrite + e.preventDefault(); + datetimepicker.data('changed', true); + _xdsoft_datetime.setCurrentTime(getCurrentValue()); + input.val(_xdsoft_datetime.str()); + datetimepicker.trigger('close.xdsoft'); + }); + month_picker .find('.xdsoft_today_button') - .on('mousedown.xdsoft', function () { + .on('touchend mousedown.xdsoft', function () { datetimepicker.data('changed', true); - _xdsoft_datetime.setCurrentTime(0); + _xdsoft_datetime.setCurrentTime(0, true); datetimepicker.trigger('afterOpen.xdsoft'); }).on('dblclick.xdsoft', function () { + var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate; + currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); + minDate = _xdsoft_datetime.strToDate(options.minDate); + minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); + if (currentDate < minDate) { + return; + } + maxDate = _xdsoft_datetime.strToDate(options.maxDate); + maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()); + if (currentDate > maxDate) { + return; + } input.val(_xdsoft_datetime.str()); + input.trigger('change'); datetimepicker.trigger('close.xdsoft'); }); - mounth_picker + month_picker .find('.xdsoft_prev,.xdsoft_next') - .on('mousedown.xdsoft', function () { + .on('touchend mousedown.xdsoft', function () { var $this = $(this), timer = 0, stop = false; (function arguments_callee1(v) { - var month = _xdsoft_datetime.currentTime.getMonth(); if ($this.hasClass(options.next)) { _xdsoft_datetime.nextMonth(); } else if ($this.hasClass(options.prev)) { @@ -1036,16 +1604,16 @@ } }(500)); - $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() { + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee2() { clearTimeout(timer); stop = true; - $([document.body, window]).off('mouseup.xdsoft', arguments_callee2); + $([options.ownerDocument.body, options.contentWindow]).off('touchend mouseup.xdsoft', arguments_callee2); }); }); timepicker .find('.xdsoft_prev,.xdsoft_next') - .on('mousedown.xdsoft', function () { + .on('touchend mousedown.xdsoft', function () { var $this = $(this), timer = 0, stop = false, @@ -1059,17 +1627,30 @@ } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) { timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px'); } - timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]); + /** + * Fixed bug: + * When using css3 transition, it will cause a bug that you cannot scroll the timepicker list. + * The reason is that the transition-duration time, if you set it to 0, all things fine, otherwise, this + * would cause a bug when you use jquery.css method. + * Let's say: * { transition: all .5s ease; } + * jquery timebox.css('marginTop') will return the original value which is before you clicking the next/prev button, + * meanwhile the timebox[0].style.marginTop will return the right value which is after you clicking the + * next/prev button. + * + * What we should do: + * Replace timebox.css('marginTop') with timebox[0].style.marginTop. + */ + timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox[0].style.marginTop, 10) / (height - pheight))]); period = (period > 10) ? 10 : period - 10; if (!stop) { timer = setTimeout(arguments_callee4, v || period); } }(500)); - $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() { + $([options.ownerDocument.body, options.contentWindow]).on('touchend mouseup.xdsoft', function arguments_callee5() { clearTimeout(timer); stop = true; - $([document.body, window]) - .off('mouseup.xdsoft', arguments_callee5); + $([options.ownerDocument.body, options.contentWindow]) + .off('touchend mouseup.xdsoft', arguments_callee5); }); }); @@ -1079,6 +1660,11 @@ .on('xchange.xdsoft', function (event) { clearTimeout(xchangeTimer); xchangeTimer = setTimeout(function () { + + if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + } + var table = '', start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0), i = 0, @@ -1086,6 +1672,10 @@ today = _xdsoft_datetime.now(), maxDate = false, minDate = false, + minDateTime = false, + maxDateTime = false, + hDate, + day, d, y, m, @@ -1094,8 +1684,9 @@ customDateSettings, newRow = true, time = '', - h = '', - line_time; + h, + line_time, + description; while (start.getDay() !== options.dayOfWeekStart) { start.setDate(start.getDate() - 1); @@ -1108,7 +1699,7 @@ } for (j = 0; j < 7; j += 1) { - table += '' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + ''; + table += '' + options.i18n[globalLocale].dayOfWeekShort[(j + options.dayOfWeekStart) % 7] + ''; } table += ''; @@ -1124,14 +1715,31 @@ minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()); } + if (options.minDateTime !== false) { + minDateTime = _xdsoft_datetime.strToDate(options.minDateTime); + minDateTime = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), minDateTime.getHours(), minDateTime.getMinutes(), minDateTime.getSeconds()); + } + + if (options.maxDateTime !== false) { + maxDateTime = _xdsoft_datetime.strToDate(options.maxDateTime); + maxDateTime = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), maxDateTime.getHours(), maxDateTime.getMinutes(), maxDateTime.getSeconds()); + } + + var maxDateTimeDay; + if (maxDateTime !== false) { + maxDateTimeDay = ((maxDateTime.getFullYear() * 12) + maxDateTime.getMonth()) * 31 + maxDateTime.getDate(); + } + while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) { classes = []; i += 1; + day = start.getDay(); d = start.getDate(); y = start.getFullYear(); m = start.getMonth(); w = _xdsoft_datetime.getWeekOfYear(start); + description = ''; classes.push('xdsoft_date'); @@ -1141,7 +1749,32 @@ customDateSettings = null; } - if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) { + if(options.allowDateRe && Object.prototype.toString.call(options.allowDateRe) === "[object RegExp]"){ + if(!options.allowDateRe.test(dateHelper.formatDate(start, options.formatDate))){ + classes.push('xdsoft_disabled'); + } + } + + if(options.allowDates && options.allowDates.length>0){ + if(options.allowDates.indexOf(dateHelper.formatDate(start, options.formatDate)) === -1){ + classes.push('xdsoft_disabled'); + } + } + + var currentDay = ((start.getFullYear() * 12) + start.getMonth()) * 31 + start.getDate(); + if ((maxDate !== false && start > maxDate) || (minDateTime !== false && start < minDateTime) || (minDate !== false && start < minDate) || (maxDateTime !== false && currentDay > maxDateTimeDay) || (customDateSettings && customDateSettings[0] === false)) { + classes.push('xdsoft_disabled'); + } + + if (options.disabledDates.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { + classes.push('xdsoft_disabled'); + } + + if (options.disabledWeekDays.indexOf(day) !== -1) { + classes.push('xdsoft_disabled'); + } + + if (input.is('[disabled]')) { classes.push('xdsoft_disabled'); } @@ -1153,18 +1786,24 @@ classes.push('xdsoft_other_month'); } - if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { + if ((options.defaultSelect || datetimepicker.data('changed')) && dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { classes.push('xdsoft_current'); } - if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) { + if (dateHelper.formatDate(today, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) { classes.push('xdsoft_today'); } - if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) === -1) { + if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) { classes.push('xdsoft_weekend'); } + if (options.highlightedDates[dateHelper.formatDate(start, options.formatDate)] !== undefined) { + hDate = options.highlightedDates[dateHelper.formatDate(start, options.formatDate)]; + classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style); + description = hDate.desc === undefined ? '' : hDate.desc; + } + if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) { classes.push(options.beforeShowDay(start)); } @@ -1177,7 +1816,7 @@ } } - table += '' + + table += '' + '
    ' + d + '
    ' + ''; @@ -1192,25 +1831,64 @@ calendar.html(table); - mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]); - mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear()); + month_picker.find('.xdsoft_label span').eq(0).text(options.i18n[globalLocale].months[_xdsoft_datetime.currentTime.getMonth()]); + month_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear() + options.yearOffset); // generate timebox time = ''; h = ''; m = ''; + + var minTimeMinutesOfDay = 0; + if (options.minTime !== false) { + var t = _xdsoft_datetime.strtotime(options.minTime); + minTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); + } + var maxTimeMinutesOfDay = 24 * 60; + if (options.maxTime !== false) { + var t = _xdsoft_datetime.strtotime(options.maxTime); + maxTimeMinutesOfDay = 60 * t.getHours() + t.getMinutes(); + } + + if (options.minDateTime !== false) { + var t = _xdsoft_datetime.strToDateTime(options.minDateTime); + var currentDayIsMinDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); + if (currentDayIsMinDateTimeDay) { + var m = 60 * t.getHours() + t.getMinutes(); + if (m > minTimeMinutesOfDay) minTimeMinutesOfDay = m; + } + } + + if (options.maxDateTime !== false) { + var t = _xdsoft_datetime.strToDateTime(options.maxDateTime); + var currentDayIsMaxDateTimeDay = dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(t, options.formatDate); + if (currentDayIsMaxDateTimeDay) { + var m = 60 * t.getHours() + t.getMinutes(); + if (m < maxTimeMinutesOfDay) maxTimeMinutesOfDay = m; + } + } + line_time = function line_time(h, m) { - var now = _xdsoft_datetime.now(); + var now = _xdsoft_datetime.now(), current_time, + isALlowTimesInit = options.allowTimes && $.isArray(options.allowTimes) && options.allowTimes.length; now.setHours(h); h = parseInt(now.getHours(), 10); now.setMinutes(m); m = parseInt(now.getMinutes(), 10); - classes = []; - if ((options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) { + var currentMinutesOfDay = 60 * h + m; + if (input.is('[disabled]') || (currentMinutesOfDay >= maxTimeMinutesOfDay) || (currentMinutesOfDay < minTimeMinutesOfDay)) { classes.push('xdsoft_disabled'); } - if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && parseInt(_xdsoft_datetime.currentTime.getHours(), 10) === parseInt(h, 10) && (options.step > 59 || Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step === parseInt(m, 10))) { + + current_time = new Date(_xdsoft_datetime.currentTime); + current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10)); + + if (!isALlowTimesInit) { + current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step); + } + + if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && ((!isALlowTimesInit && options.step > 59) || current_time.getMinutes() === parseInt(m, 10))) { if (options.defaultSelect || datetimepicker.data('changed')) { classes.push('xdsoft_current'); } else if (options.initTime) { @@ -1220,12 +1898,15 @@ if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) { classes.push('xdsoft_today'); } - time += '
    ' + now.dateFormat(options.formatTime) + '
    '; + time += '
    ' + dateHelper.formatDate(now, options.formatTime) + '
    '; }; if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) { for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) { for (j = 0; j < 60; j += options.step) { + var currentMinutesOfDay = i * 60 + j; + if (currentMinutesOfDay < minTimeMinutesOfDay) continue; + if (currentMinutesOfDay >= maxTimeMinutesOfDay) continue; h = (i < 10 ? '0' : '') + i; m = (j < 10 ? '0' : '') + j; line_time(h, m); @@ -1242,16 +1923,15 @@ timebox.html(time); opt = ''; - i = 0; - for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) { - opt += '
    ' + i + '
    '; + for (i = parseInt(options.yearStart, 10); i <= parseInt(options.yearEnd, 10); i += 1) { + opt += '
    ' + (i + options.yearOffset) + '
    '; } yearselect.children().eq(0) .html(opt); - for (i = 0, opt = ''; i <= 11; i += 1) { - opt += '
    ' + options.i18n[options.lang].months[i] + '
    '; + for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) { + opt += '
    ' + options.i18n[globalLocale].months[i] + '
    '; } monthselect.children().eq(0).html(opt); $(datetimepicker) @@ -1283,7 +1963,7 @@ timerclick = 0; calendar - .on('click.xdsoft', 'td', function (xdevent) { + .on('touchend click.xdsoft', 'td', function (xdevent) { xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap timerclick += 1; var $this = $(this), @@ -1306,9 +1986,6 @@ datetimepicker.trigger('select.xdsoft', [currentTime]); input.val(_xdsoft_datetime.str()); - if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === 0 && !options.timepicker))) && !options.inline) { - datetimepicker.trigger('close.xdsoft'); - } if (options.onSelectDate && $.isFunction(options.onSelectDate)) { options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); @@ -1317,43 +1994,51 @@ datetimepicker.data('changed', true); datetimepicker.trigger('xchange.xdsoft'); datetimepicker.trigger('changedatetime.xdsoft'); + if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) { + datetimepicker.trigger('close.xdsoft'); + } setTimeout(function () { timerclick = 0; }, 200); }); timebox - .on('click.xdsoft', 'div', function (xdevent) { - xdevent.stopPropagation(); - var $this = $(this), - currentTime = _xdsoft_datetime.currentTime; - - if (currentTime === undefined || currentTime === null) { - _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); - currentTime = _xdsoft_datetime.currentTime; - } + .on('touchstart', 'div', function (xdevent) { + this.touchMoved = false; + }) + .on('touchmove', 'div', handleTouchMoved) + .on('touchend click.xdsoft', 'div', function (xdevent) { + if (!this.touchMoved) { + xdevent.stopPropagation(); + var $this = $(this), + currentTime = _xdsoft_datetime.currentTime; + + if (currentTime === undefined || currentTime === null) { + _xdsoft_datetime.currentTime = _xdsoft_datetime.now(); + currentTime = _xdsoft_datetime.currentTime; + } - if ($this.hasClass('xdsoft_disabled')) { - return false; - } - currentTime.setHours($this.data('hour')); - currentTime.setMinutes($this.data('minute')); - datetimepicker.trigger('select.xdsoft', [currentTime]); + if ($this.hasClass('xdsoft_disabled')) { + return false; + } + currentTime.setHours($this.data('hour')); + currentTime.setMinutes($this.data('minute')); + datetimepicker.trigger('select.xdsoft', [currentTime]); - datetimepicker.data('input').val(_xdsoft_datetime.str()); - if (!options.inline) { - datetimepicker.trigger('close.xdsoft'); - } + datetimepicker.data('input').val(_xdsoft_datetime.str()); - if (options.onSelectTime && $.isFunction(options.onSelectTime)) { - options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + if (options.onSelectTime && $.isFunction(options.onSelectTime)) { + options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent); + } + datetimepicker.data('changed', true); + datetimepicker.trigger('xchange.xdsoft'); + datetimepicker.trigger('changedatetime.xdsoft'); + if (options.inline !== true && options.closeOnTimeSelect === true) { + datetimepicker.trigger('close.xdsoft'); + } } - datetimepicker.data('changed', true); - datetimepicker.trigger('xchange.xdsoft'); - datetimepicker.trigger('changedatetime.xdsoft'); }); - datepicker .on('mousewheel.xdsoft', function (event) { if (!options.scrollMonth) { @@ -1416,29 +2101,135 @@ current_time_index = 0; + /** + * Runs the callback for each of the specified node's ancestors. + * + * Return FALSE from the callback to stop ascending. + * + * @param {DOMNode} node + * @param {Function} callback + * @returns {undefined} + */ + forEachAncestorOf = function (node, callback) { + do { + node = node.parentNode; + + if (!node || callback(node) === false) { + break; + } + } while (node.nodeName !== 'HTML'); + }; + + /** + * Sets the position of the picker. + * + * @returns {undefined} + */ setPos = function () { - var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute"; + var dateInputOffset, + dateInputElem, + verticalPosition, + left, + position, + datetimepickerElem, + dateInputHasFixedAncestor, + $dateInput, + windowWidth, + verticalAnchorEdge, + datetimepickerCss, + windowHeight, + windowScrollTop; + + $dateInput = datetimepicker.data('input'); + dateInputOffset = $dateInput.offset(); + dateInputElem = $dateInput[0]; + + verticalAnchorEdge = 'top'; + verticalPosition = (dateInputOffset.top + dateInputElem.offsetHeight) - 1; + left = dateInputOffset.left; + position = "absolute"; + + windowWidth = $(options.contentWindow).width(); + windowHeight = $(options.contentWindow).height(); + windowScrollTop = $(options.contentWindow).scrollTop(); + + if ((options.ownerDocument.documentElement.clientWidth - dateInputOffset.left) < datepicker.parent().outerWidth(true)) { + var diff = datepicker.parent().outerWidth(true) - dateInputElem.offsetWidth; + left = left - diff; + } + + if ($dateInput.parent().css('direction') === 'rtl') { + left -= (datetimepicker.outerWidth() - $dateInput.outerWidth()); + } + if (options.fixed) { - top -= $(window).scrollTop(); - left -= $(window).scrollLeft(); + verticalPosition -= windowScrollTop; + left -= $(options.contentWindow).scrollLeft(); position = "fixed"; } else { - if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) { - top = offset.top - datetimepicker[0].offsetHeight + 1; + dateInputHasFixedAncestor = false; + + forEachAncestorOf(dateInputElem, function (ancestorNode) { + if (ancestorNode === null) { + return false; + } + + if (options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position') === 'fixed') { + dateInputHasFixedAncestor = true; + return false; + } + }); + + if (dateInputHasFixedAncestor) { + position = 'fixed'; + + //If the picker won't fit entirely within the viewport then display it above the date input. + if (verticalPosition + datetimepicker.outerHeight() > windowHeight + windowScrollTop) { + verticalAnchorEdge = 'bottom'; + verticalPosition = (windowHeight + windowScrollTop) - dateInputOffset.top; + } else { + verticalPosition -= windowScrollTop; + } + } else { + if (verticalPosition + datetimepicker[0].offsetHeight > windowHeight + windowScrollTop) { + verticalPosition = dateInputOffset.top - datetimepicker[0].offsetHeight + 1; + } } - if (top < 0) { - top = 0; + + if (verticalPosition < 0) { + verticalPosition = 0; } - if (left + datetimepicker[0].offsetWidth > $(window).width()) { - left = $(window).width() - datetimepicker[0].offsetWidth; + + if (left + dateInputElem.offsetWidth > windowWidth) { + left = windowWidth - dateInputElem.offsetWidth; } } - datetimepicker.css({ - left: left, - top: top, - position: position + + datetimepickerElem = datetimepicker[0]; + + forEachAncestorOf(datetimepickerElem, function (ancestorNode) { + var ancestorNodePosition; + + ancestorNodePosition = options.contentWindow.getComputedStyle(ancestorNode).getPropertyValue('position'); + + if (ancestorNodePosition === 'relative' && windowWidth >= ancestorNode.offsetWidth) { + left = left - ((windowWidth - ancestorNode.offsetWidth) / 2); + return false; + } }); + + datetimepickerCss = { + position: position, + left: left, + top: '', //Initialize to prevent previous values interfering with new ones. + bottom: '' //Initialize to prevent previous values interfering with new ones. + }; + + datetimepickerCss[verticalAnchorEdge] = verticalPosition; + + datetimepicker.css(datetimepickerCss); }; + datetimepicker .on('open.xdsoft', function (event) { var onShow = true; @@ -1448,21 +2239,21 @@ if (onShow !== false) { datetimepicker.show(); setPos(); - $(window) + $(options.contentWindow) .off('resize.xdsoft', setPos) .on('resize.xdsoft', setPos); if (options.closeOnWithoutClick) { - $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() { + $([options.ownerDocument.body, options.contentWindow]).on('touchstart mousedown.xdsoft', function arguments_callee6() { datetimepicker.trigger('close.xdsoft'); - $([document.body, window]).off('mousedown.xdsoft', arguments_callee6); + $([options.ownerDocument.body, options.contentWindow]).off('touchstart mousedown.xdsoft', arguments_callee6); }); } } }) .on('close.xdsoft', function (event) { var onClose = true; - mounth_picker + month_picker .find('.xdsoft_month,.xdsoft_year') .find('.xdsoft_select') .hide(); @@ -1474,16 +2265,21 @@ } event.stopPropagation(); }) + .on('toggle.xdsoft', function () { + if (datetimepicker.is(':visible')) { + datetimepicker.trigger('close.xdsoft'); + } else { + datetimepicker.trigger('open.xdsoft'); + } + }) .data('input', input); timer = 0; - timer1 = 0; datetimepicker.data('xdsoft_datetime', _xdsoft_datetime); datetimepicker.setOptions(options); function getCurrentValue() { - var ct = false, time; if (options.startDate) { @@ -1492,8 +2288,11 @@ ct = options.value || ((input && input.val && input.val()) ? input.val() : ''); if (ct) { ct = _xdsoft_datetime.strToDateTime(ct); + if (options.yearOffset) { + ct = new Date(ct.getFullYear() - options.yearOffset, ct.getMonth(), ct.getDate(), ct.getHours(), ct.getMinutes(), ct.getSeconds(), ct.getMilliseconds()); + } } else if (options.defaultDate) { - ct = _xdsoft_datetime.strToDate(options.defaultDate); + ct = _xdsoft_datetime.strToDateTime(options.defaultDate); if (options.defaultTime) { time = _xdsoft_datetime.strtotime(options.defaultTime); ct.setHours(time.getHours()); @@ -1511,31 +2310,243 @@ return ct || 0; } + function setMask(options) { + + var isValidValue = function (mask, value) { + var reg = mask + .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1') + .replace(/_/g, '{digit+}') + .replace(/([0-9]{1})/g, '{digit$1}') + .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}') + .replace(/\{digit[\+]\}/g, '[0-9_]{1}'); + return (new RegExp(reg)).test(value); + }, + getCaretPos = function (input) { + try { + if (options.ownerDocument.selection && options.ownerDocument.selection.createRange) { + var range = options.ownerDocument.selection.createRange(); + return range.getBookmark().charCodeAt(2) - 2; + } + if (input.setSelectionRange) { + return input.selectionStart; + } + } catch (e) { + return 0; + } + }, + setCaretPos = function (node, pos) { + node = (typeof node === "string" || node instanceof String) ? options.ownerDocument.getElementById(node) : node; + if (!node) { + return false; + } + if (node.createTextRange) { + var textRange = node.createTextRange(); + textRange.collapse(true); + textRange.moveEnd('character', pos); + textRange.moveStart('character', pos); + textRange.select(); + return true; + } + if (node.setSelectionRange) { + node.setSelectionRange(pos, pos); + return true; + } + return false; + }; + + if(options.mask) { + input.off('keydown.xdsoft'); + } + + if (options.mask === true) { + if (dateHelper.formatMask) { + options.mask = dateHelper.formatMask(options.format) + } else { + options.mask = options.format + .replace(/Y/g, '9999') + .replace(/F/g, '9999') + .replace(/m/g, '19') + .replace(/d/g, '39') + .replace(/H/g, '29') + .replace(/i/g, '59') + .replace(/s/g, '59'); + } + } + + if ($.type(options.mask) === 'string') { + if (!isValidValue(options.mask, input.val())) { + input.val(options.mask.replace(/[0-9]/g, '_')); + setCaretPos(input[0], 0); + } + + input.on('paste.xdsoft', function (event) { + // couple options here + // 1. return false - tell them they can't paste + // 2. insert over current characters - minimal validation + // 3. full fledged parsing and validation + // let's go option 2 for now + + // fires multiple times for some reason + + // https://stackoverflow.com/a/30496488/1366033 + var clipboardData = event.clipboardData || event.originalEvent.clipboardData || window.clipboardData, + pastedData = clipboardData.getData('text'), + val = this.value, + pos = this.selectionStart + + var valueBeforeCursor = val.substr(0, pos); + var valueAfterPaste = val.substr(pos + pastedData.length); + + val = valueBeforeCursor + pastedData + valueAfterPaste; + pos += pastedData.length; + + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + + event.preventDefault(); + return false; + }); + + input.on('keydown.xdsoft', function (event) { + var val = this.value, + key = event.which, + pos = this.selectionStart, + selEnd = this.selectionEnd, + hasSel = pos !== selEnd, + digit; + + // only alow these characters + if (((key >= KEY0 && key <= KEY9) || + (key >= _KEY0 && key <= _KEY9)) || + (key === BACKSPACE || key === DEL)) { + + // get char to insert which is new character or placeholder ('_') + digit = (key === BACKSPACE || key === DEL) ? '_' : + String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key); + + // we're deleting something, we're not at the start, and have normal cursor, move back one + // if we have a selection length, cursor actually sits behind deletable char, not in front + if (key === BACKSPACE && pos && !hasSel) { + pos -= 1; + } + + // don't stop on a separator, continue whatever direction you were going + // value char - keep incrementing position while on separator char and we still have room + // del char - keep decrementing position while on separator char and we still have room + while (true) { + var maskValueAtCurPos = options.mask.substr(pos, 1); + var posShorterThanMaskLength = pos < options.mask.length; + var posGreaterThanZero = pos > 0; + var notNumberOrPlaceholder = /[^0-9_]/; + var curPosOnSep = notNumberOrPlaceholder.test(maskValueAtCurPos); + var continueMovingPosition = curPosOnSep && posShorterThanMaskLength && posGreaterThanZero + + // if we hit a real char, stay where we are + if (!continueMovingPosition) break; + + // hitting backspace in a selection, you can possibly go back any further - go forward + pos += (key === BACKSPACE && !hasSel) ? -1 : 1; + + } + + + if (hasSel) { + // pos might have moved so re-calc length + var selLength = selEnd - pos + + // if we have a selection length we will wipe out entire selection and replace with default template for that range + var defaultBlank = options.mask.replace(/[0-9]/g, '_'); + var defaultBlankSelectionReplacement = defaultBlank.substr(pos, selLength); + var selReplacementRemainder = defaultBlankSelectionReplacement.substr(1) // might be empty + + var valueBeforeSel = val.substr(0, pos); + var insertChars = digit + selReplacementRemainder; + var charsAfterSelection = val.substr(pos + selLength); + + val = valueBeforeSel + insertChars + charsAfterSelection + + } else { + var valueBeforeCursor = val.substr(0, pos); + var insertChar = digit; + var valueAfterNextChar = val.substr(pos + 1); + + val = valueBeforeCursor + insertChar + valueAfterNextChar + } + + if ($.trim(val) === '') { + // if empty, set to default + val = defaultBlank + } else { + // if at the last character don't need to do anything + if (pos === options.mask.length) { + event.preventDefault(); + return false; + } + } + + // resume cursor location + pos += (key === BACKSPACE) ? 0 : 1; + // don't stop on a separator, continue whatever direction you were going + while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) { + pos += (key === BACKSPACE) ? 0 : 1; + } + + if (isValidValue(options.mask, val)) { + this.value = val; + setCaretPos(this, pos); + } else if ($.trim(val) === '') { + this.value = options.mask.replace(/[0-9]/g, '_'); + } else { + input.trigger('error_input.xdsoft'); + } + } else { + if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) { + return true; + } + } + + event.preventDefault(); + return false; + }); + } + } + _xdsoft_datetime.setCurrentTime(getCurrentValue()); input .data('xdsoft_datetimepicker', datetimepicker) - .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) { - if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { + .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function () { + if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) { + return; + } + if (!options.openOnFocus) { return; } clearTimeout(timer); timer = setTimeout(function () { - if (input.is(':disabled') || input.is(':hidden') || !input.is(':visible')) { + if (input.is(':disabled')) { return; } triggerAfterOpen = true; - _xdsoft_datetime.setCurrentTime(getCurrentValue()); - + _xdsoft_datetime.setCurrentTime(getCurrentValue(), true); + if(options.mask) { + setMask(options); + } datetimepicker.trigger('open.xdsoft'); }, 100); }) .on('keydown.xdsoft', function (event) { - var val = this.value, elementSelector, + var elementSelector, key = event.which; - if ([ENTER].indexOf(key) !== -1) { - elementSelector = $("input:visible,textarea:visible"); + if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) { + elementSelector = $("input:visible,textarea:visible,button:visible,a:visible"); datetimepicker.trigger('close.xdsoft'); elementSelector.eq(elementSelector.index(this) + 1).focus(); return false; @@ -1544,6 +2555,9 @@ datetimepicker.trigger('close.xdsoft'); return true; } + }) + .on('blur.xdsoft', function () { + datetimepicker.trigger('close.xdsoft'); }); }; destroyDateTimePicker = function (input) { @@ -1554,14 +2568,14 @@ input .data('xdsoft_datetimepicker', null) .off('.xdsoft'); - $(window).off('resize.xdsoft'); - $([window, document.body]).off('mousedown.xdsoft'); + $(options.contentWindow).off('resize.xdsoft'); + $([options.contentWindow, options.ownerDocument.body]).off('mousedown.xdsoft touchstart'); if (input.unmousewheel) { input.unmousewheel(); } } }; - $(document) + $(options.ownerDocument) .off('keydown.xdsoftctrl keyup.xdsoftctrl') .on('keydown.xdsoftctrl', function (e) { if (e.keyCode === CTRLKEY) { @@ -1573,8 +2587,9 @@ ctrlDown = false; } }); - return this.each(function () { - var datetimepicker = $(this).data('xdsoft_datetimepicker'); + + this.each(function () { + var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input; if (datetimepicker) { if ($.type(opt) === 'string') { switch (opt) { @@ -1585,16 +2600,27 @@ case 'hide': datetimepicker.trigger('close.xdsoft'); break; + case 'toggle': + datetimepicker.trigger('toggle.xdsoft'); + break; case 'destroy': destroyDateTimePicker($(this)); break; case 'reset': this.value = this.defaultValue; - if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) { + if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(dateHelper.parseDate(this.value, options.format))) { datetimepicker.data('changed', false); } datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value); break; + case 'validate': + $input = datetimepicker.data('input'); + $input.trigger('blur.xdsoft'); + break; + default: + if (datetimepicker[opt] && $.isFunction(datetimepicker[opt])) { + result = datetimepicker[opt](opt2); + } } } else { datetimepicker @@ -1610,33 +2636,251 @@ } } }); + + return result; }; + $.fn.datetimepicker.defaults = default_options; -}(jQuery)); -(function () { - - /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) - * Licensed under the MIT License (LICENSE.txt). - * - * Version: 3.1.12 - * - * Requires: jQuery 1.2.2+ - */ - !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); - -// Parse and Format Library -//http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/ - /* - * Copyright (C) 2004 Baron Schwartz - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, version 2.1. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var code="Date.prototype."+funcName+" = function() {return ";var special=false;var ch="";for(var i=0;i 0) {";var regex="";var special=false;var ch="";for(var i=0;i 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$");eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b= 9 ) ? + ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'], + slice = Array.prototype.slice, + nullLowestDeltaTimeout, lowestDelta; + + if ( $.event.fixHooks ) { + for ( var i = toFix.length; i; ) { + $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks; + } + } + + var special = $.event.special.mousewheel = { + version: '3.1.12', + + setup: function() { + if ( this.addEventListener ) { + for ( var i = toBind.length; i; ) { + this.addEventListener( toBind[--i], handler, false ); + } + } else { + this.onmousewheel = handler; + } + // Store the line height and page height for this particular element + $.data(this, 'mousewheel-line-height', special.getLineHeight(this)); + $.data(this, 'mousewheel-page-height', special.getPageHeight(this)); + }, + + teardown: function() { + if ( this.removeEventListener ) { + for ( var i = toBind.length; i; ) { + this.removeEventListener( toBind[--i], handler, false ); + } + } else { + this.onmousewheel = null; + } + // Clean up the data we added to the element + $.removeData(this, 'mousewheel-line-height'); + $.removeData(this, 'mousewheel-page-height'); + }, + + getLineHeight: function(elem) { + var $elem = $(elem), + $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent'](); + if (!$parent.length) { + $parent = $('body'); + } + return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16; + }, + + getPageHeight: function(elem) { + return $(elem).height(); + }, + + settings: { + adjustOldDeltas: true, // see shouldAdjustOldDeltas() below + normalizeOffset: true // calls getBoundingClientRect for each event + } + }; + + $.fn.extend({ + mousewheel: function(fn) { + return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel'); + }, + + unmousewheel: function(fn) { + return this.unbind('mousewheel', fn); + } + }); + + + function handler(event) { + var orgEvent = event || window.event, + args = slice.call(arguments, 1), + delta = 0, + deltaX = 0, + deltaY = 0, + absDelta = 0, + offsetX = 0, + offsetY = 0; + event = $.event.fix(orgEvent); + event.type = 'mousewheel'; + + // Old school scrollwheel delta + if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; } + if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; } + if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; } + if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; } + + // Firefox < 17 horizontal scrolling related to DOMMouseScroll event + if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { + deltaX = deltaY * -1; + deltaY = 0; + } + + // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy + delta = deltaY === 0 ? deltaX : deltaY; + + // New school wheel delta (wheel event) + if ( 'deltaY' in orgEvent ) { + deltaY = orgEvent.deltaY * -1; + delta = deltaY; + } + if ( 'deltaX' in orgEvent ) { + deltaX = orgEvent.deltaX; + if ( deltaY === 0 ) { delta = deltaX * -1; } + } + + // No change actually happened, no reason to go any further + if ( deltaY === 0 && deltaX === 0 ) { return; } + + // Need to convert lines and pages to pixels if we aren't already in pixels + // There are three delta modes: + // * deltaMode 0 is by pixels, nothing to do + // * deltaMode 1 is by lines + // * deltaMode 2 is by pages + if ( orgEvent.deltaMode === 1 ) { + var lineHeight = $.data(this, 'mousewheel-line-height'); + delta *= lineHeight; + deltaY *= lineHeight; + deltaX *= lineHeight; + } else if ( orgEvent.deltaMode === 2 ) { + var pageHeight = $.data(this, 'mousewheel-page-height'); + delta *= pageHeight; + deltaY *= pageHeight; + deltaX *= pageHeight; + } + + // Store lowest absolute delta to normalize the delta values + absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) ); + + if ( !lowestDelta || absDelta < lowestDelta ) { + lowestDelta = absDelta; + + // Adjust older deltas if necessary + if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) { + lowestDelta /= 40; + } + } + + // Adjust older deltas if necessary + if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) { + // Divide all the things by 40! + delta /= 40; + deltaX /= 40; + deltaY /= 40; + } + + // Get a whole, normalized value for the deltas + delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta); + deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta); + deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta); + + // Normalise offsetX and offsetY properties + if ( special.settings.normalizeOffset && this.getBoundingClientRect ) { + var boundingRect = this.getBoundingClientRect(); + offsetX = event.clientX - boundingRect.left; + offsetY = event.clientY - boundingRect.top; + } + + // Add information to the event object + event.deltaX = deltaX; + event.deltaY = deltaY; + event.deltaFactor = lowestDelta; + event.offsetX = offsetX; + event.offsetY = offsetY; + // Go ahead and set deltaMode to 0 since we converted to pixels + // Although this is a little odd since we overwrite the deltaX/Y + // properties with normalized deltas. + event.deltaMode = 0; + + // Add event and delta to the front of the arguments + args.unshift(event, delta, deltaX, deltaY); + + // Clearout lowestDelta after sometime to better + // handle multiple device types that give different + // a different lowestDelta + // Ex: trackpad = 3 and mouse wheel = 120 + if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); } + nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200); + + return ($.event.dispatch || $.event.handle).apply(this, args); + } + + function nullLowestDelta() { + lowestDelta = null; + } + + function shouldAdjustOldDeltas(orgEvent, absDelta) { + // If this is an older event and the delta is divisable by 120, + // then we are assuming that the browser is treating this as an + // older mouse wheel event and that we should divide the deltas + // by 40 to try and get a more usable deltaFactor. + // Side note, this actually impacts the reported scroll distance + // in older browsers and can cause scrolling to be slower than native. + // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false. + return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0; + } + +})); From 91e9369d8689890c5cd44444bf36a45d46ff7e15 Mon Sep 17 00:00:00 2001 From: heathdutton Date: Thu, 29 Nov 2018 15:25:28 -0500 Subject: [PATCH 050/240] Apply delay to mousewheel. From a suggestion by https://github.com/wagtail/wagtail/commit/87a7a798c9a4fd9d0036e16c55546c 40cf53700d. --- app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js index a89ce23b08e..d66c69d240f 100644 --- a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js +++ b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js @@ -904,7 +904,7 @@ var datetimepickerFactory = function ($) { timeboxparent.on('mousewheel', function (event) { var top = Math.abs(parseInt(timebox.css('marginTop'), 10)); - top = top - (event.deltaY * 20); + top = top - (event.deltaY * 1); if (top < 0) { top = 0; } From 991b056708cad2d47e1f308effe0ddbc0eebb84a Mon Sep 17 00:00:00 2001 From: heathdutton Date: Fri, 30 Nov 2018 08:47:16 -0500 Subject: [PATCH 051/240] Revert "Apply delay to mousewheel." This reverts commit 91e9369d8689890c5cd44444bf36a45d46ff7e15. --- app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js index d66c69d240f..a89ce23b08e 100644 --- a/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js +++ b/app/bundles/CoreBundle/Assets/js/libraries/7.datetimepicker.js @@ -904,7 +904,7 @@ var datetimepickerFactory = function ($) { timeboxparent.on('mousewheel', function (event) { var top = Math.abs(parseInt(timebox.css('marginTop'), 10)); - top = top - (event.deltaY * 1); + top = top - (event.deltaY * 20); if (top < 0) { top = 0; } From 7fcde71b26128fedc784169d944abe3c77fe0108 Mon Sep 17 00:00:00 2001 From: heathdutton Date: Fri, 30 Nov 2018 08:50:08 -0500 Subject: [PATCH 052/240] Add scroll false options to prevent mouse scroll from shifting dates. Suggestion by @scottshipman --- app/bundles/CoreBundle/Assets/js/1a.content.js | 3 +++ app/bundles/CoreBundle/Assets/js/7.charts.js | 8 ++++++-- app/bundles/EmailBundle/Assets/js/email.js | 3 +++ app/bundles/LeadBundle/Assets/js/lead.js | 3 +++ .../ReportBundle/Views/Report/details_data.html.php | 8 ++++++-- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/bundles/CoreBundle/Assets/js/1a.content.js b/app/bundles/CoreBundle/Assets/js/1a.content.js index d8bd5e4d561..717870c9711 100644 --- a/app/bundles/CoreBundle/Assets/js/1a.content.js +++ b/app/bundles/CoreBundle/Assets/js/1a.content.js @@ -1221,6 +1221,7 @@ Mautic.activateDateTimeInputs = function(el, type) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } else if(type == 'date') { @@ -1230,6 +1231,7 @@ Mautic.activateDateTimeInputs = function(el, type) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false, closeOnDateSelect: true }); @@ -1240,6 +1242,7 @@ Mautic.activateDateTimeInputs = function(el, type) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } diff --git a/app/bundles/CoreBundle/Assets/js/7.charts.js b/app/bundles/CoreBundle/Assets/js/7.charts.js index 37e09ceb0b2..bc6408d271b 100644 --- a/app/bundles/CoreBundle/Assets/js/7.charts.js +++ b/app/bundles/CoreBundle/Assets/js/7.charts.js @@ -359,7 +359,9 @@ Mautic.initDateRangePicker = function (fromId, toId) { maxDate: dateTo.val() ? new Date(dateTo.val()) : false }); }, - timepicker: false + timepicker: false, + scrollMonth: false, + scrollInput: false }); dateTo.datetimepicker({ @@ -370,7 +372,9 @@ Mautic.initDateRangePicker = function (fromId, toId) { minDate: dateFrom.val() ? new Date(dateFrom.val()) : false }); }, - timepicker: false + timepicker: false, + scrollMonth: false, + scrollInput: false }); } }; diff --git a/app/bundles/EmailBundle/Assets/js/email.js b/app/bundles/EmailBundle/Assets/js/email.js index 0e4a1b57821..1264034042c 100644 --- a/app/bundles/EmailBundle/Assets/js/email.js +++ b/app/bundles/EmailBundle/Assets/js/email.js @@ -619,6 +619,7 @@ Mautic.addDynamicContentFilter = function (selectedFilter, jQueryVariant) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } else if (fieldType == 'date') { @@ -628,6 +629,7 @@ Mautic.addDynamicContentFilter = function (selectedFilter, jQueryVariant) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false, closeOnDateSelect: true }); @@ -638,6 +640,7 @@ Mautic.addDynamicContentFilter = function (selectedFilter, jQueryVariant) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } else if (fieldType == 'lookup_id') { diff --git a/app/bundles/LeadBundle/Assets/js/lead.js b/app/bundles/LeadBundle/Assets/js/lead.js index 6ed6a89a2c5..aab73097719 100644 --- a/app/bundles/LeadBundle/Assets/js/lead.js +++ b/app/bundles/LeadBundle/Assets/js/lead.js @@ -609,6 +609,7 @@ Mautic.addLeadListFilter = function (elId, elObj) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } else if (fieldType == 'date') { @@ -618,6 +619,7 @@ Mautic.addLeadListFilter = function (elId, elObj) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false, closeOnDateSelect: true }); @@ -628,6 +630,7 @@ Mautic.addLeadListFilter = function (elId, elObj) { lazyInit: true, validateOnBlur: false, allowBlank: true, + scrollMonth: false, scrollInput: false }); } else if (fieldType == 'lookup_id') { diff --git a/app/bundles/ReportBundle/Views/Report/details_data.html.php b/app/bundles/ReportBundle/Views/Report/details_data.html.php index 29a7c942ad6..bd54756ca14 100644 --- a/app/bundles/ReportBundle/Views/Report/details_data.html.php +++ b/app/bundles/ReportBundle/Views/Report/details_data.html.php @@ -213,14 +213,18 @@ function getTotal($a, $f, $t, $allrows, $ac) mQuery('.datetimepicker').datetimepicker({ format:'Y-m-d H:i:s', closeOnDateSelect: true, - validateOnBlur: false + validateOnBlur: false, + scrollMonth: false, + scrollInput: false }); }); mQuery(document).ready(function() { mQuery('.datepicker').datetimepicker({ format:'Y-m-d', closeOnDateSelect: true, - validateOnBlur: false + validateOnBlur: false, + scrollMonth: false, + scrollInput: false }); }); From b44faa6f2a9e375041dafe09c29e8fa5e52dab94 Mon Sep 17 00:00:00 2001 From: atbrowne Date: Fri, 30 Nov 2018 15:13:49 -0500 Subject: [PATCH 053/240] Prevent lead fields from being overwritten by empty values when editing via REST API. --- .../Api/CustomFieldsApiControllerTrait.php | 12 ++++++------ .../LeadBundle/Controller/Api/LeadApiController.php | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/bundles/LeadBundle/Controller/Api/CustomFieldsApiControllerTrait.php b/app/bundles/LeadBundle/Controller/Api/CustomFieldsApiControllerTrait.php index a9169fa4f5e..6a55aff3448 100644 --- a/app/bundles/LeadBundle/Controller/Api/CustomFieldsApiControllerTrait.php +++ b/app/bundles/LeadBundle/Controller/Api/CustomFieldsApiControllerTrait.php @@ -105,9 +105,9 @@ protected function getEntityFormOptions() * @param Lead|Company $entity * @param Form $form * @param array $parameters - * @param bool $isPost + * @param bool $isPostOrPatch */ - protected function setCustomFieldValues($entity, $form, $parameters, $isPost = false) + protected function setCustomFieldValues($entity, $form, $parameters, $isPostOrPatch = false) { //set the custom field values //pull the data from the form in order to apply the form's formatting @@ -115,14 +115,14 @@ protected function setCustomFieldValues($entity, $form, $parameters, $isPost = f $parameters[$f->getName()] = $f->getData(); } - if ($isPost) { + if ($isPostOrPatch) { // Don't overwrite the contacts accumulated points if (isset($parameters['points']) && empty($parameters['points'])) { unset($parameters['points']); } - // When merging a contact because of a unique identifier match in POST /api/contacts//new, all 0 values must be unset because - // we have to assume 0 was not meant to overwrite an existing value. Other empty values will be caught by LeadModel::setCustomFieldValues + // When merging a contact because of a unique identifier match in POST /api/contacts//new or PATCH /api/contacts//edit all 0 values must be unset because + // we have to assume 0 was not meant to overwrite an existing value. Other empty values will be caught by LeadModel::setFieldValues $parameters = array_filter( $parameters, function ($value) { @@ -135,6 +135,6 @@ function ($value) { ); } - $this->model->setFieldValues($entity, $parameters, !$isPost); + $this->model->setFieldValues($entity, $parameters, !$isPostOrPatch); } } diff --git a/app/bundles/LeadBundle/Controller/Api/LeadApiController.php b/app/bundles/LeadBundle/Controller/Api/LeadApiController.php index b27fd2d28a4..12f226acd5a 100644 --- a/app/bundles/LeadBundle/Controller/Api/LeadApiController.php +++ b/app/bundles/LeadBundle/Controller/Api/LeadApiController.php @@ -679,7 +679,8 @@ protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit') unset($parameters['frequencyRules']); } - $this->setCustomFieldValues($entity, $form, $parameters, 'POST' === $this->request->getMethod()); + $isPostOrPatch = 'POST' === $this->request->getMethod() || 'PATCH' === $this->request->getMethod(); + $this->setCustomFieldValues($entity, $form, $parameters, $isPostOrPatch); } /** From cd72395cc6f1016e03a5dd38fcf646bfcd3ef189 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 8 Dec 2018 11:29:34 -0500 Subject: [PATCH 054/240] Fix default avatar URL for non-Apache servers The UrlHelper::rel2abs function generated incorrect absolute URLs for many server configurations. This PR changes the algorithm to be more generic, producing an absolute URL that more closely matches what the browser is using. --- app/bundles/CoreBundle/Helper/UrlHelper.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/bundles/CoreBundle/Helper/UrlHelper.php b/app/bundles/CoreBundle/Helper/UrlHelper.php index 6b5fffb05dd..80bb3da7360 100644 --- a/app/bundles/CoreBundle/Helper/UrlHelper.php +++ b/app/bundles/CoreBundle/Helper/UrlHelper.php @@ -89,16 +89,14 @@ public static function rel2abs($rel) { $path = $host = $scheme = ''; - $base = 'http'; - if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { - $base .= 's'; - } - $base .= '://'; - if ($_SERVER['SERVER_PORT'] != '80') { - $base .= $_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI']; - } else { - $base .= $_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']; - } + $ssl = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'; + $scheme = strtolower($_SERVER['SERVER_PROTOCOL']); + $scheme = substr($scheme, 0, strpos($scheme, '/')).($ssl ? 's' : ''); + $port = $_SERVER['SERVER_PORT']; + $port = ((!$ssl && $port == '80') || ($ssl && $port == '443')) ? '' : ":$port"; + $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null; + $host = isset($host) ? $host : $_SERVER['SERVER_NAME'].$port; + $base = "$scheme://$host".$_SERVER['REQUEST_URI']; $base = str_replace('/index_dev.php', '', $base); $base = str_replace('/index.php', '', $base); From 9b10ea889ea099e1650ba92514169ca94d6fe223 Mon Sep 17 00:00:00 2001 From: John Linhart Date: Tue, 27 Nov 2018 11:10:19 +0100 Subject: [PATCH 055/240] Catch false value from CacheStorageHelper as it means it doesn't exist and log it --- .../CoreBundle/Helper/CacheStorageHelper.php | 8 ++++ app/bundles/EmailBundle/Config/config.php | 1 + .../EventListener/MomentumSubscriber.php | 20 ++++++-- .../Helper/RequestStorageHelper.php | 10 +++- .../EventListener/MomentumSubscriberTest.php | 47 ++++++++++++++++++- .../Tests/Helper/RequestStorageHelperTest.php | 14 ++++++ 6 files changed, 93 insertions(+), 7 deletions(-) diff --git a/app/bundles/CoreBundle/Helper/CacheStorageHelper.php b/app/bundles/CoreBundle/Helper/CacheStorageHelper.php index dacd47893c9..0486ca31e5c 100644 --- a/app/bundles/CoreBundle/Helper/CacheStorageHelper.php +++ b/app/bundles/CoreBundle/Helper/CacheStorageHelper.php @@ -101,6 +101,14 @@ public function __construct($adaptor, $namespace = null, Connection $connection $this->setCacheAdaptor(); } + /** + * @return string|false + */ + public function getAdaptorClassName() + { + return get_class($this->cacheAdaptor); + } + /** * @param $name * @param $data diff --git a/app/bundles/EmailBundle/Config/config.php b/app/bundles/EmailBundle/Config/config.php index afa09ec3097..779b835f5f7 100644 --- a/app/bundles/EmailBundle/Config/config.php +++ b/app/bundles/EmailBundle/Config/config.php @@ -127,6 +127,7 @@ 'mautic.transport.momentum.callback', 'mautic.queue.service', 'mautic.email.helper.request.storage', + 'monolog.logger.mautic', ], ], 'mautic.email.monitored.bounce.subscriber' => [ diff --git a/app/bundles/EmailBundle/EventListener/MomentumSubscriber.php b/app/bundles/EmailBundle/EventListener/MomentumSubscriber.php index fb5f065903f..e06d2efd630 100644 --- a/app/bundles/EmailBundle/EventListener/MomentumSubscriber.php +++ b/app/bundles/EmailBundle/EventListener/MomentumSubscriber.php @@ -22,6 +22,7 @@ use Mautic\QueueBundle\Queue\QueueName; use Mautic\QueueBundle\Queue\QueueService; use Mautic\QueueBundle\QueueEvents; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -48,15 +49,18 @@ class MomentumSubscriber extends CommonSubscriber * @param MomentumCallbackInterface $momentumCallback * @param QueueService $queueService * @param RequestStorageHelper $requestStorageHelper + * @param LoggerInterface $logger */ public function __construct( MomentumCallbackInterface $momentumCallback, QueueService $queueService, - RequestStorageHelper $requestStorageHelper + RequestStorageHelper $requestStorageHelper, + LoggerInterface $logger ) { $this->momentumCallback = $momentumCallback; $this->queueService = $queueService; $this->requestStorageHelper = $requestStorageHelper; + $this->logger = $logger; } /** @@ -80,9 +84,15 @@ public function onMomentumWebhookQueueProcessing(QueueConsumerEvent $event) if ($event->checkTransport(MomentumTransport::class)) { $payload = $event->getPayload(); $key = $payload['key']; - $request = $this->requestStorageHelper->getRequest($key); - $this->momentumCallback->processCallbackRequest($request); - $this->requestStorageHelper->deleteCachedRequest($key); + + try { + $request = $this->requestStorageHelper->getRequest($key); + $this->momentumCallback->processCallbackRequest($request); + $this->requestStorageHelper->deleteCachedRequest($key); + } catch (\UnexpectedValueException $e) { + $this->logger->error($e->getMessage()); + } + $event->setResult(QueueConsumerResults::ACKNOWLEDGE); } } @@ -96,7 +106,7 @@ public function onMomentumWebhookRequest(TransportWebhookEvent $event) if ($this->queueService->isQueueEnabled() && $event->transportIsInstanceOf($transport)) { // Beanstalk jobs are limited to 65,535 kB. Momentum can send up to 10.000 items per request. // One item has about 1,6 kB. Lets store the request to the cache storage instead of the job itself. - $key = $this->requestStorageHelper->storeRequest($transport, $event->getRequest()); + $key = $this->requestStorageHelper->storeRequest($transport, $event->getRequest()); $this->queueService->publishToQueue(QueueName::TRANSPORT_WEBHOOK, ['transport' => $transport, 'key' => $key]); $event->stopPropagation(); } diff --git a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php index 58c366c4d6c..3f768283afa 100644 --- a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php +++ b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php @@ -60,10 +60,18 @@ public function storeRequest($transportName, Request $request) * @param string $key * * @return Request + * + * @throws \UnexpectedValueException */ public function getRequest($key) { - return new Request([], $this->cacheStorage->get($key)); + $cachedRequest = $this->cacheStorage->get($key); + + if (false === $cachedRequest) { + throw new \UnexpectedValueException("Request with key '{$key}' was not found in the cache store '{$this->cacheStorage->getAdaptorClassName()}'."); + } + + return new Request([], $cachedRequest); } /** diff --git a/app/bundles/EmailBundle/Tests/EventListener/MomentumSubscriberTest.php b/app/bundles/EmailBundle/Tests/EventListener/MomentumSubscriberTest.php index 297b2c282a5..c3a9b8c3b8a 100644 --- a/app/bundles/EmailBundle/Tests/EventListener/MomentumSubscriberTest.php +++ b/app/bundles/EmailBundle/Tests/EventListener/MomentumSubscriberTest.php @@ -20,12 +20,16 @@ use Mautic\QueueBundle\Queue\QueueConsumerResults; use Mautic\QueueBundle\Queue\QueueName; use Mautic\QueueBundle\Queue\QueueService; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; class MomentumSubscriberTest extends \PHPUnit_Framework_TestCase { private $queueServiceMock; private $momentumCallbackMock; + private $requestStorageHelperMock; + private $loggerMock; + private $momentumSubscriber; protected function setUp() { @@ -34,7 +38,13 @@ protected function setUp() $this->momentumCallbackMock = $this->createMock(MomentumCallbackInterface::class); $this->queueServiceMock = $this->createMock(QueueService::class); $this->requestStorageHelperMock = $this->createMock(RequestStorageHelper::class); - $this->momentumSubscriber = new MomentumSubscriber($this->momentumCallbackMock, $this->queueServiceMock, $this->requestStorageHelperMock); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->momentumSubscriber = new MomentumSubscriber( + $this->momentumCallbackMock, + $this->queueServiceMock, + $this->requestStorageHelperMock, + $this->loggerMock + ); } public function testOnMomentumWebhookQueueProcessingForNonMomentumTransport() @@ -95,6 +105,41 @@ public function testOnMomentumWebhookQueueProcessingForMomentumTransport() $this->momentumSubscriber->onMomentumWebhookQueueProcessing($queueConsumerEvent); } + public function testOnMomentumWebhookQueueProcessingForMomentumTransportIfRequestNotFounc() + { + $queueConsumerEvent = $this->createMock(QueueConsumerEvent::class); + + $queueConsumerEvent->expects($this->once()) + ->method('getPayload') + ->willReturn([ + 'transport' => MomentumTransport::class, + 'key' => 'value', + ]); + + $queueConsumerEvent->expects($this->once()) + ->method('checkTransport') + ->with(MomentumTransport::class) + ->willReturn(true); + + $this->requestStorageHelperMock->expects($this->once()) + ->method('getRequest') + ->with('value') + ->will($this->throwException(new \UnexpectedValueException('Error message'))); + + $this->momentumCallbackMock->expects($this->never()) + ->method('processCallbackRequest'); + + $this->loggerMock->expects($this->once()) + ->method('error') + ->with('Error message'); + + $queueConsumerEvent->expects($this->once()) + ->method('setResult') + ->with(QueueConsumerResults::ACKNOWLEDGE); + + $this->momentumSubscriber->onMomentumWebhookQueueProcessing($queueConsumerEvent); + } + public function testOnMomentumWebhookRequestWhenQueueIsDisabled() { $transportWebhookEvent = $this->createMock(TransportWebhookEvent::class); diff --git a/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php b/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php index b5d8657b895..b9603235235 100644 --- a/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php +++ b/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php @@ -72,6 +72,20 @@ public function testGetRequest() $this->assertEquals($payload, $request->request->all()); } + public function testGetRequestIfNotFound() + { + $payload = ['some' => 'values']; + $key = MomentumTransport::class.';webhook_request;5b43832134cfb0.36545510'; + + $this->cacheStorageMock->expects($this->once()) + ->method('get') + ->with($key) + ->willReturn(false); + + $this->expectException(\UnexpectedValueException::class); + $this->helper->getRequest($key); + } + public function testGetTransportNameFromKey() { $this->assertEquals(MomentumTransport::class, $this->helper->getTransportNameFromKey('Mautic\EmailBundle\Swiftmailer\Transport\MomentumTransport:webhook_request:5b43832134cfb0.36545510')); From c85a7f43ebc10207688f8cad7cf0db798b1b545a Mon Sep 17 00:00:00 2001 From: John Linhart Date: Fri, 30 Nov 2018 12:23:57 +0100 Subject: [PATCH 056/240] Fix for getting email transport form the cache key with default prefix --- .../EmailBundle/Helper/RequestStorageHelper.php | 11 ++++++++++- .../Tests/Helper/RequestStorageHelperTest.php | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php index 3f768283afa..2c9169ba8c3 100644 --- a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php +++ b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php @@ -91,7 +91,16 @@ public function deleteCachedRequest($key) */ public function getTransportNameFromKey($key) { - list($transportName) = explode(self::KEY_SEPARATOR, $key); + // Remove the default cache key prefix if set. + if (strpos($key, ':') !== false) { + list($prefix, $key) = explode(':', $key); + } + + // Take the part before the key separator as the serialized transpot name. + list($serializedTransportName) = explode(self::KEY_SEPARATOR, $key); + + // Unserialize transport name to the standard full class name. + $transportName = str_replace('|', '\\', $serializedTransportName); return $transportName; } diff --git a/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php b/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php index b9603235235..3e6f84a62a4 100644 --- a/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php +++ b/app/bundles/EmailBundle/Tests/Helper/RequestStorageHelperTest.php @@ -90,4 +90,12 @@ public function testGetTransportNameFromKey() { $this->assertEquals(MomentumTransport::class, $this->helper->getTransportNameFromKey('Mautic\EmailBundle\Swiftmailer\Transport\MomentumTransport:webhook_request:5b43832134cfb0.36545510')); } + + /** + * The StorageHelper will add '%mautic.db_table_prefix%' as a prefix to each cache key. + */ + public function testGetTransportNameFromKeyWithGlobalPrefix() + { + $this->assertEquals(MomentumTransport::class, $this->helper->getTransportNameFromKey('mautic:Mautic|EmailBundle|Swiftmailer|Transport|MomentumTransport;webhook_request;5bfbe8ce671198.00044461')); + } } From 729c9ecaac6ab4e0c62182abc450938614624ef7 Mon Sep 17 00:00:00 2001 From: John Linhart Date: Fri, 30 Nov 2018 13:43:57 +0100 Subject: [PATCH 057/240] Remove the cache prefix when getting and removing the cache item too --- .../Helper/RequestStorageHelper.php | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php index 2c9169ba8c3..6e1a4c6fba5 100644 --- a/app/bundles/EmailBundle/Helper/RequestStorageHelper.php +++ b/app/bundles/EmailBundle/Helper/RequestStorageHelper.php @@ -65,6 +65,7 @@ public function storeRequest($transportName, Request $request) */ public function getRequest($key) { + $key = $this->removeCachePrefix($key); $cachedRequest = $this->cacheStorage->get($key); if (false === $cachedRequest) { @@ -79,6 +80,8 @@ public function getRequest($key) */ public function deleteCachedRequest($key) { + $key = $this->removeCachePrefix($key); + $this->cacheStorage->delete($key); } @@ -91,10 +94,7 @@ public function deleteCachedRequest($key) */ public function getTransportNameFromKey($key) { - // Remove the default cache key prefix if set. - if (strpos($key, ':') !== false) { - list($prefix, $key) = explode(':', $key); - } + $key = $this->removeCachePrefix($key); // Take the part before the key separator as the serialized transpot name. list($serializedTransportName) = explode(self::KEY_SEPARATOR, $key); @@ -105,6 +105,22 @@ public function getTransportNameFromKey($key) return $transportName; } + /** + * Remove the default cache key prefix if set. + * + * @param string $key + * + * @return string + */ + private function removeCachePrefix($key) + { + if (strpos($key, ':') !== false) { + list($prefix, $key) = explode(':', $key); + } + + return $key; + } + /** * Generates unique hash in format $transportName:webhook_request:unique.hash. * From 5be51774728f6573a8c579adba90ebcfa7c2097d Mon Sep 17 00:00:00 2001 From: Lukas Drahy Date: Wed, 19 Dec 2018 10:17:19 +0100 Subject: [PATCH 058/240] Validate multiselect input field on API to maxlen 255 --- .../LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php index 8327b92cb8f..3b091063a78 100644 --- a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php +++ b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php @@ -169,6 +169,10 @@ function (FormEvent $event) use ($alias, $type) { case 'select': case 'multiselect': case 'boolean': + if ($type == 'multiselect') { + $constraints[] = new Length(['max' => 255]); + } + $typeProperties = [ 'required' => $required, 'label' => $field['label'], @@ -268,6 +272,10 @@ function (FormEvent $event) use ($alias, $type) { ] ); break; + case 'multiselect': + if ($type == 'multiselect') { + $constraints[] = new Length(['max' => 255]); + } } $builder->add( From 02a781dabacae681199d1a3eb3ae9c6812e7d0a4 Mon Sep 17 00:00:00 2001 From: Lukas Drahy Date: Tue, 15 Jan 2019 11:15:52 +0100 Subject: [PATCH 059/240] Use Length validator class --- app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php index 3b091063a78..4e3b19d89bf 100644 --- a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php +++ b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\Validator\Constraints\Date; use Symfony\Component\Validator\Constraints\Email; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; trait EntityFieldsBuildFormTrait From a15dcca6f7978d30d778cc7a97ec66db35fa1cc2 Mon Sep 17 00:00:00 2001 From: Lukas Drahy Date: Tue, 15 Jan 2019 11:16:23 +0100 Subject: [PATCH 060/240] Remove unused Date validator class --- app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php index 4e3b19d89bf..f2f38291c6f 100644 --- a/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php +++ b/app/bundles/LeadBundle/Form/Type/EntityFieldsBuildFormTrait.php @@ -16,7 +16,6 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints\Date; use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; From 453c0c518ff4e63ab4a14699a5bdbb72d05f4af5 Mon Sep 17 00:00:00 2001 From: mitresh95 Date: Thu, 23 Aug 2018 16:11:29 -0400 Subject: [PATCH 061/240] Resolving merge conflict while cherry-pick. --- .../LeadBundle/Controller/ListController.php | 17 ++- app/bundles/LeadBundle/Model/ListModel.php | 95 ++++++++++++-- .../Tests/Model/LeadListModelTest.php | 121 ++++++++++++++++++ 3 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 app/bundles/LeadBundle/Tests/Model/LeadListModelTest.php diff --git a/app/bundles/LeadBundle/Controller/ListController.php b/app/bundles/LeadBundle/Controller/ListController.php index 7cdf19cd711..f1a5de1f62b 100644 --- a/app/bundles/LeadBundle/Controller/ListController.php +++ b/app/bundles/LeadBundle/Controller/ListController.php @@ -531,9 +531,20 @@ public function batchDeleteAction() if ($this->request->getMethod() == 'POST') { /** @var ListModel $model */ - $model = $this->getModel('lead.list'); - $ids = json_decode($this->request->query->get('ids', '{}')); - $deleteIds = []; + $model = $this->getModel('lead.list'); + $ids = json_decode($this->request->query->get('ids', '{}')); + $canNotBeDeleted = $model->canNotBeDeleted($ids); + + if (!empty($canNotBeDeleted)) { + $flashes[] = [ + 'type' => 'error', + 'msg' => 'mautic.lead.list.error.cannot.delete.batch', + 'msgVars' => ['%segments%' => implode(', ', $canNotBeDeleted)], + ]; + } + + $toBeDeleted = array_diff($ids, array_keys($canNotBeDeleted)); + $deleteIds = []; // Loop over the IDs to perform access checks pre-delete foreach ($ids as $objectId) { diff --git a/app/bundles/LeadBundle/Model/ListModel.php b/app/bundles/LeadBundle/Model/ListModel.php index f951bac54a3..f5846726ab7 100644 --- a/app/bundles/LeadBundle/Model/ListModel.php +++ b/app/bundles/LeadBundle/Model/ListModel.php @@ -1715,24 +1715,101 @@ public function getSegmentContactsLineChartData($unit, \DateTime $dateFrom, \Dat } /** - * Is custom field used in at least one defined segment? + * Get segments which are dependent on given segment. * - * @param LeadField $field + * @param int $segmentId * - * @return bool + * @return array */ - public function isFieldUsed(LeadField $field) + public function getSegmentsWithDependenciesOnSegment($segmentId) { - $alias = $field->getAlias(); - $aliasLength = mb_strlen($alias); - $likeContent = "%;s:5:\"field\";s:${aliasLength}:\"{$alias}\";%"; + $page = 1; + $limit = 1000; + $start = 0; + + $filter = [ + 'force' => [ + ['column' => 'l.filters', 'expr' => 'LIKE', 'value'=>'%s:8:"leadlist"%'], + ['column' => 'l.id', 'expr' => 'neq', 'value'=>$segmentId], + ], + ]; + + $entities = $this->getEntities( + [ + 'start' => $start, + 'limit' => $limit, + 'filter' => $filter, + ] + ); + $dependents = []; + foreach ($entities as $entity) { + $retrFilters = $entity->getFilters(); + foreach ($retrFilters as $eachFilter) { + if ($eachFilter['type'] === 'leadlist' && in_array($segmentId, $eachFilter['filter'])) { + $dependents[] = $entity->getName(); + } + } + } + + return $dependents; + } + + /** + * Get segments which are used as a dependent by other segments to prevent batch deletion of them. + * + * @param array $segmentIds + * + * @return array + */ + public function canNotBeDeleted($segmentIds) + { $filter = [ 'force' => [ - ['column' => 'l.filters', 'expr' => 'LIKE', 'value'=> $likeContent], + ['column' => 'l.filters', 'expr' => 'LIKE', 'value'=>'%s:8:"leadlist"%'], ], ]; - return $this->getEntities(['filter' => $filter])->count() !== 0; + $entities = $this->getEntities( + [ + 'filter' => $filter, + ] + ); + + $idsNotToBeDeleted = []; + $namesNotToBeDeleted = []; + $dependency = []; + + foreach ($entities as $entity) { + $retrFilters = $entity->getFilters(); + foreach ($retrFilters as $eachFilter) { + if ($eachFilter['type'] !== 'leadlist') { + continue; + } + + $idsNotToBeDeleted = array_unique(array_merge($idsNotToBeDeleted, $eachFilter['filter'])); + foreach ($eachFilter['filter'] as $val) { + if (!empty($dependency[$val])) { + $dependency[$val] = array_merge($dependency[$val], [$entity->getId()]); + $dependency[$val] = array_unique($dependency[$val]); + } else { + $dependency[$val] = [$entity->getId()]; + } + } + } + } + foreach ($dependency as $key => $value) { + if (array_intersect($value, $segmentIds) === $value) { + $idsNotToBeDeleted = array_unique(array_diff($idsNotToBeDeleted, [$key])); + } + } + + $idsNotToBeDeleted = array_intersect($segmentIds, $idsNotToBeDeleted); + + foreach ($idsNotToBeDeleted as $val) { + $namesNotToBeDeleted[$val] = $this->getEntity($val)->getName(); + } + + return $namesNotToBeDeleted; } } diff --git a/app/bundles/LeadBundle/Tests/Model/LeadListModelTest.php b/app/bundles/LeadBundle/Tests/Model/LeadListModelTest.php new file mode 100644 index 00000000000..8046ca09be2 --- /dev/null +++ b/app/bundles/LeadBundle/Tests/Model/LeadListModelTest.php @@ -0,0 +1,121 @@ +getMockBuilder(ListModel::class) + ->disableOriginalConstructor() + ->setMethods(['getEntities', 'getEntity']) + ->getMock(); + + $mockListModel->expects($this->any()) + ->method('getEntity') + ->willReturnCallback(function ($id) { + $mockEntity = $this->getMockBuilder(LeadList::class) + ->disableOriginalConstructor() + ->setMethods(['getName']) + ->getMock(); + + $mockEntity->expects($this->once()) + ->method('getName') + ->willReturn((string) $id); + + return $mockEntity; + }); + + $filters = 'a:1:{i:0;a:7:{s:4:"glue";s:3:"and";s:5:"field";s:8:"leadlist";s:6:"object";s:4:"lead";s:4:"type";s:8:"leadlist";s:6:"filter";a:2:{i:0;i:1;i:1;i:3;}s:7:"display";N;s:8:"operator";s:2:"in";}}'; + + $filters4 = 'a:1:{i:0;a:7:{s:4:"glue";s:3:"and";s:5:"field";s:8:"leadlist";s:6:"object";s:4:"lead";s:4:"type";s:8:"leadlist";s:6:"filter";a:1:{i:0;i:3;}s:7:"display";N;s:8:"operator";s:2:"in";}}'; + + $mockEntity = $this->getMockBuilder(LeadList::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockEntity1 = clone $mockEntity; + $mockEntity1->expects($this->once()) + ->method('getFilters') + ->willReturn([]); + $mockEntity1->expects($this->any()) + ->method('getId') + ->willReturn(1); + + $mockEntity2 = clone $mockEntity; + $mockEntity2->expects($this->once()) + ->method('getFilters') + ->willReturn(unserialize($filters)); + $mockEntity2->expects($this->any()) + ->method('getId') + ->willReturn(2); + + $mockEntity3 = clone $mockEntity; + $mockEntity3->expects($this->once()) + ->method('getFilters') + ->willReturn([]); + $mockEntity3->expects($this->any()) + ->method('getId') + ->willReturn(3); + + $mockEntity4 = clone $mockEntity; + $mockEntity4->expects($this->once()) + ->method('getFilters') + ->willReturn(unserialize($filters4)); + $mockEntity4->expects($this->any()) + ->method('getId') + ->willReturn(4); + + $mockListModel->expects($this->once()) + ->method('getEntities') + ->willReturn([ + 1 => $mockEntity1, + 2 => $mockEntity2, + 3 => $mockEntity3, + 4 => $mockEntity4, + ]); + + $this->fixture = $mockListModel; + } + + /** + * @dataProvider segmentTestDataProvider + */ + public function testSegmentsCanBeDeletedCorrecty(array $arg, array $expected, $message) + { + $result = $this->fixture->canNotBeDeleted($arg); + + $this->assertEquals($expected, $result, $message); + } + + public function segmentTestDataProvider() + { + return [ + [ + [1], + [1 => '1'], + '2 is dependent on 1, so 1 cannot be deleted.', + ], + [ + [1, 3], + [1 => '1', 3 => '3'], + '2 is dependent on 1 & 3, so 1 & 3 cannot be deleted.', + ], + [ + [1, 2, 3, 4], + [], + 'Since we are deleting all segments, it should not prevent any from being deleted.', + ], + [ + [2], + [], + 'Segments without any other segment dependent on them should always be able to be deleted.', + ], + ]; + } +} From 2ea2034142e4160f3bc554b776a093f9f2ac97c6 Mon Sep 17 00:00:00 2001 From: Mitresh Date: Fri, 16 Nov 2018 12:00:01 -0500 Subject: [PATCH 062/240] Resolving conflicts --- app/bundles/LeadBundle/Model/ListModel.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/bundles/LeadBundle/Model/ListModel.php b/app/bundles/LeadBundle/Model/ListModel.php index f5846726ab7..e08f49b5a67 100644 --- a/app/bundles/LeadBundle/Model/ListModel.php +++ b/app/bundles/LeadBundle/Model/ListModel.php @@ -1714,6 +1714,28 @@ public function getSegmentContactsLineChartData($unit, \DateTime $dateFrom, \Dat return $chart->render(); } + /** + * Is custom field used in at least one defined segment? + * + * @param LeadField $field + * + * @return bool + */ + public function isFieldUsed(LeadField $field) + { + $alias = $field->getAlias(); + $aliasLength = mb_strlen($alias); + $likeContent = "%;s:5:\"field\";s:${aliasLength}:\"{$alias}\";%"; + + $filter = [ + 'force' => [ + ['column' => 'l.filters', 'expr' => 'LIKE', 'value'=> $likeContent], + ], + ]; + + return $this->getEntities(['filter' => $filter])->count() !== 0; + } + /** * Get segments which are dependent on given segment. * From c222948525500cbf9469721cf336ed4d08e30ca5 Mon Sep 17 00:00:00 2001 From: Alan Hartless Date: Tue, 15 Jan 2019 15:08:19 -0500 Subject: [PATCH 063/240] Fixed preventing deletion of dependent segments --- .../LeadBundle/Controller/ListController.php | 20 ++++++++++++++++++- .../LeadBundle/Translations/en_US/flashes.ini | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/bundles/LeadBundle/Controller/ListController.php b/app/bundles/LeadBundle/Controller/ListController.php index f1a5de1f62b..2082455802a 100644 --- a/app/bundles/LeadBundle/Controller/ListController.php +++ b/app/bundles/LeadBundle/Controller/ListController.php @@ -455,6 +455,8 @@ private function getPostActionVars($objectId = null) */ public function deleteAction($objectId) { + /** @var ListModel $model */ + $model = $this->getModel('lead.list'); $page = $this->get('session')->get('mautic.segment.page', 1); $returnUrl = $this->generateUrl('mautic_segment_index', ['page' => $page]); $flashes = []; @@ -469,6 +471,22 @@ public function deleteAction($objectId) ], ]; + $dependents = $model->getSegmentsWithDependenciesOnSegment($objectId); + + if (!empty($dependents)) { + $flashes[] = [ + 'type' => 'error', + 'msg' => 'mautic.lead.list.error.cannot.delete', + 'msgVars' => ['%segments%' => implode(', ', $dependents)], + ]; + + return $this->postActionRedirect( + array_merge($postActionVars, [ + 'flashes' => $flashes, + ]) + ); + } + if ($this->request->getMethod() == 'POST') { /** @var ListModel $model */ $model = $this->getModel('lead.list'); @@ -547,7 +565,7 @@ public function batchDeleteAction() $deleteIds = []; // Loop over the IDs to perform access checks pre-delete - foreach ($ids as $objectId) { + foreach ($toBeDeleted as $objectId) { $entity = $model->getEntity($objectId); if ($entity === null) { diff --git a/app/bundles/LeadBundle/Translations/en_US/flashes.ini b/app/bundles/LeadBundle/Translations/en_US/flashes.ini index a7af39600b5..0b46456d883 100644 --- a/app/bundles/LeadBundle/Translations/en_US/flashes.ini +++ b/app/bundles/LeadBundle/Translations/en_US/flashes.ini @@ -10,6 +10,8 @@ mautic.lead.lead.notice.addedtolists=" Date: Wed, 16 Jan 2019 09:35:33 +0100 Subject: [PATCH 064/240] add option to lock command with flock on NFS V4 --- .../CoreBundle/Command/ModeratedCommand.php | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/app/bundles/CoreBundle/Command/ModeratedCommand.php b/app/bundles/CoreBundle/Command/ModeratedCommand.php index 3d679c54397..bf8b526cc89 100644 --- a/app/bundles/CoreBundle/Command/ModeratedCommand.php +++ b/app/bundles/CoreBundle/Command/ModeratedCommand.php @@ -15,12 +15,14 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\LockHandler; abstract class ModeratedCommand extends ContainerAwareCommand { - const MODE_LOCK = 'lock'; - const MODE_PID = 'pid'; + const MODE_LOCK = 'lock'; + const MODE_PID = 'pid'; + const MODE_FLOCK = 'flock'; protected $checkFile; protected $moderationKey; @@ -32,6 +34,8 @@ abstract class ModeratedCommand extends ContainerAwareCommand protected $lockFile; private $bypassLocking; + private $flockHandle; + /* @var OutputInterface $output */ protected $output; @@ -54,7 +58,7 @@ protected function configure() '--lock_mode', '-x', InputOption::VALUE_REQUIRED, - 'Force use of PID or FILE LOCK for semaphore. Allowed value are "pid" or "file_lock". By default, lock will try with pid, if not available will use file system', + 'Allowed value are "pid" , "file_lock" or "flock". By default, lock will try with pid, if not available will use file system', 'pid' ); } @@ -72,7 +76,7 @@ protected function checkRunStatus(InputInterface $input, OutputInterface $output $this->bypassLocking = $input->getOption('bypass-locking'); $lockMode = $input->getOption('lock_mode'); - if (!in_array($lockMode, ['pid', 'file_lock'])) { + if (!in_array($lockMode, ['pid', 'file_lock', 'flock'])) { $output->writeln('Unknown locking method specified.'); return false; @@ -125,6 +129,9 @@ protected function completeRun() if (self::MODE_LOCK == $this->moderationMode) { $this->lockHandler->release(); } + if (self::MODE_FLOCK == $this->moderationMode) { + fclose($this->flockHandle); + } // Attempt to keep things tidy @unlink($this->lockFile); @@ -176,6 +183,36 @@ private function checkStatus($force = false, $lockMode = null) return true; } + } elseif ($lockMode === self::MODE_FLOCK && !$force) { + $this->moderationMode = self::MODE_FLOCK; + $error = null; + // Silence error reporting + set_error_handler(function ($errno, $msg) use (&$error) { + $error = $msg; + }); + + if (!$this->flockHandle = fopen($this->lockFile, 'r+') ?: fopen($this->lockFile, 'r')) { + if ($this->flockHandle = fopen($this->lockFile, 'x')) { + chmod($this->lockFile, 0666); + } elseif (!$this->flockHandle = fopen($this->lockFile, 'r+') ?: fopen($this->lockFile, 'r')) { + usleep(100); + $this->flockHandle = fopen($this->lockFile, 'r+') ?: fopen($this->lockFile, 'r'); + } + } + + restore_error_handler(); + + if (!$this->flockHandle) { + throw new IOException($error, 0, null, $this->lockFile); + } + if (!flock($this->flockHandle, LOCK_EX | LOCK_NB)) { + fclose($this->flockHandle); + $this->flockHandle = null; + + return false; + } + + return true; } // in anycase, fallback on file system From 5328cd6da7de5976b6fe3c7f6bebf317dd2cbef3 Mon Sep 17 00:00:00 2001 From: Enc3phale Date: Thu, 17 Jan 2019 14:33:44 +0100 Subject: [PATCH 065/240] Fix front js interface bug for new segment --- app/bundles/LeadBundle/Assets/js/lead.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bundles/LeadBundle/Assets/js/lead.js b/app/bundles/LeadBundle/Assets/js/lead.js index 4a638eabbbb..2983e7ace27 100644 --- a/app/bundles/LeadBundle/Assets/js/lead.js +++ b/app/bundles/LeadBundle/Assets/js/lead.js @@ -566,6 +566,7 @@ Mautic.addLeadListFilter = function (elId, elObj) { 'fast', function () { mQuery(this).remove(); + Mautic.reorderSegmentFilters(); } ); }); From 448a9b02deb16fc12c7c9bea84eb7edb2d5a32a0 Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Wed, 7 Nov 2018 18:48:00 +0100 Subject: [PATCH 066/240] Implemented ability to redirect customer to the save page after he presses the apply button on email settings edit page. --- app/bundles/ConfigBundle/Assets/js/config.js | 62 ++++++++++++++++++- .../Controller/ConfigController.php | 12 +++- .../CoreBundle/Form/Type/ConfigType.php | 2 + .../Config/_config_coreconfig_widget.html.php | 1 + 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/app/bundles/ConfigBundle/Assets/js/config.js b/app/bundles/ConfigBundle/Assets/js/config.js index 0e39580614d..8969b37e866 100644 --- a/app/bundles/ConfigBundle/Assets/js/config.js +++ b/app/bundles/ConfigBundle/Assets/js/config.js @@ -13,4 +13,64 @@ Mautic.removeConfigValue = function(action, el) { mQuery(el).parent().addClass('hide'); } }); -}; \ No newline at end of file +}; + +/** + * + * @returns string|false + */ +Mautic.parseQuery = function (query) { + var vars = query.split('&'); + var queryString = {}; + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1]); + // If first entry with this name + if (typeof queryString[key] === 'undefined') { + queryString[key] = decodeURIComponent(value); + // If second entry with this name + } else if (typeof queryString[key] === 'string') { + var arr = [queryString[key], decodeURIComponent(value)]; + queryString[key] = arr; + // If third or later entry with this name + } else { + queryString[key].push(decodeURIComponent(value)); + } + } + return queryString; +} + +Mautic.parseUrlHashParameter = function(url) { + var url = url.split('#'); + if ('undefined' != typeof url[1]) { + return url[1]; + } + + return false; +} + +Mautic.observeConfigTabs = function() { + + if (!mQuery('#config_coreconfig_last_shown_tab').length) { + return; + } + + var parameters = Mautic.parseQuery(window.location.search.substr(1)); + if ('undefiend' != typeof parameters['tab']) { + mQuery('#config_coreconfig_last_shown_tab').val(parameters['tab']); + mQuery('a[data-toggle="tab"]').each(function (i, tab) { + if (mQuery(tab).attr('href') == ('#' + parameters['tab'])) { + mQuery(tab).tab('show'); + } + }); + } + + mQuery('a[data-toggle="tab"]').on('show.bs.tab', function (e) { + var tab = Mautic.parseUrlHashParameter(e.target.href); + if (tab) { + mQuery('#config_coreconfig_last_shown_tab').val(tab); + } + }); +} +mQuery(Mautic.observeConfigTabs); \ No newline at end of file diff --git a/app/bundles/ConfigBundle/Controller/ConfigController.php b/app/bundles/ConfigBundle/Controller/ConfigController.php index 888f819e435..61ce5ee0786 100644 --- a/app/bundles/ConfigBundle/Controller/ConfigController.php +++ b/app/bundles/ConfigBundle/Controller/ConfigController.php @@ -63,6 +63,7 @@ public function editAction() /** @var \Mautic\CoreBundle\Configurator\Configurator $configurator */ $configurator = $this->get('mautic.configurator'); $isWritabale = $configurator->isFileWritable(); + $openTab = null; // Check for a submitted form and process it if ($this->request->getMethod() == 'POST') { @@ -132,6 +133,10 @@ public function editAction() /** @var \Mautic\CoreBundle\Helper\CacheHelper $cacheHelper */ $cacheHelper = $this->get('mautic.helper.cache'); $cacheHelper->clearContainerFile(); + + if ($isValid && !empty($formData['coreconfig']['last_shown_tab'])) { + $openTab = $formData['coreconfig']['last_shown_tab']; + } } catch (\RuntimeException $exception) { $this->addFlash('mautic.config.config.error.not.updated', ['%exception%' => $exception->getMessage()], 'error'); } @@ -148,7 +153,12 @@ public function editAction() // If the form is saved or cancelled, redirect back to the dashboard if ($cancelled || $isValid) { if (!$cancelled && $this->isFormApplied($form)) { - return $this->delegateRedirect($this->generateUrl('mautic_config_action', ['objectAction' => 'edit'])); + $redirectParameters = ['objectAction' => 'edit']; + if ($openTab) { + $redirectParameters['tab'] = $openTab; + } + + return $this->delegateRedirect($this->generateUrl('mautic_config_action', $redirectParameters)); } else { return $this->delegateRedirect($this->generateUrl('mautic_dashboard_index')); } diff --git a/app/bundles/CoreBundle/Form/Type/ConfigType.php b/app/bundles/CoreBundle/Form/Type/ConfigType.php index 636738f895d..7107044eed5 100644 --- a/app/bundles/CoreBundle/Form/Type/ConfigType.php +++ b/app/bundles/CoreBundle/Form/Type/ConfigType.php @@ -101,6 +101,8 @@ public function __construct( */ public function buildForm(FormBuilderInterface $builder, array $options) { + $builder->add('last_shown_tab', 'hidden'); + $builder->add( 'site_url', 'text', diff --git a/app/bundles/CoreBundle/Views/FormTheme/Config/_config_coreconfig_widget.html.php b/app/bundles/CoreBundle/Views/FormTheme/Config/_config_coreconfig_widget.html.php index 646372df373..c4cea0edab4 100644 --- a/app/bundles/CoreBundle/Views/FormTheme/Config/_config_coreconfig_widget.html.php +++ b/app/bundles/CoreBundle/Views/FormTheme/Config/_config_coreconfig_widget.html.php @@ -27,6 +27,7 @@ rowIfExists($fields, 'log_path', $template); ?> rowIfExists($fields, 'theme', $template); ?> rowIfExists($fields, 'image_path', $template); ?> + rowIfExists($fields, 'last_shown_tab'); ?>
  • From 928661ba87ecc8970568dce899404cd9d09fe7ae Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Fri, 2 Nov 2018 12:02:49 +0100 Subject: [PATCH 067/240] Update "Custom field is used" message --- .../CoreBundle/Translations/en_US/flashes.ini | 4 +-- .../LeadBundle/Controller/FieldController.php | 32 ++++++++++++++++--- app/bundles/LeadBundle/Model/FieldModel.php | 12 +++++++ app/bundles/LeadBundle/Model/ListModel.php | 9 +++++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/app/bundles/CoreBundle/Translations/en_US/flashes.ini b/app/bundles/CoreBundle/Translations/en_US/flashes.ini index 20a60784bba..62e22db7895 100644 --- a/app/bundles/CoreBundle/Translations/en_US/flashes.ini +++ b/app/bundles/CoreBundle/Translations/en_US/flashes.ini @@ -11,8 +11,8 @@ mautic.core.notice.batch_deleted="%count% items deleted" mautic.core.notice.created="%name% has been created!" mautic.core.notice.deleted="%name% has been deleted!" mautic.core.notice.updated="%name% has been updated!" -mautic.core.notice.used.field="Field %name% (#%id%) is used and cannot be deleted." -mautic.core.notice.used.fields="Some fields are used and cannot be deleted." +mautic.core.notice.used.field="Field "%name%" (#%id%) cannot be deleted because it's used in the following Segment(s): %segments%." +mautic.core.notice.used.fields="Field(s) "%fields%" cannot be deleted because they are used in the following Segment(s): %segments%." mautic.core.language.helper.error.fetching.package="An error occurred while downloading the language package." mautic.core.language.helper.error.follow.redirects="Whoops, either safe_mode or open_basedir is turned on. Download the language from here. Unzip and upload it to the /translations directory." mautic.core.language.helper.invalid.language="Requested language '%language%' does not exist among the available Mautic languages. The language was reset to the default one." diff --git a/app/bundles/LeadBundle/Controller/FieldController.php b/app/bundles/LeadBundle/Controller/FieldController.php index cdc16262665..c5cac1e4c00 100644 --- a/app/bundles/LeadBundle/Controller/FieldController.php +++ b/app/bundles/LeadBundle/Controller/FieldController.php @@ -415,13 +415,19 @@ public function deleteAction($objectId) return $this->accessDenied(); } - if ($model->isUsedField($field)) { + $segments = []; + foreach ($model->getFieldSegments($field) as $segment) { + $segments[] = sprintf('"%s" (%d)', $segment->getName(), $segment->getId()); + } + + if (count($segments)) { $flashMessage = [ 'type' => 'error', 'msg' => 'mautic.core.notice.used.field', 'msgVars' => [ - '%name%' => $field->getLabel(), - '%id%' => $objectId, + '%name%' => $field->getLabel(), + '%id%' => $objectId, + '%segments%' => implode(', ', $segments), ], ]; } else { @@ -497,16 +503,34 @@ public function batchDeleteAction() // Delete everything we are able to if (!empty($deleteIds)) { $filteredDeleteIds = $model->filterUsedFieldIds($deleteIds); + $usedFieldIds = array_diff($deleteIds, $filteredDeleteIds); + $segments = []; + $usedFieldsNames = []; + + if ($usedFieldIds) { + // Iterating through all used fileds to get segments they are used in + foreach ($usedFieldIds as $usedFieldId) { + $fieldEntity = $model->getEntity($usedFieldId); + foreach ($model->getFieldSegments($fieldEntity) as $segment) { + $segments[$segment->getId()] = sprintf('"%s" (%d)', $segment->getName(), $segment->getId()); + $usedFieldsNames[] = sprintf('"%s"', $fieldEntity->getName()); + } + } + } if ($filteredDeleteIds !== $deleteIds) { $flashes[] = [ 'type' => 'error', 'msg' => 'mautic.core.notice.used.fields', + 'msgVars' => [ + '%segments%' => implode(', ', $segments), + '%fields%' => implode(', ', array_unique($usedFieldsNames)), + ], ]; } if (count($filteredDeleteIds)) { - $entities = $model->deleteEntities($deleteIds); + $entities = $model->deleteEntities($filteredDeleteIds); $flashes[] = [ 'type' => 'notice', diff --git a/app/bundles/LeadBundle/Model/FieldModel.php b/app/bundles/LeadBundle/Model/FieldModel.php index 6c583099b6b..abaaa787aba 100644 --- a/app/bundles/LeadBundle/Model/FieldModel.php +++ b/app/bundles/LeadBundle/Model/FieldModel.php @@ -602,6 +602,18 @@ public function isUsedField(LeadField $field) return $this->leadListModel->isFieldUsed($field); } + /** + * Returns list of all segments that use $field. + * + * @param LeadField $field + * + * @return \Doctrine\ORM\Tools\Pagination\Paginator + */ + public function getFieldSegments(LeadField $field) + { + return $this->leadListModel->getFieldSegments($field); + } + /** * Filter used field ids. * diff --git a/app/bundles/LeadBundle/Model/ListModel.php b/app/bundles/LeadBundle/Model/ListModel.php index f951bac54a3..1a7ebeb4bda 100644 --- a/app/bundles/LeadBundle/Model/ListModel.php +++ b/app/bundles/LeadBundle/Model/ListModel.php @@ -1722,6 +1722,13 @@ public function getSegmentContactsLineChartData($unit, \DateTime $dateFrom, \Dat * @return bool */ public function isFieldUsed(LeadField $field) + { + $segments = $this->getFieldSegments($field); + + return 0 < $segments->count(); + } + + public function getFieldSegments(LeadField $field) { $alias = $field->getAlias(); $aliasLength = mb_strlen($alias); @@ -1733,6 +1740,6 @@ public function isFieldUsed(LeadField $field) ], ]; - return $this->getEntities(['filter' => $filter])->count() !== 0; + return $this->getEntities(['filter' => $filter]); } } From 878e0bd80b9df0cbd6dafc5558e0ce2a969d2ec3 Mon Sep 17 00:00:00 2001 From: Anton Vlasenko Date: Tue, 6 Nov 2018 13:48:28 +0100 Subject: [PATCH 068/240] Reschedule Events in Contact Record not persisting --- .../CampaignBundle/Assets/js/campaign.js | 52 +++++++++++++++++-- app/bundles/CampaignBundle/Config/config.php | 1 + .../CampaignBundle/Model/EventLogModel.php | 33 +++++------- .../Translations/en_US/messages.ini | 1 + .../SubscribedEvents/Timeline/index.html.php | 5 +- 5 files changed, 68 insertions(+), 24 deletions(-) diff --git a/app/bundles/CampaignBundle/Assets/js/campaign.js b/app/bundles/CampaignBundle/Assets/js/campaign.js index b3778bccea8..4c63543e844 100644 --- a/app/bundles/CampaignBundle/Assets/js/campaign.js +++ b/app/bundles/CampaignBundle/Assets/js/campaign.js @@ -1868,21 +1868,23 @@ Mautic.campaignBuilderValidateConnection = function (epDetails, targetType, targ */ Mautic.updateScheduledCampaignEvent = function(eventId, contactId) { // Convert scheduled date/time to an input - mQuery('#timeline-campaign-event-'+eventId+' .btn-edit').prop('disabled', true).addClass('disabled'); + mQuery('#timeline-campaign-event-'+eventId+' .btn-reschedule').addClass('disabled'); var converting = false; var eventWrapper = '#timeline-campaign-event-'+eventId; var eventSpan = '.timeline-campaign-event-date-'+eventId; var eventText = '#timeline-campaign-event-text-'+eventId; + var saveButton = '#timeline-campaign-event-save-' + eventId; var originalDate = mQuery(eventWrapper+' '+eventSpan).first().text(); var revertInput = function(input) { converting = true; mQuery(input).datetimepicker('destroy'); mQuery(eventSpan).text(originalDate); - mQuery(eventWrapper+' .btn').prop('disabled', false).removeClass('disabled'); + mQuery(eventWrapper+' .btn-reschedule').removeClass('disabled'); }; var date = mQuery(eventSpan).attr('data-date'); + mQuery(saveButton).show(); var input = mQuery('') .css('height', '20px') .css('color', '#000000') @@ -1902,8 +1904,8 @@ Mautic.updateScheduledCampaignEvent = function(eventId, contactId) { originalDate: date }, function (response) { mQuery(eventSpan).text(response.formattedDate); - mQuery(eventSpan).attr('data-data', response.date); - mQuery(eventWrapper+' .btn').prop('disabled', false).removeClass('disabled'); + mQuery(eventSpan).attr('data-date', response.date); + mQuery(eventWrapper+' .btn-reschedule').removeClass('disabled'); if (response.success) { mQuery(eventText).removeClass('text-warning').addClass('text-info'); @@ -1911,21 +1913,63 @@ Mautic.updateScheduledCampaignEvent = function(eventId, contactId) { mQuery('.fa.timeline-campaign-event-cancelled-'+eventId).remove(); mQuery('.timeline-campaign-event-scheduled-'+eventId).removeClass('hide'); mQuery('.timeline-campaign-event-cancelled-'+eventId).addClass('hide'); + mQuery(saveButton).hide(); } }, false ); } else if (code == 27) { e.preventDefault(); revertInput(input); + mQuery(saveButton).hide(); } }) .on('blur', function (e) { if (!converting) { revertInput(input); } + mQuery(saveButton).hide(); }); mQuery('#timeline-campaign-event-'+eventId+' '+eventSpan).html(input); Mautic.activateDateTimeInputs('#timeline-reschedule'); + mQuery('#timeline-reschedule').focus(); +}; + +/** + * + * @param eventId + * @param contactId + */ +Mautic.saveScheduledCampaignEvent = function (eventId, contactId) { + var saveButton = '#timeline-campaign-event-save-' + eventId; + mQuery(saveButton).addClass('disabled'); + + // Convert scheduled date/time to an input + var eventWrapper = '#timeline-campaign-event-' + eventId; + var eventSpan = '.timeline-campaign-event-date-' + eventId; + var eventText = '#timeline-campaign-event-text-' + eventId; + + var date = mQuery(eventSpan).attr('data-date'); + Mautic.ajaxActionRequest('campaign:updateScheduledCampaignEvent', + { + eventId: eventId, + contactId: contactId, + date: mQuery('#timeline-reschedule').val(), + originalDate: date + }, function (response) { + mQuery(eventSpan).text(response.formattedDate); + mQuery(eventSpan).attr('data-date', response.date); + + if (response.success) { + mQuery(eventText).removeClass('text-warning').addClass('text-info'); + mQuery(eventSpan).css('textDecoration', 'inherit'); + mQuery('.fa.timeline-campaign-event-cancelled-' + eventId).remove(); + mQuery('.timeline-campaign-event-scheduled-' + eventId).removeClass('hide'); + mQuery('.timeline-campaign-event-cancelled-' + eventId).addClass('hide'); + } + + mQuery(saveButton).removeClass('disabled').hide(); + mQuery(eventWrapper + ' .btn-reschedule').removeClass('disabled'); + }, false); }; /** diff --git a/app/bundles/CampaignBundle/Config/config.php b/app/bundles/CampaignBundle/Config/config.php index c9edeb46f38..a052fd486f0 100644 --- a/app/bundles/CampaignBundle/Config/config.php +++ b/app/bundles/CampaignBundle/Config/config.php @@ -274,6 +274,7 @@ 'mautic.campaign.model.event', 'mautic.campaign.model.campaign', 'mautic.helper.ip_lookup', + 'mautic.campaign.scheduler', ], ], ], diff --git a/app/bundles/CampaignBundle/Model/EventLogModel.php b/app/bundles/CampaignBundle/Model/EventLogModel.php index 2378705bd84..d337d50a79a 100644 --- a/app/bundles/CampaignBundle/Model/EventLogModel.php +++ b/app/bundles/CampaignBundle/Model/EventLogModel.php @@ -11,10 +11,9 @@ namespace Mautic\CampaignBundle\Model; -use Mautic\CampaignBundle\CampaignEvents; use Mautic\CampaignBundle\Entity\Event; use Mautic\CampaignBundle\Entity\LeadEventLog; -use Mautic\CampaignBundle\Event\CampaignScheduledEvent; +use Mautic\CampaignBundle\Executioner\Scheduler\EventScheduler; use Mautic\CoreBundle\Helper\InputHelper; use Mautic\CoreBundle\Helper\IpLookupHelper; use Mautic\CoreBundle\Model\AbstractCommonModel; @@ -40,6 +39,11 @@ class EventLogModel extends AbstractCommonModel */ protected $ipLookupHelper; + /** + * @var EventScheduler + */ + protected $eventScheduler; + /** * EventLogModel constructor. * @@ -47,11 +51,12 @@ class EventLogModel extends AbstractCommonModel * @param CampaignModel $campaignModel * @param IpLookupHelper $ipLookupHelper */ - public function __construct(EventModel $eventModel, CampaignModel $campaignModel, IpLookupHelper $ipLookupHelper) + public function __construct(EventModel $eventModel, CampaignModel $campaignModel, IpLookupHelper $ipLookupHelper, EventScheduler $eventScheduler) { $this->eventModel = $eventModel; $this->campaignModel = $campaignModel; $this->ipLookupHelper = $ipLookupHelper; + $this->eventScheduler = $eventScheduler; } /** @@ -198,26 +203,16 @@ public function updateContactEvent(Event $event, Lead $contact, array $parameter } /** - * @param $entity + * @param LeadEventLog $entity */ public function saveEntity(LeadEventLog $entity) { - $eventSettings = $this->campaignModel->getEvents(); - if ($this->dispatcher->hasListeners(CampaignEvents::ON_EVENT_SCHEDULED)) { - $event = $entity->getEvent(); - $args = [ - 'eventSettings' => $eventSettings[$event->getEventType()][$event->getType()], - 'eventDetails' => null, - 'event' => $event->convertToArray(), - 'lead' => $entity->getLead(), - 'systemTriggered' => false, - 'dateScheduled' => $entity->getTriggerDate(), - ]; - - $scheduledEvent = new CampaignScheduledEvent($args, $entity); - $this->dispatcher->dispatch(CampaignEvents::ON_EVENT_SCHEDULED, $scheduledEvent); + $triggerDate = $entity->getTriggerDate(); + if (null === $triggerDate) { + // Reschedule for now + $triggerDate = new \DateTime(); } - $this->getRepository()->saveEntity($entity); + $this->eventScheduler->reschedule($entity, $triggerDate); } } diff --git a/app/bundles/CampaignBundle/Translations/en_US/messages.ini b/app/bundles/CampaignBundle/Translations/en_US/messages.ini index 88324607b26..09e305bcf28 100644 --- a/app/bundles/CampaignBundle/Translations/en_US/messages.ini +++ b/app/bundles/CampaignBundle/Translations/en_US/messages.ini @@ -48,6 +48,7 @@ mautic.campaign.event.timed.choice.custom="Custom" mautic.campaign.event.leadchange="contact changed campaigns" mautic.campaign.event.leadchange_descr="Trigger actions when a contact is added/removed from a campaign." mautic.campaign.event.reschedule="Reschedule this event." +mautic.campaign.event.save="Save" mautic.campaign.event.cancel="Cancel this event (it can be rescheduled later)." mautic.campaign.event.cancelled="This event has been cancelled. Reschedule it to restore." mautic.campaign.event.cancelled.time="This event was scheduled for %date% but has been cancelled." diff --git a/app/bundles/CampaignBundle/Views/SubscribedEvents/Timeline/index.html.php b/app/bundles/CampaignBundle/Views/SubscribedEvents/Timeline/index.html.php index 63297031a47..830c26d80b4 100644 --- a/app/bundles/CampaignBundle/Views/SubscribedEvents/Timeline/index.html.php +++ b/app/bundles/CampaignBundle/Views/SubscribedEvents/Timeline/index.html.php @@ -48,7 +48,10 @@ hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())): ?> - +