diff options
author | Benaka Moorthi <benaka.moorthi@gmail.com> | 2013-08-10 03:25:50 +0400 |
---|---|---|
committer | Benaka Moorthi <benaka.moorthi@gmail.com> | 2013-08-10 03:25:50 +0400 |
commit | a3bc6979a99444f29da1aa7973338e3dcd72d309 (patch) | |
tree | 2ac49526db2e40a9a3dfedd0b95cb68b07062265 /plugins | |
parent | cfb17ed2795101ea90d71d124d91b0cbad09858c (diff) |
Refs #3089, add initial visitor profile popup that uses dynamic data from Live plugin API. No client-side behavior yet & placeholder images still present.
Diffstat (limited to 'plugins')
25 files changed, 794 insertions, 1917 deletions
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index c78d5b3d6e..c7b6e01a5f 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -41,6 +41,8 @@ class Piwik_CoreHome extends Plugin public function getCssFiles(&$cssFiles) { $cssFiles[] = "libs/jquery/themes/base/jquery-ui.css"; + $cssFiles[] = "libs/jquery/stylesheets/jquery.jscrollpane.css"; + $cssFiles[] = "libs/jquery/stylesheets/scroll.less"; $cssFiles[] = "plugins/Zeitgeist/stylesheets/base.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/coreHome.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/menu.less"; @@ -62,6 +64,9 @@ class Piwik_CoreHome extends Plugin $jsFiles[] = "libs/jquery/jquery.truncate.js"; $jsFiles[] = "libs/jquery/jquery.scrollTo.js"; $jsFiles[] = "libs/jquery/jquery.history.js"; + $jsFiles[] = "libs/jquery/jquery.jscrollpane.js"; + $jsFiles[] = "libs/jquery/jquery.mousewheel.js"; + $jsFiles[] = "libs/jquery/mwheelIntent.js"; $jsFiles[] = "libs/javascript/sprintf.js"; $jsFiles[] = "plugins/Zeitgeist/javascripts/piwikHelper.js"; $jsFiles[] = "plugins/Zeitgeist/javascripts/ajaxHelper.js"; diff --git a/plugins/CoreHome/javascripts/popover.js b/plugins/CoreHome/javascripts/popover.js index 0ffe6d7619..27c07dc334 100644 --- a/plugins/CoreHome/javascripts/popover.js +++ b/plugins/CoreHome/javascripts/popover.js @@ -17,10 +17,11 @@ var Piwik_Popover = (function () { } }; - var openPopover = function (title) { + var openPopover = function (title, dialogClass) { createContainer(); - container.dialog({ + var options = + { title: title, modal: true, width: '950px', @@ -28,6 +29,10 @@ var Piwik_Popover = (function () { resizable: false, autoOpen: true, open: function (event, ui) { + if (dialogClass) { + $(this).parent().addClass(dialogClass).attr('style', ''); + } + $('.ui-widget-overlay').on('click.popover', function () { container.dialog('close'); }); @@ -45,7 +50,9 @@ var Piwik_Popover = (function () { closeCallback = false; } } - }); + }; + + container.dialog(options); // override the undocumented _title function to ensure that the title attribute is not escaped (according to jQueryUI bug #6016) container.data( "uiDialog" )._title = function(title) { @@ -70,7 +77,7 @@ var Piwik_Popover = (function () { * @param {string} [popoverSubject] subject of the popover (e.g. url, optional) * @param {int} [height] height of the popover in px (optional) */ - showLoading: function (popoverName, popoverSubject, height) { + showLoading: function (popoverName, popoverSubject, height, dialogClass) { var loading = $(document.createElement('div')).addClass('Piwik_Popover_Loading'); var loadingMessage = popoverSubject ? translations.General_LoadingPopoverFor_js : @@ -94,7 +101,7 @@ var Piwik_Popover = (function () { } if (!isOpen) { - openPopover(); + openPopover(null, dialogClass); } this.setContent(loading); @@ -203,9 +210,18 @@ var Piwik_Popover = (function () { * @param {string} url * @param {string} loadingName */ - createPopupAndLoadUrl: function (url, loadingName) { + createPopupAndLoadUrl: function (url, loadingName, dialogClass) { + // make sure the minimum top position of the popover is 106px + var ensureMinimumTop = function () { + var popoverContainer = $('#Piwik_Popover').parent(); + if (popoverContainer.position().top < 106) { + popoverContainer.css('top', '106px'); + } + }; + // open the popover - var box = Piwik_Popover.showLoading(loadingName); + var box = Piwik_Popover.showLoading(loadingName, null, null, dialogClass); + ensureMinimumTop(); var callback = function (html) { function setPopoverTitleIfOneFoundInContainer() { @@ -218,6 +234,7 @@ var Piwik_Popover = (function () { Piwik_Popover.setContent(html); setPopoverTitleIfOneFoundInContainer(); + ensureMinimumTop(); }; var ajaxRequest = new ajaxHelper(); ajaxRequest.addParams(piwikHelper.getArrayFromQueryString(url), 'get'); @@ -226,6 +243,4 @@ var Piwik_Popover = (function () { ajaxRequest.send(false); } }; - -})(); - +})();
\ No newline at end of file diff --git a/plugins/Live/API.php b/plugins/Live/API.php index 3354c67335..739973bb99 100644 --- a/plugins/Live/API.php +++ b/plugins/Live/API.php @@ -155,6 +155,158 @@ class Piwik_Live_API return $dataTable; } + const VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE = 100; + const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10; + const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%'; + + /** + * TODO + * TODO: add abandoned cart info. + * TODO: check for most recent vs. first visit + * TODO: make sure ecommerce is enabled for site, check for goals plugin, etc. + */ + public function getVisitorProfile($idSite, $period, $date, $idVisitor, $segment = false) + { + if ($segment !== false) { + $segment .= '&'; + } + $segment .= 'visitorId==' . $idVisitor; // TODO what happens when visitorId is in the segment? + + $visits = $this->getLastVisitsDetails($idSite, $period, $date, $segment, $filter_limit = self::VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE); + if ($visits->getRowsCount() == 0) { + return array(); + } + + $result = array(); + + // use the most recent visit for IP/browser/OS/etc. info + // TODO: could just do all of this in twig/JS... really need to do it here? + $mostRecentVisit = $visits->getFirstRow(); + $result['latestVisitIp'] = $mostRecentVisit->getColumn('visitIp'); + $result['visitorId'] = $mostRecentVisit->getColumn('visitorId'); + $result['browserCode'] = $mostRecentVisit->getColumn('browserCode'); + $result['browserName'] = Piwik_UserSettings_getBrowserFromBrowserVersion($mostRecentVisit->getColumn('browserName')); + $result['browserLogo'] = $mostRecentVisit->getColumn('browserIcon'); + $result['operatingSystemCode'] = $mostRecentVisit->getColumn('operatingSystemCode'); + $result['operatingSystemShortName'] = $mostRecentVisit->getColumn('operatingSystemShortName'); + $result['operatingSystemLogo'] = $mostRecentVisit->getColumn('operatingSystemIcon'); + $result['resolution'] = $mostRecentVisit->getColumn('resolution'); + $result['customVariables'] = $mostRecentVisit->getColumn('customVariables'); + + // aggregate all requested visits info for total_* info + $result['totalVisits'] = 0; + $result['totalVisitDuration'] = 0; + $result['totalActionCount'] = 0; + $result['totalGoalConversions'] = 0; + $result['totalEcommerceConversions'] = 0; + $result['totalEcommerceRevenue'] = 0; + $result['totalEcommerceItems'] = 0; + $result['totalAbandonedCarts'] = 0; + $result['totalAbandonedCartsRevenue'] = 0; + $result['totalAbandonedCartsItems'] = 0; + foreach ($visits->getRows() as $visit) { + ++$result['totalVisits']; + + $result['totalVisitDuration'] += $visit->getColumn('visitDuration'); + $result['totalActionCount'] += $visit->getColumn('actions'); + $result['totalGoalConversions'] += $visit->getColumn('goalConversions'); + + // individual goal conversions are stored in action details + foreach ($visit->getColumn('actionDetails') as $action) { + if ($action['type'] == 'goal') { // handle goal conversion + $idGoal = $action['goalId']; + + if (!isset($result['totalConversionsByGoal'][$idGoal])) { + $result['totalConversionsByGoal'][$idGoal] = 0; + } + ++$result['totalConversionsByGoal'][$idGoal]; + + if (!empty($action['revenue'])) { + if (!isset($result['totalRevenueByGoal'][$idGoal])) { + $result['totalRevenueByGoal'][$idGoal] = 0; + } + $result['totalRevenueByGoal'][$idGoal] += $action['revenue']; + } + } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { // handle ecommerce order + ++$result['totalEcommerceConversions']; + $result['totalEcommerceRevenue'] += $action['revenue']; + $result['totalEcommerceItems'] += $action['items']; + } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) { // handler abandoned cart + ++$result['totalAbandonedCarts']; + $result['totalAbandonedCartsRevenue'] += $action['revenue']; + $result['totalAbandonedCartsItems'] += $action['items']; + } + } + } + + $result['totalVisitDurationPretty'] = Piwik::getPrettyTimeFromSeconds($result['totalVisitDuration']); + + // use requested visits for first/last visit info + $result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($visits->getRows())); + $result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($visits->getRows())); + + // use N most recent visits for last_visits + $visits->deleteRowsOffset(self::VISITOR_PROFILE_MAX_VISITS_TO_SHOW); + $result['lastVisits'] = $visits; + + // use the right date format for the pretty server date + $timezone = Site::getTimezoneFor($idSite); + foreach ($result['lastVisits']->getRows() as $visit) { + $dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone); + $dateTimePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT); + + $visit->setColumn('serverDatePrettyFirstAction', $dateTimePretty); + } + + return $result; + } + + /** + * Returns a summary for an important visit. Used to describe the first & last visits of a visitor. + * + * @param Piwik\DataTable\Row $visit + */ + private function getVisitorProfileVisitSummary($visit) + { + $today = Date::today(); + + $serverDate = $visit->getColumn('serverDate'); + return array( + 'date' => $serverDate, + 'prettyDate' => Date::factory($serverDate)->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT), + 'daysAgo' => (int)Date::secondsToDays($today->getTimestamp() - Date::factory($serverDate)->getTimestamp()), + 'referralSummary' => $this->getReferrerSummaryForVisit($visit), + ); + } + + /** + * Returns a summary for a visit's referral. + * + * @param Piwik\DataTable\Row $visit + */ + private function getReferrerSummaryForVisit($visit) + { + $referrerType = $visit->getColumn('referrerType'); + if ($referrerType === false + || $referrerType == 'direct' + ) { + $result = Piwik_Translate('Referers_DirectEntry'); + } else if ($referrerType == 'search') { + $result = $visit->getColumn('referrerName'); + + $keyword = $visit->getColumn('referrerKeyword'); + if ($keyword !== false) { + $result .= ' (' . $keyword . ')'; + } + } else if ($referrerType == 'campaign') { + $result = Piwik_Translate('Referers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')'; + } else { + $result = $visit->getColumn('referrerName'); + } + + return $result; + } + /** * @deprecated */ diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php index 3e708d4d79..f1ee8bda23 100644 --- a/plugins/Live/Controller.php +++ b/plugins/Live/Controller.php @@ -99,6 +99,7 @@ class Piwik_Live_Controller extends Controller */ public function getVisitorLog($fetch = false) { + $test = array(); $str = (string)$test; return $this->getLastVisitsDetails($fetch); } @@ -130,4 +131,18 @@ class Piwik_Live_Controller extends Controller $view->pisToday = $today['actions']; return $view; } + + /** + * TODO + */ + public function getVisitorProfilePopup() + { + $idSite = Common::getRequestVar('idSite', null, 'int'); + + $view = new View('@Live/getVisitorProfilePopup.twig'); + $view->idSite = $idSite; + $view->goals = Piwik_Goals_API::getInstance()->getGoals($idSite); + $view->visitorData = Request::processRequest('Live.getVisitorProfile'); + echo $view->render(); + } }
\ No newline at end of file diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php index e4c15c1ed7..54820cd836 100644 --- a/plugins/Live/Live.php +++ b/plugins/Live/Live.php @@ -35,6 +35,7 @@ class Piwik_Live extends Plugin public function getCssFiles(&$cssFiles) { $cssFiles[] = "plugins/Live/stylesheets/live.less"; + $cssFiles[] = "plugins/Live/stylesheets/visitor_profile.less"; } public function getJsFiles(&$jsFiles) @@ -89,4 +90,4 @@ class Piwik_Live extends Plugin ) ); } -} +}
\ No newline at end of file diff --git a/plugins/Live/images/REMOVE_ME_avatar.jpg b/plugins/Live/images/REMOVE_ME_avatar.jpg Binary files differnew file mode 100644 index 0000000000..dfd7ec81de --- /dev/null +++ b/plugins/Live/images/REMOVE_ME_avatar.jpg diff --git a/plugins/Live/images/REMOVE_ME_chart.png b/plugins/Live/images/REMOVE_ME_chart.png Binary files differnew file mode 100644 index 0000000000..5f8f03ad6a --- /dev/null +++ b/plugins/Live/images/REMOVE_ME_chart.png diff --git a/plugins/Live/images/REMOVE_ME_map.jpg b/plugins/Live/images/REMOVE_ME_map.jpg Binary files differnew file mode 100644 index 0000000000..ffae900c21 --- /dev/null +++ b/plugins/Live/images/REMOVE_ME_map.jpg diff --git a/plugins/Live/images/avatar_frame.png b/plugins/Live/images/avatar_frame.png Binary files differnew file mode 100644 index 0000000000..23eccfd5fb --- /dev/null +++ b/plugins/Live/images/avatar_frame.png diff --git a/plugins/Live/images/paperclip.png b/plugins/Live/images/paperclip.png Binary files differnew file mode 100644 index 0000000000..b38d33caa1 --- /dev/null +++ b/plugins/Live/images/paperclip.png diff --git a/plugins/Live/images/visitor_profile_background.jpg b/plugins/Live/images/visitor_profile_background.jpg Binary files differnew file mode 100644 index 0000000000..082d637dcf --- /dev/null +++ b/plugins/Live/images/visitor_profile_background.jpg diff --git a/plugins/SegmentEditor/images/down_arrow.png b/plugins/Live/images/visitor_profile_close.png Binary files differindex a364892c7d..ae132b7b2c 100644 --- a/plugins/SegmentEditor/images/down_arrow.png +++ b/plugins/Live/images/visitor_profile_close.png diff --git a/plugins/SegmentEditor/images/slide.png b/plugins/Live/images/visitor_profile_gradient.png Binary files differindex 657b9a6de5..ac5068b54d 100644 --- a/plugins/SegmentEditor/images/slide.png +++ b/plugins/Live/images/visitor_profile_gradient.png diff --git a/plugins/Live/stylesheets/visitor_profile.less b/plugins/Live/stylesheets/visitor_profile.less new file mode 100644 index 0000000000..4b34c32698 --- /dev/null +++ b/plugins/Live/stylesheets/visitor_profile.less @@ -0,0 +1,365 @@ +.visitor-profile { + position:relative; + width:1149px; + height:758px; + border:1px solid #a19e96; + border-radius:5px; + background:url(../images/visitor_profile_background.jpg) repeat; + box-shadow:5px 5px 5px rgba(0,0,0,0.22); + + h1 { + font-size:18px; + color:#7e7363; + text-shadow:0 1px 0 rgba(255,255,255,1); + margin:9px 0 0 0; + padding:0; + + a { + font-size:12px; + margin-left:3px; + } + } + + span, strong { + display:inline-block; + font-size:14px; + color:#5e5e5c; + line-height:19px; + padding-left:4px; + } + + p { + font-size:13px; + color:#5e5e5c; + line-height:20px; + } + + h2 { + display:inline-block; + font-size:14px; + margin:0 0 0 5px; + padding:0; + font-weight:bold; + color:black; + } + + // TODO: iOS icon looks better in mockup + // TODO: remove temporary images +} + +.visitor-profile-close { + position:absolute; + right:-17px; + top:-16px; + height:35px; + width:35px; + background:url(../images/visitor_profile_close.png) no-repeat; +} + +.visitor-profile a { + text-decoration:none; + color:#255792; +} + +.visitor-profile > div { + width:100%; +} + +.visitor-profile-info { + height:705px; + border-top:2px solid #f6f6f6; + border-bottom:1px solid #d1cec8; + border-radius:5px 5px 0 0; + box-shadow:inset 0 25px 15px -10px #e0e0e0, inset 0 -25px 15px -10px #e0e0e0; + + > div { + width:573px; + height:100%; + float:left; + border-left:1px solid #d1cec8; + + > div { + border-bottom:1px solid #d1cec8; + } + } + + > div:first-child { + border-left:none; + } + + > div:last-child { + border-bottom:none; + } +} + +.visitor-profile-avatar > div { + position:relative; + float:left; + height:145px; + margin-right:15px; + padding:12px 0 13px; +} + +.visitor-profile-avatar > div:first-child { + width:166px; + margin-right:0; + padding-left:16px; + + > .visitor-profile-image-frame { + width:149px; + height:154px; + background:url(../images/avatar_frame.png) no-repeat; + + > img { // avatar image + width:122px; + height:120px; + margin:11px 0 0 12px; + } + } + + > img { // paperclip image + position:absolute; + top:-8px; + left:3px; + z-index:2; + } +} + +.visitor-profile-avatar > div:last-child { + margin-right:0; +} + +.visitor-profile-more-info { + height:18px; + border-top:1px solid #fff; + border-radius:0 0 5px 5px; + text-align:center; + padding:20px 0 13px; + + > a { + font-size:14px; + text-decoration:none; + color:#255792; + text-shadow:0 1px 0 rgba(255,255,255,1); + } +} + +.visitor-profile-latest-visit-column { + padding-top:6px; + display:inline-block; + vertical-align:top; +} + +.visitor-profile-browser { + width:75px; + display:inline-block; +} + +.visitor-profile-os { + width:75px; + display:inline-block; +} + +.visitor-profile-latest-visit-column:first-child { + margin-right:9px; +} + +.visitor-profile-avatar ul { + width:178px; +} + +.visitor-profile-avatar ul li { + display:inline-block; + height:24px; + border-bottom:1px solid #d1cec8; + width:100%; +} + +.visitor-profile-avatar ul li:last-child { + border-bottom:none; +} + +.visitor-profile-avatar ul li:first-child { + border-bottom:1px solid #d1cec8; // make sure there is a border if only one item is shown in the list +} + +.visitor-profile-map { + padding:19px 0 18px 19px; +} + +.visitor-profile-map * { + border-radius:2px; + background-color:#fff; + width:532px; + height:243px; + padding:2px; +} + +.visitor-profile-summary,.visitor-profile-important-visits { + overflow:hidden; + height:116px; + padding:5px 0 0 22px; +} + +.visitor-profile-summary > div { + margin-top:6px; +} + +.visitor-profile-important-visits { + + > div { + float:left; + width:265px; + height:100%; + + > div { + margin-top:13px; + } + } + + span { + padding-left:0; + } +} + +.visitor-profile-location { + padding:10px 0 4px 19px; + + img { + margin-top:-12px; + } +} + +.visitor-profile-pages-visited { + height:42px; + overflow-y:auto; + position:relative; + margin-right:10px; + border-bottom:none!important; + padding:8px 18px 10px 13px; + + h1 { + margin-left:6px; + } +} + +.visitor-profile-actions { + height:460px; + overflow-y:auto; + position:relative; + margin-right:10px; + border-bottom:none!important; + padding:0 18px 0 13px; + + ol { + counter-reset:item; + list-style-type:none; + + > li { + display:block; + font-size:12px; + font-weight:700; + line-height:25px; + padding:0 0 10px 13px; + + span { + font-size:13px; + font-weight:700; + line-height:25px; + padding-left:0; + } + } + + > li:before { + content:counter(item) " "; + counter-increment:item; + } + } + + // TODO: unordered lists no longer used, remove + ol li ul,ol li ol { + border-top:1px solid #d1cec8; + } + + ol li ul { + padding-left:15px; + } + + ol > li > ol > li { + margin-left:-12px; + } + + ol li ul li,ol li ol li { + display:block; + color:#5e5e5c; + font-size:13px; + line-height:22px; + padding-top:1px; + padding-bottom:1px; + } + + ol li ol li { + padding-bottom:4px; + } + + ol li ul li a,ol li ol li a { + display:inline-block; + } + + ol > li ol li span { + padding-left:4px; + } + + ol > li ol li .action-list-url { + margin-left:15px; + line-height:14px; + display:inline-block; + } + + ol > li ol li img { + margin-left:7px; + } + + // overrides for _actionsDetails styles + strong { + font-size:13px; + line-height:25px; + } +} + +.visitor-profile-date { + float:right; + font-size:13px; + line-height:26px; +} + +.visitor-profile-fog { + height:25px; + width:546px; + position:absolute; + bottom:51px; + right:28px; + background:url(../images/visitor_profile_gradient.png) repeat-x; +} + +// popup css +.visitor-profile-popup { + width: 1151px; + height: auto; + padding: 0; + + > .ui-dialog-titlebar { + display: none; + } + + > #Piwik_Popover { + padding: 0; + margin: 0; + overflow: visible; + } +} + +.visitor-profile-goal-name { + font-weight:bold; + font-style:italic; +}
\ No newline at end of file diff --git a/plugins/Live/templates/_actionsList.twig b/plugins/Live/templates/_actionsList.twig new file mode 100644 index 0000000000..9f5bc672e4 --- /dev/null +++ b/plugins/Live/templates/_actionsList.twig @@ -0,0 +1,106 @@ +{% set visitorHasSomeEcommerceActivity %}0{% endset %} +{% for action in actionDetails %} + {% set customVariablesTooltip %} + {% if action.customVariables is defined %} + {{ 'CustomVariables_CustomVariables'|translate }} + {% for id,customVariable in action.customVariables %} + {% set name = 'customVariablePageName' ~ id %} + {% set value = 'customVariablePageValue' ~ id %} + - {{ customVariable[name]|raw }} {% if customVariable[value]|length > 0 %} = {{ customVariable[value]|raw }}{% endif %} + {% endfor %} + {% endif %} + {% endset %} + {% if not javascriptVariablesToSet.filterEcommerce or action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %} + <li class="{% if action.goalName is defined %}goal{% else %}action{% endif %}" + title="{{ action.serverTimePretty }}{% if action.url is defined and action.url|trim|length %} + {{ action.url }}{% endif %} {% if customVariablesTooltip|trim|length %} + + {{ customVariablesTooltip|trim }}{% endif %}{% if action.timeSpentPretty is defined %} + + {{ 'General_TimeOnPage'|translate }}: {{ action.timeSpentPretty|raw }}{% endif %}{% if action.generationTime is defined %} + + {{ 'General_ColumnGenerationTime'|translate }}: {{ action.generationTime|raw }}{% endif %}"> + {% if action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %} + {# Ecommerce Abandoned Cart / Ecommerce Order #} + <img src="{{ action.icon }}"/> + {% if action.type == 'ecommerceOrder' %} + {% set visitorHasSomeEcommerceActivity %}1{% endset %} + <strong>{{ 'Goals_EcommerceOrder'|translate }}</strong> + <span style='color:#666666'>({{ action.orderId }})</span> + {% else %} + <strong>{{'Goals_AbandonedCart'|translate}}</strong> + + {# TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer #} + {% if javascriptVariablesToSet.filterEcommerce == 2 %} + {% set visitorHasSomeEcommerceActivity %}1{% endset %} + {% endif %} + {% endif %} + <br/> + <span {% if not isWidget %}style='margin-left:20px'{% endif %}> + {% if action.type == 'ecommerceOrder' %} + <abbr title=" + {{ 'Live_GoalRevenue'|translate }}: {{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }} + {% if action.revenueSubTotal is not empty %} - {{ 'General_Subtotal'|translate }}: {{ action.revenueSubTotal|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} + {% if action.revenueTax is not empty %} - {{ 'General_Tax'|translate }}: {{ action.revenueTax|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} + {% if action.revenueShipping is not empty %} - {{ 'General_Shipping'|translate }}: {{ action.revenueShipping|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} + {% if action.revenueDiscount is not empty %} - {{ 'General_Discount'|translate }}: {{ action.revenueDiscount|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} + ">{{ 'Live_GoalRevenue'|translate }}: + {% else %} + {% set revenueLeft %}{{ 'Live_GoalRevenue'|translate }}{% endset %} + {{ 'Goals_LeftInCart'|translate(revenueLeft) }}: + {% endif %} + <strong>{{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }}</strong> + {% if action.type == 'ecommerceOrder' %} + </abbr> + {% endif %}, {{ 'General_Quantity'|translate }}: {{ action.items }} + + {# Ecommerce items in Cart/Order #} + {% if action.itemDetails is not empty %} + <ul style='list-style:square;margin-left:{% if isWidget %}15{% else %}50{% endif %}px'> + {% for product in action.itemDetails %} + <li> + {{ product.itemSKU }}{% if product.itemName is not empty %}: {{ product.itemName }}{% endif %} + {% if product.itemCategory is not empty %} ({{ product.itemCategory }}){% endif %} + , + {{ 'General_Quantity'|translate }}: {{ product.quantity }}, + {{ 'General_Price'|translate }}: {{ product.price|money(javascriptVariablesToSet.idSite)|raw }} + </li> + {% endfor %} + </ul> + {% endif %} + </span> + {% elseif action.goalName is not defined%} + {# Page view / Download / Outlink #} + {% if action.pageTitle is defined and action.pageTitle is not empty %} + <span>{{ action.pageTitle|truncate(80) }}</span> + {% endif %} + {% if action.siteSearchKeyword is defined %} + {% if action.type == 'search' %} + <img src='{{ action.icon }}' title='{{ 'Actions_SubmenuSitesearch'|translate }}'> + {% endif %} + {{ action.siteSearchKeyword|truncate(80) }} + {% endif %} + {% if action.url is not empty %} + {% if action.type == 'action' and action.pageTitle is not empty %}<br/>{% endif %} + {% if action.type == 'download' or action.type == 'outlink' %} + <img src='{{ action.icon }}'> + {% endif %} + <a href="{{ action.url }}" target="_blank" class="action-list-url" + {% if overrideLinkStyle is not defined or overrideLinkStyle %}style="{% if action.type=='action' and action.pageTitle is not empty %}margin-left: 25px;{% endif %}text-decoration:underline;"{% endif %}> + {{ action.url|truncate(80) }} + </a> + {% elseif action.type != 'search' %} + <br/> + <span style="margin-left: 25px;">{{ javascriptVariablesToSet.pageUrlNotDefined }}</span> + {% endif %} + {% else %} + {# Goal conversion #} + <img src="{{ action.icon }}" /> + <strong>{{ action.goalName }}</strong> + {% if action.revenue > 0 %}, {{ 'Live_GoalRevenue'|translate }}: + <strong>{{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }}</strong> + {% endif %} + {% endif %} + </li> + {% endif %} +{% endfor %}
\ No newline at end of file diff --git a/plugins/Live/templates/getVisitorLog.twig b/plugins/Live/templates/getVisitorLog.twig index 953528ac2f..211d1d0cb3 100644 --- a/plugins/Live/templates/getVisitorLog.twig +++ b/plugins/Live/templates/getVisitorLog.twig @@ -187,112 +187,7 @@ </strong> <br/> <ol class='visitorLog'> - {% set visitorHasSomeEcommerceActivity %}0{% endset %} - {% for action in visitor.getColumn('actionDetails') %} - {% set customVariablesTooltip %} - {% if action.customVariables is defined %} - {{ 'CustomVariables_CustomVariables'|translate }} - {% for id,customVariable in action.customVariables %} - {% set name = 'customVariablePageName' ~ id %} - {% set value = 'customVariablePageValue' ~ id %} - - {{ customVariable[name]|raw }} {% if customVariable[value]|length > 0 %} = {{ customVariable[value]|raw }}{% endif %} - {% endfor %} - {% endif %} - {% endset %} - {% if not javascriptVariablesToSet.filterEcommerce or action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %} - <li class="{% if action.goalName is defined %}goal{% else %}action{% endif %}" - title="{{ action.serverTimePretty }}{% if action.url is defined and action.url|trim|length %} - {{ action.url }}{% endif %} {% if customVariablesTooltip|trim|length %} - - {{ customVariablesTooltip|trim }}{% endif %}{% if action.timeSpentPretty is defined %} - - {{ 'General_TimeOnPage'|translate }}: {{ action.timeSpentPretty|raw }}{% endif %}{% if action.generationTime is defined %} - - {{ 'General_ColumnGenerationTime'|translate }}: {{ action.generationTime|raw }}{% endif %}"> - {% if action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %} - {# Ecommerce Abandoned Cart / Ecommerce Order #} - <img src="{{ action.icon }}"/> - {% if action.type == 'ecommerceOrder' %} - {% set visitorHasSomeEcommerceActivity %}1{% endset %} - <strong>{{ 'Goals_EcommerceOrder'|translate }}</strong> - <span style='color:#666666'>({{ action.orderId }})</span> - {% else %} - <strong>{{'Goals_AbandonedCart'|translate}}</strong> - - {# TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer #} - {% if javascriptVariablesToSet.filterEcommerce == 2 %} - {% set visitorHasSomeEcommerceActivity %}1{% endset %} - {% endif %} - {% endif %} - <br/> - <span {% if not isWidget %}style='margin-left:20px'{% endif %}> - {% if action.type == 'ecommerceOrder' %} - <abbr title=" - {{ 'Live_GoalRevenue'|translate }}: {{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }} - {% if action.revenueSubTotal is not empty %} - {{ 'General_Subtotal'|translate }}: {{ action.revenueSubTotal|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} - {% if action.revenueTax is not empty %} - {{ 'General_Tax'|translate }}: {{ action.revenueTax|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} - {% if action.revenueShipping is not empty %} - {{ 'General_Shipping'|translate }}: {{ action.revenueShipping|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} - {% if action.revenueDiscount is not empty %} - {{ 'General_Discount'|translate }}: {{ action.revenueDiscount|money(javascriptVariablesToSet.idSite)|raw }}{% endif %} - ">{{ 'Live_GoalRevenue'|translate }}: - {% else %} - {% set revenueLeft %}{{ 'Live_GoalRevenue'|translate }}{% endset %} - {{ 'Goals_LeftInCart'|translate(revenueLeft) }}: - {% endif %} - <strong>{{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }}</strong> - {% if action.type == 'ecommerceOrder' %} - </abbr> - {% endif %}, {{ 'General_Quantity'|translate }}: {{ action.items }} - - {# Ecommerce items in Cart/Order #} - {% if action.itemDetails is not empty %} - <ul style='list-style:square;margin-left:{% if isWidget %}15{% else %}50{% endif %}px'> - {% for product in action.itemDetails %} - <li> - {{ product.itemSKU }}{% if product.itemName is not empty %}: {{ product.itemName }}{% endif %} - {% if product.itemCategory is not empty %} ({{ product.itemCategory }}){% endif %} - , - {{ 'General_Quantity'|translate }}: {{ product.quantity }}, - {{ 'General_Price'|translate }}: {{ product.price|money(javascriptVariablesToSet.idSite)|raw }} - </li> - {% endfor %} - </ul> - {% endif %} - </span> - {% elseif action.goalName is not defined%} - {# Page view / Download / Outlink #} - {% if action.pageTitle is defined %} - {{ action.pageTitle|truncate(80) }} - {% endif %} - {% if action.siteSearchKeyword is defined %} - {% if action.type == 'search' %} - <img src='{{ action.icon }}' title='{{ 'Actions_SubmenuSitesearch'|translate }}'> - {% endif %} - {{ action.siteSearchKeyword|truncate(80) }} - {% endif %} - {% if action.url is not empty %} - {% if action.type == 'action' and action.pageTitle is not empty %}<br/>{% endif %} - {% if action.type == 'download' or action.type == 'outlink' %} - <img src='{{ action.icon }}'> - {% endif %} - <a href="{{ action.url }}" target="_blank" - style="{% if action.type=='action' and action.pageTitle is not empty %}margin-left: 25px;{% endif %}text-decoration:underline;"> - {{ action.url|truncate(80) }} - </a> - {% elseif action.type != 'search' %} - <br/> - <span style="margin-left: 25px;">{{ javascriptVariablesToSet.pageUrlNotDefined }}</span> - {% endif %} - {% else %} - {# Goal conversion #} - <img src="{{ action.icon }}" /> - <strong>{{ action.goalName }}</strong> - {% if action.revenue > 0 %}, {{ 'Live_GoalRevenue'|translate }}: - <strong>{{ action.revenue|money(javascriptVariablesToSet.idSite)|raw }}</strong> - {% endif %} - {% endif %} - </li> - {% endif %} - {% endfor %} + {% include "@Live/_actionsList.twig" with {'actionDetails': visitor.getColumn('actionDetails')} %} </ol> </td> </tr> @@ -317,8 +212,8 @@ var visitorLogTitle = '{{ 'Live_VisitorLog'|translate|e('js') }}'; function Piwik_Live_LoadVisitorPopover(visitorId) { var startingDate = piwik.minDateYear + '-01-01'; - var url = 'module=Live&action=getVisitorLog&period=range&date=' + startingDate + ',today&show_footer=0&segment=visitorId' + encodeURIComponent('==') + visitorId; - return Piwik_Popover.createPopupAndLoadUrl(url, visitorLogTitle); + var url = 'module=Live&action=getVisitorProfilePopup&period=range&date=' + startingDate + ',today&idVisitor=' + encodeURIComponent(visitorId); + return Piwik_Popover.createPopupAndLoadUrl(url, visitorLogTitle, 'visitor-profile-popup'); } $(document).ready(function () { diff --git a/plugins/Live/templates/getVisitorProfilePopup.twig b/plugins/Live/templates/getVisitorProfilePopup.twig new file mode 100644 index 0000000000..8fe3ef39e2 --- /dev/null +++ b/plugins/Live/templates/getVisitorProfilePopup.twig @@ -0,0 +1,121 @@ +<div class="visitor-profile"> + <a href class="visitor-profile-close"></a> + <div class="visitor-profile-info"> + <div> + <div class="visitor-profile-avatar"> + <div> + <div class="visitor-profile-image-frame"><!-- TODO translate --> + <img src="plugins/Live/images/REMOVE_ME_avatar.jpg" alt=""/> + </div> + <img src="plugins/Live/images/paperclip.png" alt=""/> + </div> + <div> + <h1>Visitor profile</h1> + <div> + <div class="visitor-profile-latest-visit-column"> + <ul> + <li><span>IP</span><strong>{{ visitorData.latestVisitIp }}</strong></li> + <li><span>ID</span><strong>{{ visitorData.visitorId }}</strong></li> + <li> + <div class="visitor-profile-browser"> + <img src="{{ visitorData.browserLogo }}"/><span>{{ visitorData.browserName }}</span> + </div> + <div class="visitor-profile-os"> + <img src="{{ visitorData.operatingSystemLogo }}"/><span>{{ visitorData.operatingSystemShortName }}</span> + </div> + </li> + <li><span>Resolution</span><strong>{{ visitorData.resolution }}</strong></li> + </ul> + </div> + <div class="visitor-profile-latest-visit-column"> + <ul> + {% for id,customVariable in visitorData.customVariables %} + {% if loop.index0 < 4 %} + {% set name='customVariableName' ~ id %} + {% set value='customVariableValue' ~ id %} + <li><span>{{ customVariable[name]|truncate(30) }}</span>{% if customVariable[value]|length > 0 %}<strong>{{ customVariable[value]|truncate(50) }}</strong>{% endif %}</li> + {% endif %} + {% endfor %} + {# TODO: Other custom vars go in expanding div. #} + </ul> + </div> + </div> + </div> + <p style="clear:both; border:none!important;"></p> + </div> + <div style="clear:both; border:none!important;"></div> + <div class="visitor-profile-map"> + <img alt="" src="plugins/Live/images/REMOVE_ME_map.jpg"/> {# TODO: map #} + </div> + <div class="visitor-profile-important-visits"> + <div> + <h1>First visit</h1> + <div> + <p><strong>{{ visitorData.firstVisit.prettyDate }}</strong><span> - {{ visitorData.firstVisit.daysAgo }} days ago</span></p> + <p><span>from:</span></p> + <p><strong>{{ visitorData.firstVisit.referralSummary }}</strong></p> + </div> + </div> + <div> + <h1>Last visit</h1> + <div> + <p><strong>{{ visitorData.lastVisit.prettyDate }}</strong><span> - {{ visitorData.lastVisit.daysAgo }} days ago</span></p> + <p><span>from:</span></p> + <p><strong>{{ visitorData.lastVisit.referralSummary }}</strong></p> + </div> + </div> + </div> + <div class="visitor-profile-summary"> + <h1>Summary</h1> + <div> + <p>Spent a total of <strong>{{ visitorData.totalVisitDurationPretty|raw }} on the website</strong>, and <strong>viewed {{ visitorData.totalActionCount }} pages in {{ visitorData.totalVisits }} visits.</strong></p> + <p><strong>Converted {{ visitorData.totalGoalConversions }} Goals</strong> ( + {%- for idGoal, totalConversions in visitorData.totalConversionsByGoal -%} + {%- if not loop.first %}, {% endif -%}{{- totalConversions }} <span class="visitor-profile-goal-name">{{ goals[idGoal]['name'] -}}</span> + {%- endfor -%} + ).</p> + <p>Ecommerce: <strong>{{ visitorData.totalEcommerceConversions }} orders for a total of {{ visitorData.totalEcommerceRevenue|money(idSite)|raw }}</strong>, purchased {{ visitorData.totalEcommerceItems }} items.</p> + </div> + </div> + </div> + <div> + <div class="visitor-profile-location"> + <h1>Location</h1> + <img src="plugins/Live/images/REMOVE_ME_chart.png" alt=""/> {# TODO: country & bar graph #} + </div> + <div class="visitor-profile-pages-visited"> + <h1>Visited pages<a href>see all</a></h1> + </div> + <div class="visitor-profile-actions"> + <ol> + {% for visitInfo in visitorData.lastVisits.getRows() %} + <li><h2>Visit</h2><span class="visitor-profile-date">{{ visitInfo.getColumn('serverDatePrettyFirstAction') }}</span> + <ol> + {% include "@Live/_actionsList.twig" with {'actionDetails': visitInfo.getColumn('actionDetails'), + 'javascriptVariablesToSet': { + 'filterEcommerce': false, + 'idSite': idSite + }, + 'overrideLinkStyle': false} %} + </ol> + </li> + {% endfor %} + </ol> + <br/> + </div> + <div class="visitor-profile-fog"></div> + </div> + </div> + <div class="visitor-profile-more-info"> + <a href="#">View more visitor information</a> + </div> +</div> +<script type="text/javascript"> +$(function() { + $('.visitor-profile-actions').jScrollPane({ + showArrows: true, + verticalArrowPositions: 'os', + horizontalArrowPositions: 'os' + }); +}); +</script>
\ No newline at end of file diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php index 5d1731c006..48359598a6 100644 --- a/plugins/SegmentEditor/SegmentEditor.php +++ b/plugins/SegmentEditor/SegmentEditor.php @@ -97,16 +97,11 @@ class Piwik_SegmentEditor extends Plugin public function getJsFiles(&$jsFiles) { - $jsFiles[] = "plugins/SegmentEditor/javascripts/jquery.jscrollpane.js"; $jsFiles[] = "plugins/SegmentEditor/javascripts/Segmentation.js"; - $jsFiles[] = "plugins/SegmentEditor/javascripts/jquery.mousewheel.js"; - $jsFiles[] = "plugins/SegmentEditor/javascripts/mwheelIntent.js"; } public function getCssFiles(&$cssFiles) { $cssFiles[] = "plugins/SegmentEditor/stylesheets/segmentation.less"; - $cssFiles[] = "plugins/SegmentEditor/stylesheets/jquery.jscrollpane.css"; - $cssFiles[] = "plugins/SegmentEditor/stylesheets/scroll.less"; } } diff --git a/plugins/SegmentEditor/images/scroller.png b/plugins/SegmentEditor/images/scroller.png Binary files differdeleted file mode 100644 index ddcab93426..0000000000 --- a/plugins/SegmentEditor/images/scroller.png +++ /dev/null diff --git a/plugins/SegmentEditor/images/up_arrow.png b/plugins/SegmentEditor/images/up_arrow.png Binary files differdeleted file mode 100644 index c7cb24c2b4..0000000000 --- a/plugins/SegmentEditor/images/up_arrow.png +++ /dev/null diff --git a/plugins/SegmentEditor/javascripts/jquery.jscrollpane.js b/plugins/SegmentEditor/javascripts/jquery.jscrollpane.js deleted file mode 100644 index 48d8be8537..0000000000 --- a/plugins/SegmentEditor/javascripts/jquery.jscrollpane.js +++ /dev/null @@ -1,1341 +0,0 @@ -/*! - * jScrollPane - v2.0.0beta12 - 2012-09-27 - * http://jscrollpane.kelvinluck.com/ - * - * Copyright (c) 2010 Kelvin Luck - * Dual licensed under the MIT or GPL licenses. - */ - -// Script: jScrollPane - cross browser customisable scrollbars -// -// *Version: 2.0.0beta12, Last updated: 2012-09-27* -// -// Project Home - http://jscrollpane.kelvinluck.com/ -// GitHub - http://github.com/vitch/jScrollPane -// Source - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js -// (Minified) - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js -// -// About: License -// -// Copyright (c) 2012 Kelvin Luck -// Dual licensed under the MIT or GPL Version 2 licenses. -// http://jscrollpane.kelvinluck.com/MIT-LICENSE.txt -// http://jscrollpane.kelvinluck.com/GPL-LICENSE.txt -// -// About: Examples -// -// All examples and demos are available through the jScrollPane example site at: -// http://jscrollpane.kelvinluck.com/ -// -// About: Support and Testing -// -// This plugin is tested on the browsers below and has been found to work reliably on them. If you run -// into a problem on one of the supported browsers then please visit the support section on the jScrollPane -// website (http://jscrollpane.kelvinluck.com/) for more information on getting support. You are also -// welcome to fork the project on GitHub if you can contribute a fix for a given issue. -// -// jQuery Versions - tested in 1.4.2+ - reported to work in 1.3.x -// Browsers Tested - Firefox 3.6.8, Safari 5, Opera 10.6, Chrome 5.0, IE 6, 7, 8 -// -// About: Release History -// -// 2.0.0beta12 - (2012-09-27) fix for jQuery 1.8+ -// 2.0.0beta11 - (2012-05-14) -// 2.0.0beta10 - (2011-04-17) cleaner required size calculation, improved keyboard support, stickToBottom/Left, other small fixes -// 2.0.0beta9 - (2011-01-31) new API methods, bug fixes and correct keyboard support for FF/OSX -// 2.0.0beta8 - (2011-01-29) touchscreen support, improved keyboard support -// 2.0.0beta7 - (2011-01-23) scroll speed consistent (thanks Aivo Paas) -// 2.0.0beta6 - (2010-12-07) scrollToElement horizontal support -// 2.0.0beta5 - (2010-10-18) jQuery 1.4.3 support, various bug fixes -// 2.0.0beta4 - (2010-09-17) clickOnTrack support, bug fixes -// 2.0.0beta3 - (2010-08-27) Horizontal mousewheel, mwheelIntent, keyboard support, bug fixes -// 2.0.0beta2 - (2010-08-21) Bug fixes -// 2.0.0beta1 - (2010-08-17) Rewrite to follow modern best practices and enable horizontal scrolling, initially hidden -// elements and dynamically sized elements. -// 1.x - (2006-12-31 - 2010-07-31) Initial version, hosted at googlecode, deprecated - -(function ($, window, undefined) { - - $.fn.jScrollPane = function (settings) { - // JScrollPane "class" - public methods are available through $('selector').data('jsp') - function JScrollPane(elem, s) { - var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight, - percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY, - verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition, - verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown, - horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight, - reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth, - wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false, - originalElement = elem.clone(false, false).empty(), - mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp'; - - originalPadding = elem.css('paddingTop') + ' ' + - elem.css('paddingRight') + ' ' + - elem.css('paddingBottom') + ' ' + - elem.css('paddingLeft'); - originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) + - (parseInt(elem.css('paddingRight'), 10) || 0); - - function initialise(s) { - - var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY, - hasContainingSpaceChanged, originalScrollTop, originalScrollLeft, - maintainAtBottom = false, maintainAtRight = false; - - settings = s; - - if (pane === undefined) { - originalScrollTop = elem.scrollTop(); - originalScrollLeft = elem.scrollLeft(); - - elem.css( - { - overflow: 'hidden', - padding: 0 - } - ); - // TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should - // come back to it later and check once it is unhidden... - paneWidth = elem.innerWidth(); - paneHeight = elem.innerHeight(); - - elem.width(paneWidth); - - pane = $('<div class="jspPane" />').css('padding', originalPadding).append(elem.children()); - container = $('<div class="jspContainer" />') - .css({ - 'width': paneWidth + 'px', - 'height': paneHeight + 'px' - } - ).append(pane).appendTo(elem); - - /* - // Move any margins from the first and last children up to the container so they can still - // collapse with neighbouring elements as they would before jScrollPane - firstChild = pane.find(':first-child'); - lastChild = pane.find(':last-child'); - elem.css( - { - 'margin-top': firstChild.css('margin-top'), - 'margin-bottom': lastChild.css('margin-bottom') - } - ); - firstChild.css('margin-top', 0); - lastChild.css('margin-bottom', 0); - */ - } else { - elem.css('width', ''); - - maintainAtBottom = settings.stickToBottom && isCloseToBottom(); - maintainAtRight = settings.stickToRight && isCloseToRight(); - - hasContainingSpaceChanged = elem.innerWidth() + originalPaddingTotalWidth != paneWidth || elem.outerHeight() != paneHeight; - - if (hasContainingSpaceChanged) { - paneWidth = elem.innerWidth() + originalPaddingTotalWidth; - paneHeight = elem.innerHeight(); - container.css({ - width: paneWidth + 'px', - height: paneHeight + 'px' - }); - } - - // If nothing changed since last check... - if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) { - elem.width(paneWidth); - return; - } - previousContentWidth = contentWidth; - - pane.css('width', ''); - elem.width(paneWidth); - - container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end(); - } - - pane.css('overflow', 'auto'); - if (s.contentWidth) { - contentWidth = s.contentWidth; - } else { - contentWidth = pane[0].scrollWidth; - } - contentHeight = pane[0].scrollHeight; - pane.css('overflow', ''); - - percentInViewH = contentWidth / paneWidth; - percentInViewV = contentHeight / paneHeight; - isScrollableV = percentInViewV > 1; - - isScrollableH = percentInViewH > 1; - - //console.log(paneWidth, paneHeight, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableH, isScrollableV); - - if (!(isScrollableH || isScrollableV)) { - elem.removeClass('jspScrollable'); - pane.css({ - top: 0, - width: container.width() - originalPaddingTotalWidth - }); - removeMousewheel(); - removeFocusHandler(); - removeKeyboardNav(); - removeClickOnTrack(); - } else { - elem.addClass('jspScrollable'); - - isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition); - if (isMaintainingPositon) { - lastContentX = contentPositionX(); - lastContentY = contentPositionY(); - } - - initialiseVerticalScroll(); - initialiseHorizontalScroll(); - resizeScrollbars(); - - if (isMaintainingPositon) { - scrollToX(maintainAtRight ? (contentWidth - paneWidth ) : lastContentX, false); - scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false); - } - - initFocusHandler(); - initMousewheel(); - initTouch(); - - if (settings.enableKeyboardNavigation) { - initKeyboardNav(); - } - if (settings.clickOnTrack) { - initClickOnTrack(); - } - - observeHash(); - if (settings.hijackInternalLinks) { - hijackInternalLinks(); - } - } - - if (settings.autoReinitialise && !reinitialiseInterval) { - reinitialiseInterval = setInterval( - function () { - initialise(settings); - }, - settings.autoReinitialiseDelay - ); - } else if (!settings.autoReinitialise && reinitialiseInterval) { - clearInterval(reinitialiseInterval); - } - - originalScrollTop && elem.scrollTop(0) && scrollToY(originalScrollTop, false); - originalScrollLeft && elem.scrollLeft(0) && scrollToX(originalScrollLeft, false); - - elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]); - } - - function initialiseVerticalScroll() { - if (isScrollableV) { - - container.append( - $('<div class="jspVerticalBar" />').append( - $('<div class="jspCap jspCapTop" />'), - $('<div class="jspTrack" />').append( - $('<div class="jspDrag" />').append( - $('<div class="jspDragTop" />'), - $('<div class="jspDragBottom" />') - ) - ), - $('<div class="jspCap jspCapBottom" />') - ) - ); - - verticalBar = container.find('>.jspVerticalBar'); - verticalTrack = verticalBar.find('>.jspTrack'); - verticalDrag = verticalTrack.find('>.jspDrag'); - - if (settings.showArrows) { - arrowUp = $('<a class="jspArrow jspArrowUp" />').bind( - 'mousedown.jsp', getArrowScroll(0, -1) - ).bind('click.jsp', nil); - arrowDown = $('<a class="jspArrow jspArrowDown" />').bind( - 'mousedown.jsp', getArrowScroll(0, 1) - ).bind('click.jsp', nil); - if (settings.arrowScrollOnHover) { - arrowUp.bind('mouseover.jsp', getArrowScroll(0, -1, arrowUp)); - arrowDown.bind('mouseover.jsp', getArrowScroll(0, 1, arrowDown)); - } - - appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown); - } - - verticalTrackHeight = paneHeight; - container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each( - function () { - verticalTrackHeight -= $(this).outerHeight(); - } - ); - - - verticalDrag.hover( - function () { - verticalDrag.addClass('jspHover'); - }, - function () { - verticalDrag.removeClass('jspHover'); - } - ).bind( - 'mousedown.jsp', - function (e) { - // Stop IE from allowing text selection - $('html').bind('dragstart.jsp selectstart.jsp', nil); - - verticalDrag.addClass('jspActive'); - - var startY = e.pageY - verticalDrag.position().top; - - $('html').bind( - 'mousemove.jsp', - function (e) { - positionDragY(e.pageY - startY, false); - } - ).bind('mouseup.jsp mouseleave.jsp', cancelDrag); - return false; - } - ); - sizeVerticalScrollbar(); - } - } - - function sizeVerticalScrollbar() { - verticalTrack.height(verticalTrackHeight + 'px'); - verticalDragPosition = 0; - scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth(); - - // Make the pane thinner to allow for the vertical scrollbar - pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth); - - // Add margin to the left of the pane if scrollbars are on that side (to position - // the scrollbar on the left or right set it's left or right property in CSS) - try { - if (verticalBar.position().left === 0) { - pane.css('margin-left', scrollbarWidth + 'px'); - } - } catch (err) { - } - } - - function initialiseHorizontalScroll() { - if (isScrollableH) { - - container.append( - $('<div class="jspHorizontalBar" />').append( - $('<div class="jspCap jspCapLeft" />'), - $('<div class="jspTrack" />').append( - $('<div class="jspDrag" />').append( - $('<div class="jspDragLeft" />'), - $('<div class="jspDragRight" />') - ) - ), - $('<div class="jspCap jspCapRight" />') - ) - ); - - horizontalBar = container.find('>.jspHorizontalBar'); - horizontalTrack = horizontalBar.find('>.jspTrack'); - horizontalDrag = horizontalTrack.find('>.jspDrag'); - - if (settings.showArrows) { - arrowLeft = $('<a class="jspArrow jspArrowLeft" />').bind( - 'mousedown.jsp', getArrowScroll(-1, 0) - ).bind('click.jsp', nil); - arrowRight = $('<a class="jspArrow jspArrowRight" />').bind( - 'mousedown.jsp', getArrowScroll(1, 0) - ).bind('click.jsp', nil); - if (settings.arrowScrollOnHover) { - arrowLeft.bind('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft)); - arrowRight.bind('mouseover.jsp', getArrowScroll(1, 0, arrowRight)); - } - appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight); - } - - horizontalDrag.hover( - function () { - horizontalDrag.addClass('jspHover'); - }, - function () { - horizontalDrag.removeClass('jspHover'); - } - ).bind( - 'mousedown.jsp', - function (e) { - // Stop IE from allowing text selection - $('html').bind('dragstart.jsp selectstart.jsp', nil); - - horizontalDrag.addClass('jspActive'); - - var startX = e.pageX - horizontalDrag.position().left; - - $('html').bind( - 'mousemove.jsp', - function (e) { - positionDragX(e.pageX - startX, false); - } - ).bind('mouseup.jsp mouseleave.jsp', cancelDrag); - return false; - } - ); - horizontalTrackWidth = container.innerWidth(); - sizeHorizontalScrollbar(); - } - } - - function sizeHorizontalScrollbar() { - container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each( - function () { - horizontalTrackWidth -= $(this).outerWidth(); - } - ); - - horizontalTrack.width(horizontalTrackWidth + 'px'); - horizontalDragPosition = 0; - } - - function resizeScrollbars() { - if (isScrollableH && isScrollableV) { - var horizontalTrackHeight = horizontalTrack.outerHeight(), - verticalTrackWidth = verticalTrack.outerWidth(); - verticalTrackHeight -= horizontalTrackHeight; - $(horizontalBar).find('>.jspCap:visible,>.jspArrow').each( - function () { - horizontalTrackWidth += $(this).outerWidth(); - } - ); - horizontalTrackWidth -= verticalTrackWidth; - paneHeight -= verticalTrackWidth; - paneWidth -= horizontalTrackHeight; - horizontalTrack.parent().append( - $('<div class="jspCorner" />').css('width', horizontalTrackHeight + 'px') - ); - sizeVerticalScrollbar(); - sizeHorizontalScrollbar(); - } - // reflow content - if (isScrollableH) { - pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px'); - } - contentHeight = pane.outerHeight(); - percentInViewV = contentHeight / paneHeight; - - if (isScrollableH) { - horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth); - if (horizontalDragWidth > settings.horizontalDragMaxWidth) { - horizontalDragWidth = settings.horizontalDragMaxWidth; - } else if (horizontalDragWidth < settings.horizontalDragMinWidth) { - horizontalDragWidth = settings.horizontalDragMinWidth; - } - horizontalDrag.width(horizontalDragWidth + 'px'); - dragMaxX = horizontalTrackWidth - horizontalDragWidth; - _positionDragX(horizontalDragPosition); // To update the state for the arrow buttons - } - if (isScrollableV) { - verticalDragHeight = Math.ceil(1 / percentInViewV * verticalTrackHeight); - if (verticalDragHeight > settings.verticalDragMaxHeight) { - verticalDragHeight = settings.verticalDragMaxHeight; - } else if (verticalDragHeight < settings.verticalDragMinHeight) { - verticalDragHeight = settings.verticalDragMinHeight; - } - verticalDrag.height(verticalDragHeight + 'px'); - dragMaxY = verticalTrackHeight - verticalDragHeight; - _positionDragY(verticalDragPosition); // To update the state for the arrow buttons - } - } - - function appendArrows(ele, p, a1, a2) { - var p1 = "before", p2 = "after", aTemp; - - // Sniff for mac... Is there a better way to determine whether the arrows would naturally appear - // at the top or the bottom of the bar? - if (p == "os") { - p = /Mac/.test(navigator.platform) ? "after" : "split"; - } - if (p == p1) { - p2 = p; - } else if (p == p2) { - p1 = p; - aTemp = a1; - a1 = a2; - a2 = aTemp; - } - - ele[p1](a1)[p2](a2); - } - - function getArrowScroll(dirX, dirY, ele) { - return function () { - arrowScroll(dirX, dirY, this, ele); - this.blur(); - return false; - }; - } - - function arrowScroll(dirX, dirY, arrow, ele) { - arrow = $(arrow).addClass('jspActive'); - - var eve, - scrollTimeout, - isFirst = true, - doScroll = function () { - if (dirX !== 0) { - jsp.scrollByX(dirX * settings.arrowButtonSpeed); - } - if (dirY !== 0) { - jsp.scrollByY(dirY * settings.arrowButtonSpeed); - } - scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq); - isFirst = false; - }; - - doScroll(); - - eve = ele ? 'mouseout.jsp' : 'mouseup.jsp'; - ele = ele || $('html'); - ele.bind( - eve, - function () { - arrow.removeClass('jspActive'); - scrollTimeout && clearTimeout(scrollTimeout); - scrollTimeout = null; - ele.unbind(eve); - } - ); - } - - function initClickOnTrack() { - removeClickOnTrack(); - if (isScrollableV) { - verticalTrack.bind( - 'mousedown.jsp', - function (e) { - if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) { - var clickedTrack = $(this), - offset = clickedTrack.offset(), - direction = e.pageY - offset.top - verticalDragPosition, - scrollTimeout, - isFirst = true, - doScroll = function () { - var offset = clickedTrack.offset(), - pos = e.pageY - offset.top - verticalDragHeight / 2, - contentDragY = paneHeight * settings.scrollPagePercent, - dragY = dragMaxY * contentDragY / (contentHeight - paneHeight); - if (direction < 0) { - if (verticalDragPosition - dragY > pos) { - jsp.scrollByY(-contentDragY); - } else { - positionDragY(pos); - } - } else if (direction > 0) { - if (verticalDragPosition + dragY < pos) { - jsp.scrollByY(contentDragY); - } else { - positionDragY(pos); - } - } else { - cancelClick(); - return; - } - scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq); - isFirst = false; - }, - cancelClick = function () { - scrollTimeout && clearTimeout(scrollTimeout); - scrollTimeout = null; - $(document).unbind('mouseup.jsp', cancelClick); - }; - doScroll(); - $(document).bind('mouseup.jsp', cancelClick); - return false; - } - } - ); - } - - if (isScrollableH) { - horizontalTrack.bind( - 'mousedown.jsp', - function (e) { - if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) { - var clickedTrack = $(this), - offset = clickedTrack.offset(), - direction = e.pageX - offset.left - horizontalDragPosition, - scrollTimeout, - isFirst = true, - doScroll = function () { - var offset = clickedTrack.offset(), - pos = e.pageX - offset.left - horizontalDragWidth / 2, - contentDragX = paneWidth * settings.scrollPagePercent, - dragX = dragMaxX * contentDragX / (contentWidth - paneWidth); - if (direction < 0) { - if (horizontalDragPosition - dragX > pos) { - jsp.scrollByX(-contentDragX); - } else { - positionDragX(pos); - } - } else if (direction > 0) { - if (horizontalDragPosition + dragX < pos) { - jsp.scrollByX(contentDragX); - } else { - positionDragX(pos); - } - } else { - cancelClick(); - return; - } - scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq); - isFirst = false; - }, - cancelClick = function () { - scrollTimeout && clearTimeout(scrollTimeout); - scrollTimeout = null; - $(document).unbind('mouseup.jsp', cancelClick); - }; - doScroll(); - $(document).bind('mouseup.jsp', cancelClick); - return false; - } - } - ); - } - } - - function removeClickOnTrack() { - if (horizontalTrack) { - horizontalTrack.unbind('mousedown.jsp'); - } - if (verticalTrack) { - verticalTrack.unbind('mousedown.jsp'); - } - } - - function cancelDrag() { - $('html').unbind('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp'); - - if (verticalDrag) { - verticalDrag.removeClass('jspActive'); - } - if (horizontalDrag) { - horizontalDrag.removeClass('jspActive'); - } - } - - function positionDragY(destY, animate) { - if (!isScrollableV) { - return; - } - if (destY < 0) { - destY = 0; - } else if (destY > dragMaxY) { - destY = dragMaxY; - } - - // can't just check if(animate) because false is a valid value that could be passed in... - if (animate === undefined) { - animate = settings.animateScroll; - } - if (animate) { - jsp.animate(verticalDrag, 'top', destY, _positionDragY); - } else { - verticalDrag.css('top', destY); - _positionDragY(destY); - } - - } - - function _positionDragY(destY) { - if (destY === undefined) { - destY = verticalDrag.position().top; - } - - container.scrollTop(0); - verticalDragPosition = destY; - - var isAtTop = verticalDragPosition === 0, - isAtBottom = verticalDragPosition == dragMaxY, - percentScrolled = destY / dragMaxY, - destTop = -percentScrolled * (contentHeight - paneHeight); - - if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) { - wasAtTop = isAtTop; - wasAtBottom = isAtBottom; - elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]); - } - - updateVerticalArrows(isAtTop, isAtBottom); - pane.css('top', destTop); - elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll'); - } - - function positionDragX(destX, animate) { - if (!isScrollableH) { - return; - } - if (destX < 0) { - destX = 0; - } else if (destX > dragMaxX) { - destX = dragMaxX; - } - - if (animate === undefined) { - animate = settings.animateScroll; - } - if (animate) { - jsp.animate(horizontalDrag, 'left', destX, _positionDragX); - } else { - horizontalDrag.css('left', destX); - _positionDragX(destX); - } - } - - function _positionDragX(destX) { - if (destX === undefined) { - destX = horizontalDrag.position().left; - } - - container.scrollTop(0); - horizontalDragPosition = destX; - - var isAtLeft = horizontalDragPosition === 0, - isAtRight = horizontalDragPosition == dragMaxX, - percentScrolled = destX / dragMaxX, - destLeft = -percentScrolled * (contentWidth - paneWidth); - - if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) { - wasAtLeft = isAtLeft; - wasAtRight = isAtRight; - elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]); - } - - updateHorizontalArrows(isAtLeft, isAtRight); - pane.css('left', destLeft); - elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll'); - } - - function updateVerticalArrows(isAtTop, isAtBottom) { - if (settings.showArrows) { - arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled'); - arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled'); - } - } - - function updateHorizontalArrows(isAtLeft, isAtRight) { - if (settings.showArrows) { - arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled'); - arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled'); - } - } - - function scrollToY(destY, animate) { - var percentScrolled = destY / (contentHeight - paneHeight); - positionDragY(percentScrolled * dragMaxY, animate); - } - - function scrollToX(destX, animate) { - var percentScrolled = destX / (contentWidth - paneWidth); - positionDragX(percentScrolled * dragMaxX, animate); - } - - function scrollToElement(ele, stickToTop, animate) { - var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX; - - // Legal hash values aren't necessarily legal jQuery selectors so we need to catch any - // errors from the lookup... - try { - e = $(ele); - } catch (err) { - return; - } - eleHeight = e.outerHeight(); - eleWidth = e.outerWidth(); - - container.scrollTop(0); - container.scrollLeft(0); - - // loop through parents adding the offset top of any elements that are relatively positioned between - // the focused element and the jspPane so we can get the true distance from the top - // of the focused element to the top of the scrollpane... - while (!e.is('.jspPane')) { - eleTop += e.position().top; - eleLeft += e.position().left; - e = e.offsetParent(); - if (/^body|html$/i.test(e[0].nodeName)) { - // we ended up too high in the document structure. Quit! - return; - } - } - - viewportTop = contentPositionY(); - maxVisibleEleTop = viewportTop + paneHeight; - if (eleTop < viewportTop || stickToTop) { // element is above viewport - destY = eleTop - settings.verticalGutter; - } else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport - destY = eleTop - paneHeight + eleHeight + settings.verticalGutter; - } - if (destY) { - scrollToY(destY, animate); - } - - viewportLeft = contentPositionX(); - maxVisibleEleLeft = viewportLeft + paneWidth; - if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport - destX = eleLeft - settings.horizontalGutter; - } else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport - destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter; - } - if (destX) { - scrollToX(destX, animate); - } - - } - - function contentPositionX() { - return -pane.position().left; - } - - function contentPositionY() { - return -pane.position().top; - } - - function isCloseToBottom() { - var scrollableHeight = contentHeight - paneHeight; - return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10); - } - - function isCloseToRight() { - var scrollableWidth = contentWidth - paneWidth; - return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10); - } - - function initMousewheel() { - container.unbind(mwEvent).bind( - mwEvent, - function (event, delta, deltaX, deltaY) { - var dX = horizontalDragPosition, dY = verticalDragPosition; - jsp.scrollBy(deltaX * settings.mouseWheelSpeed, -deltaY * settings.mouseWheelSpeed, false); - // return true if there was no movement so rest of screen can scroll - return dX == horizontalDragPosition && dY == verticalDragPosition; - } - ); - } - - function removeMousewheel() { - container.unbind(mwEvent); - } - - function nil() { - return false; - } - - function initFocusHandler() { - pane.find(':input,a').unbind('focus.jsp').bind( - 'focus.jsp', - function (e) { - scrollToElement(e.target, false); - } - ); - } - - function removeFocusHandler() { - pane.find(':input,a').unbind('focus.jsp'); - } - - function initKeyboardNav() { - var keyDown, elementHasScrolled, validParents = []; - isScrollableH && validParents.push(horizontalBar[0]); - isScrollableV && validParents.push(verticalBar[0]); - - // IE also focuses elements that don't have tabindex set. - pane.focus( - function () { - elem.focus(); - } - ); - - elem.attr('tabindex', 0) - .unbind('keydown.jsp keypress.jsp') - .bind( - 'keydown.jsp', - function (e) { - if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)) { - return; - } - var dX = horizontalDragPosition, dY = verticalDragPosition; - switch (e.keyCode) { - case 40: // down - case 38: // up - case 34: // page down - case 32: // space - case 33: // page up - case 39: // right - case 37: // left - keyDown = e.keyCode; - keyDownHandler(); - break; - case 35: // end - scrollToY(contentHeight - paneHeight); - keyDown = null; - break; - case 36: // home - scrollToY(0); - keyDown = null; - break; - } - - elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition; - return !elementHasScrolled; - } - ).bind( - 'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls... - function (e) { - if (e.keyCode == keyDown) { - keyDownHandler(); - } - return !elementHasScrolled; - } - ); - - if (settings.hideFocus) { - elem.css('outline', 'none'); - if ('hideFocus' in container[0]) { - elem.attr('hideFocus', true); - } - } else { - elem.css('outline', ''); - if ('hideFocus' in container[0]) { - elem.attr('hideFocus', false); - } - } - - function keyDownHandler() { - var dX = horizontalDragPosition, dY = verticalDragPosition; - switch (keyDown) { - case 40: // down - jsp.scrollByY(settings.keyboardSpeed, false); - break; - case 38: // up - jsp.scrollByY(-settings.keyboardSpeed, false); - break; - case 34: // page down - case 32: // space - jsp.scrollByY(paneHeight * settings.scrollPagePercent, false); - break; - case 33: // page up - jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false); - break; - case 39: // right - jsp.scrollByX(settings.keyboardSpeed, false); - break; - case 37: // left - jsp.scrollByX(-settings.keyboardSpeed, false); - break; - } - - elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition; - return elementHasScrolled; - } - } - - function removeKeyboardNav() { - elem.attr('tabindex', '-1') - .removeAttr('tabindex') - .unbind('keydown.jsp keypress.jsp'); - } - - function observeHash() { - if (location.hash && location.hash.length > 1) { - var e, - retryInt, - hash = escape(location.hash.substr(1)) // hash must be escaped to prevent XSS - ; - try { - e = $('#' + hash + ', a[name="' + hash + '"]'); - } catch (err) { - return; - } - - if (e.length && pane.find(hash)) { - // nasty workaround but it appears to take a little while before the hash has done its thing - // to the rendered page so we just wait until the container's scrollTop has been messed up. - if (container.scrollTop() === 0) { - retryInt = setInterval( - function () { - if (container.scrollTop() > 0) { - scrollToElement(e, true); - $(document).scrollTop(container.position().top); - clearInterval(retryInt); - } - }, - 50 - ); - } else { - scrollToElement(e, true); - $(document).scrollTop(container.position().top); - } - } - } - } - - function hijackInternalLinks() { - // only register the link handler once - if ($(document.body).data('jspHijack')) { - return; - } - - // remember that the handler was bound - $(document.body).data('jspHijack', true); - - // use live handler to also capture newly created links - $(document.body).delegate('a[href*=#]', 'click', function (event) { - // does the link point to the same page? - // this also takes care of cases with a <base>-Tag or Links not starting with the hash # - // e.g. <a href="index.html#test"> when the current url already is index.html - var href = this.href.substr(0, this.href.indexOf('#')), - locationHref = location.href, - hash, - element, - container, - jsp, - scrollTop, - elementTop; - if (location.href.indexOf('#') !== -1) { - locationHref = location.href.substr(0, location.href.indexOf('#')); - } - if (href !== locationHref) { - // the link points to another page - return; - } - - // check if jScrollPane should handle this click event - hash = escape(this.href.substr(this.href.indexOf('#') + 1)); - - // find the element on the page - try { - element = $('#' + hash + ', a[name="' + hash + '"]'); - } catch (e) { - // hash is not a valid jQuery identifier - return; - } - - if (!element.length) { - // this link does not point to an element on this page - return; - } - - container = element.closest('.jspScrollable'); - jsp = container.data('jsp'); - - // jsp might be another jsp instance than the one, that bound this event - // remember: this event is only bound once for all instances. - jsp.scrollToElement(element, true); - - if (container[0].scrollIntoView) { - // also scroll to the top of the container (if it is not visible) - scrollTop = $(window).scrollTop(); - elementTop = element.offset().top; - if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) { - container[0].scrollIntoView(); - } - } - - // jsp handled this event, prevent the browser default (scrolling :P) - event.preventDefault(); - }); - } - - // Init touch on iPad, iPhone, iPod, Android - function initTouch() { - var startX, - startY, - touchStartX, - touchStartY, - moved, - moving = false; - - container.unbind('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').bind( - 'touchstart.jsp', - function (e) { - var touch = e.originalEvent.touches[0]; - startX = contentPositionX(); - startY = contentPositionY(); - touchStartX = touch.pageX; - touchStartY = touch.pageY; - moved = false; - moving = true; - } - ).bind( - 'touchmove.jsp', - function (ev) { - if (!moving) { - return; - } - - var touchPos = ev.originalEvent.touches[0], - dX = horizontalDragPosition, dY = verticalDragPosition; - - jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY); - - moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5; - - // return true if there was no movement so rest of screen can scroll - return dX == horizontalDragPosition && dY == verticalDragPosition; - } - ).bind( - 'touchend.jsp', - function (e) { - moving = false; - /*if(moved) { - return false; - }*/ - } - ).bind( - 'click.jsp-touchclick', - function (e) { - if (moved) { - moved = false; - return false; - } - } - ); - } - - function destroy() { - var currentY = contentPositionY(), - currentX = contentPositionX(); - elem.removeClass('jspScrollable').unbind('.jsp'); - elem.replaceWith(originalElement.append(pane.children())); - originalElement.scrollTop(currentY); - originalElement.scrollLeft(currentX); - - // clear reinitialize timer if active - if (reinitialiseInterval) { - clearInterval(reinitialiseInterval); - } - } - - // Public API - $.extend( - jsp, - { - // Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it - // was initialised). The settings object which is passed in will override any settings from the - // previous time it was initialised - if you don't pass any settings then the ones from the previous - // initialisation will be used. - reinitialise: function (s) { - s = $.extend({}, settings, s); - initialise(s); - }, - // Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so - // that it can be seen within the viewport. If stickToTop is true then the element will appear at - // the top of the viewport, if it is false then the viewport will scroll as little as possible to - // show the element. You can also specify if you want animation to occur. If you don't provide this - // argument then the animateScroll value from the settings object is used instead. - scrollToElement: function (ele, stickToTop, animate) { - scrollToElement(ele, stickToTop, animate); - }, - // Scrolls the pane so that the specified co-ordinates within the content are at the top left - // of the viewport. animate is optional and if not passed then the value of animateScroll from - // the settings object this jScrollPane was initialised with is used. - scrollTo: function (destX, destY, animate) { - scrollToX(destX, animate); - scrollToY(destY, animate); - }, - // Scrolls the pane so that the specified co-ordinate within the content is at the left of the - // viewport. animate is optional and if not passed then the value of animateScroll from the settings - // object this jScrollPane was initialised with is used. - scrollToX: function (destX, animate) { - scrollToX(destX, animate); - }, - // Scrolls the pane so that the specified co-ordinate within the content is at the top of the - // viewport. animate is optional and if not passed then the value of animateScroll from the settings - // object this jScrollPane was initialised with is used. - scrollToY: function (destY, animate) { - scrollToY(destY, animate); - }, - // Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate - // is optional and if not passed then the value of animateScroll from the settings object this - // jScrollPane was initialised with is used. - scrollToPercentX: function (destPercentX, animate) { - scrollToX(destPercentX * (contentWidth - paneWidth), animate); - }, - // Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate - // is optional and if not passed then the value of animateScroll from the settings object this - // jScrollPane was initialised with is used. - scrollToPercentY: function (destPercentY, animate) { - scrollToY(destPercentY * (contentHeight - paneHeight), animate); - }, - // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then - // the value of animateScroll from the settings object this jScrollPane was initialised with is used. - scrollBy: function (deltaX, deltaY, animate) { - jsp.scrollByX(deltaX, animate); - jsp.scrollByY(deltaY, animate); - }, - // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then - // the value of animateScroll from the settings object this jScrollPane was initialised with is used. - scrollByX: function (deltaX, animate) { - var destX = contentPositionX() + Math[deltaX < 0 ? 'floor' : 'ceil'](deltaX), - percentScrolled = destX / (contentWidth - paneWidth); - positionDragX(percentScrolled * dragMaxX, animate); - }, - // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then - // the value of animateScroll from the settings object this jScrollPane was initialised with is used. - scrollByY: function (deltaY, animate) { - var destY = contentPositionY() + Math[deltaY < 0 ? 'floor' : 'ceil'](deltaY), - percentScrolled = destY / (contentHeight - paneHeight); - positionDragY(percentScrolled * dragMaxY, animate); - }, - // Positions the horizontal drag at the specified x position (and updates the viewport to reflect - // this). animate is optional and if not passed then the value of animateScroll from the settings - // object this jScrollPane was initialised with is used. - positionDragX: function (x, animate) { - positionDragX(x, animate); - }, - // Positions the vertical drag at the specified y position (and updates the viewport to reflect - // this). animate is optional and if not passed then the value of animateScroll from the settings - // object this jScrollPane was initialised with is used. - positionDragY: function (y, animate) { - positionDragY(y, animate); - }, - // This method is called when jScrollPane is trying to animate to a new position. You can override - // it if you want to provide advanced animation functionality. It is passed the following arguments: - // * ele - the element whose position is being animated - // * prop - the property that is being animated - // * value - the value it's being animated to - // * stepCallback - a function that you must execute each time you update the value of the property - // You can use the default implementation (below) as a starting point for your own implementation. - animate: function (ele, prop, value, stepCallback) { - var params = {}; - params[prop] = value; - ele.animate( - params, - { - 'duration': settings.animateDuration, - 'easing': settings.animateEase, - 'queue': false, - 'step': stepCallback - } - ); - }, - // Returns the current x position of the viewport with regards to the content pane. - getContentPositionX: function () { - return contentPositionX(); - }, - // Returns the current y position of the viewport with regards to the content pane. - getContentPositionY: function () { - return contentPositionY(); - }, - // Returns the width of the content within the scroll pane. - getContentWidth: function () { - return contentWidth; - }, - // Returns the height of the content within the scroll pane. - getContentHeight: function () { - return contentHeight; - }, - // Returns the horizontal position of the viewport within the pane content. - getPercentScrolledX: function () { - return contentPositionX() / (contentWidth - paneWidth); - }, - // Returns the vertical position of the viewport within the pane content. - getPercentScrolledY: function () { - return contentPositionY() / (contentHeight - paneHeight); - }, - // Returns whether or not this scrollpane has a horizontal scrollbar. - getIsScrollableH: function () { - return isScrollableH; - }, - // Returns whether or not this scrollpane has a vertical scrollbar. - getIsScrollableV: function () { - return isScrollableV; - }, - // Gets a reference to the content pane. It is important that you use this method if you want to - // edit the content of your jScrollPane as if you access the element directly then you may have some - // problems (as your original element has had additional elements for the scrollbars etc added into - // it). - getContentPane: function () { - return pane; - }, - // Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the - // animateScroll value from settings is used instead. - scrollToBottom: function (animate) { - positionDragY(dragMaxY, animate); - }, - // Hijacks the links on the page which link to content inside the scrollpane. If you have changed - // the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the - // contents of your scroll pane will work then call this function. - hijackInternalLinks: $.noop, - // Removes the jScrollPane and returns the page to the state it was in before jScrollPane was - // initialised. - destroy: function () { - destroy(); - } - } - ); - - initialise(s); - } - - // Pluginifying code... - settings = $.extend({}, $.fn.jScrollPane.defaults, settings); - - // Apply default speed - $.each(['mouseWheelSpeed', 'arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function () { - settings[this] = settings[this] || settings.speed; - }); - - return this.each( - function () { - var elem = $(this), jspApi = elem.data('jsp'); - if (jspApi) { - jspApi.reinitialise(settings); - } else { - $("script", elem).filter('[type="text/javascript"],:not([type])').remove(); - jspApi = new JScrollPane(elem, settings); - elem.data('jsp', jspApi); - } - } - ); - }; - - $.fn.jScrollPane.defaults = { - showArrows: false, - maintainPosition: true, - stickToBottom: false, - stickToRight: false, - clickOnTrack: true, - autoReinitialise: false, - autoReinitialiseDelay: 500, - verticalDragMinHeight: 0, - verticalDragMaxHeight: 99999, - horizontalDragMinWidth: 0, - horizontalDragMaxWidth: 99999, - contentWidth: undefined, - animateScroll: false, - animateDuration: 300, - animateEase: 'linear', - hijackInternalLinks: false, - verticalGutter: 4, - horizontalGutter: 4, - mouseWheelSpeed: 0, - arrowButtonSpeed: 0, - arrowRepeatFreq: 50, - arrowScrollOnHover: false, - trackClickSpeed: 0, - trackClickRepeatFreq: 70, - verticalArrowPositions: 'split', - horizontalArrowPositions: 'split', - enableKeyboardNavigation: true, - hideFocus: false, - keyboardSpeed: 0, - initialDelay: 300, // Delay before starting repeating - speed: 30, // Default speed when others falsey - scrollPagePercent: .8 // Percent of visible area scrolled when pageUp/Down or track area pressed - }; - -})(jQuery, this); diff --git a/plugins/SegmentEditor/javascripts/jquery.mousewheel.js b/plugins/SegmentEditor/javascripts/jquery.mousewheel.js deleted file mode 100644 index 9d65c7162b..0000000000 --- a/plugins/SegmentEditor/javascripts/jquery.mousewheel.js +++ /dev/null @@ -1,117 +0,0 @@ -/*! Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net) - * Licensed under the MIT License (LICENSE.txt). - * - * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. - * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. - * Thanks to: Seamus Leahy for adding deltaX and deltaY - * - * Version: 3.1.3 - * - * Requires: 1.2.2+ - */ - -(function (factory) { - if ( typeof define === 'function' && define.amd ) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS style for Browserify - module.exports = factory; - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - - var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll']; - var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll']; - var lowestDelta, lowestDeltaXY; - - if ( $.event.fixHooks ) { - for ( var i = toFix.length; i; ) { - $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks; - } - } - - $.event.special.mousewheel = { - setup: function() { - if ( this.addEventListener ) { - for ( var i = toBind.length; i; ) { - this.addEventListener( toBind[--i], handler, false ); - } - } else { - this.onmousewheel = handler; - } - }, - - teardown: function() { - if ( this.removeEventListener ) { - for ( var i = toBind.length; i; ) { - this.removeEventListener( toBind[--i], handler, false ); - } - } else { - this.onmousewheel = null; - } - } - }; - - $.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, - absDeltaXY = 0, - fn; - event = $.event.fix(orgEvent); - event.type = "mousewheel"; - - // Old school scrollwheel delta - if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta; } - if ( orgEvent.detail ) { delta = orgEvent.detail * -1; } - - // New school wheel delta (wheel event) - if ( orgEvent.deltaY ) { - deltaY = orgEvent.deltaY * -1; - delta = deltaY; - } - if ( orgEvent.deltaX ) { - deltaX = orgEvent.deltaX; - delta = deltaX * -1; - } - - // Webkit - if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY; } - if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = orgEvent.wheelDeltaX * -1; } - - // Look for lowest delta to normalize the delta values - absDelta = Math.abs(delta); - if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; } - absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX)); - if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; } - - // Get a whole value for the deltas - fn = delta > 0 ? 'floor' : 'ceil'; - delta = Math[fn](delta / lowestDelta); - deltaX = Math[fn](deltaX / lowestDeltaXY); - deltaY = Math[fn](deltaY / lowestDeltaXY); - - // Add event and delta to the front of the arguments - args.unshift(event, delta, deltaX, deltaY); - - return ($.event.dispatch || $.event.handle).apply(this, args); - } - -})); diff --git a/plugins/SegmentEditor/javascripts/mwheelIntent.js b/plugins/SegmentEditor/javascripts/mwheelIntent.js deleted file mode 100644 index 72b7d135ca..0000000000 --- a/plugins/SegmentEditor/javascripts/mwheelIntent.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @author trixta - * @version 1.2 - */ -(function($){ - - var mwheelI = { - pos: [-260, -260] - }, - minDif = 3, - doc = document, - root = doc.documentElement, - body = doc.body, - longDelay, shortDelay - ; - - function unsetPos(){ - if(this === mwheelI.elem){ - mwheelI.pos = [-260, -260]; - mwheelI.elem = false; - minDif = 3; - } - } - - $.event.special.mwheelIntent = { - setup: function(){ - var jElm = $(this).bind('mousewheel', $.event.special.mwheelIntent.handler); - if( this !== doc && this !== root && this !== body ){ - jElm.bind('mouseleave', unsetPos); - } - jElm = null; - return true; - }, - teardown: function(){ - $(this) - .unbind('mousewheel', $.event.special.mwheelIntent.handler) - .unbind('mouseleave', unsetPos) - ; - return true; - }, - handler: function(e, d){ - var pos = [e.clientX, e.clientY]; - if( this === mwheelI.elem || Math.abs(mwheelI.pos[0] - pos[0]) > minDif || Math.abs(mwheelI.pos[1] - pos[1]) > minDif ){ - mwheelI.elem = this; - mwheelI.pos = pos; - minDif = 250; - - clearTimeout(shortDelay); - shortDelay = setTimeout(function(){ - minDif = 10; - }, 200); - clearTimeout(longDelay); - longDelay = setTimeout(function(){ - minDif = 3; - }, 1500); - e = $.extend({}, e, {type: 'mwheelIntent'}); - return $.event.dispatch.apply(this, arguments); - } - } - }; - $.fn.extend({ - mwheelIntent: function(fn) { - return fn ? this.bind("mwheelIntent", fn) : this.trigger("mwheelIntent"); - }, - - unmwheelIntent: function(fn) { - return this.unbind("mwheelIntent", fn); - } - }); - - $(function(){ - body = doc.body; - //assume that document is always scrollable, doesn't hurt if not - $(doc).bind('mwheelIntent.mwheelIntentDefault', $.noop); - }); -})(jQuery); diff --git a/plugins/SegmentEditor/stylesheets/jquery.jscrollpane.css b/plugins/SegmentEditor/stylesheets/jquery.jscrollpane.css deleted file mode 100644 index 0364216fe0..0000000000 --- a/plugins/SegmentEditor/stylesheets/jquery.jscrollpane.css +++ /dev/null @@ -1,119 +0,0 @@ -/* - * CSS Styles that are needed by jScrollPane for it to operate correctly. - * - * Include this stylesheet in your site or copy and paste the styles below into your stylesheet - jScrollPane - * may not operate correctly without them. - */ - -.jspContainer -{ - overflow: hidden; - position: relative; -} - -.jspPane -{ - position: absolute; -} - -.jspVerticalBar -{ - position: absolute; - top: 0; - right: 0; - width: 8px; - height: 100%; - background: red; -} - -.jspHorizontalBar -{ - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 16px; - background: red; -} - -.jspVerticalBar *, -.jspHorizontalBar * -{ - margin: 0; - padding: 0; -} - -.jspCap -{ - display: none; -} - -.jspHorizontalBar .jspCap -{ - float: left; -} - -.jspTrack -{ - background: #dde; - position: relative; -} - -.jspDrag -{ - background: #bbd; - position: relative; - top: 0; - left: 0; - cursor: pointer; -} - -.jspHorizontalBar .jspTrack, -.jspHorizontalBar .jspDrag -{ - float: left; - height: 100%; -} - -.jspArrow -{ - background: #50506d; - text-indent: -20000px; - display: block; - cursor: pointer; -} - -.jspArrow.jspDisabled -{ - cursor: default; -} - -.jspVerticalBar .jspArrow -{ - height: 16px; -} - -.jspHorizontalBar .jspArrow -{ - width: 16px; - float: left; - height: 100%; -} - -.jspVerticalBar .jspArrow:focus -{ - outline: none; -} - -.jspCorner -{ - background: #eeeef4; - float: left; - height: 100%; -} - -/* Yuk! CSS Hack for IE6 3 pixel bug :( */ -* html .jspCorner -{ - margin: 0 -3px 0 0; -}
\ No newline at end of file diff --git a/plugins/SegmentEditor/stylesheets/scroll.less b/plugins/SegmentEditor/stylesheets/scroll.less deleted file mode 100644 index 23c0402e15..0000000000 --- a/plugins/SegmentEditor/stylesheets/scroll.less +++ /dev/null @@ -1,140 +0,0 @@ -/* - * CSS Styles that are needed by jScrollPane for it to operate correctly. - * - * Include this stylesheet in your site or copy and paste the styles below into your stylesheet - jScrollPane - * may not operate correctly without them. - */ - -.jspContainer -{ - /*overflow: hidden;*/ - position: relative; -} - -.jspPane -{ - position: absolute; -} - -.jspVerticalBar -{ - position: absolute; - top: 0; - right: 0; - width: 16px; - height: 100%; -} - -.jspHorizontalBar -{ - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 16px; - background: red; -} - -.jspVerticalBar *, -.jspHorizontalBar * -{ - margin: 0; - padding: 0; -} - -.jspCap -{ - display: none; -} - -.jspHorizontalBar .jspCap -{ - float: left; -} - -.jspTrack -{ - background: url("plugins/SegmentEditor/images/slide.png") transparent no-repeat 7px; - position: relative; - background-size: 20% 100%; - /*height: 447px!important;*/ -} - -.jspDrag -{ - background: url("plugins/SegmentEditor/images/scroller.png") transparent no-repeat; - background-size: 100% 100%; - width: 17px; - position: relative; - top: 0; - left: 0; - cursor: pointer; -} - -.jspHorizontalBar .jspTrack, -.jspHorizontalBar .jspDrag -{ - float: left; - height: 100%; -} - -.jspArrow -{ - - text-indent: -20000px; - display: block; - cursor: pointer; -} -.jspArrowDown{ - background: url("plugins/SegmentEditor/images/down_arrow.png") transparent no-repeat; -} -.jspArrowUp{ - background: url("plugins/SegmentEditor/images/up_arrow.png") transparent no-repeat; -} - -.jspVerticalBar .jspArrow -{ - height: 14px; - -} - -.jspHorizontalBar .jspArrow -{ - width: 16px; - float: left; - height: 100%; -} - -.jspVerticalBar .jspArrow:focus -{ - outline: none; -} - -.jspCorner -{ - background: #eeeef4; - float: left; - height: 100%; -} - -/* Yuk! CSS Hack for IE6 3 pixel bug :( */ -* html .jspCorner -{ - margin: 0 -3px 0 0; -} - -/* Styles specific to this particular page */ -.scroll-pane-before, -.scroll-pane-after, -.scroll-pane-split, -.scroll-pane-os -{ - width: 100%; - height: 200px; - overflow: auto; -} -.horizontal-only -{ - height: auto; - max-height: 200px; -} |