diff --git a/core/modules/tour/config/tour.tooltip.admin-content-en.yml b/core/modules/tour/config/tour.tooltip.admin-content-en.yml new file mode 100644 index 0000000..3a0c21b --- /dev/null +++ b/core/modules/tour/config/tour.tooltip.admin-content-en.yml @@ -0,0 +1,12 @@ +id: admin-content-en +label: Administer content +langcode: en +body: Content can be filtered and bulk operated from this interface. +paths: + - admin/content +weight: "2" +attributes: + data-options: tipLocation:top + data-id: node-admin-content + data-text: Next + class: custom diff --git a/core/modules/tour/config/tour.tooltip.search-en.yml b/core/modules/tour/config/tour.tooltip.search-en.yml new file mode 100644 index 0000000..3acf2fc --- /dev/null +++ b/core/modules/tour/config/tour.tooltip.search-en.yml @@ -0,0 +1,12 @@ +id: search-en +label: Search for content +langcode: en +body: Enter your search terms to start searching. +paths: + - + - search +weight: "1" +attributes: + data-id: search-block-form + data-text: Next + class: custom diff --git a/core/modules/tour/css/joyride-2.0.3.css b/core/modules/tour/css/joyride-2.0.3.css new file mode 100644 index 0000000..cf33219 --- /dev/null +++ b/core/modules/tour/css/joyride-2.0.3.css @@ -0,0 +1,226 @@ +/* Artfully masterminded by ZURB */ +body { + position: relative; +} + +#joyRideTipContent { display: none; } + +/* Default styles for the container */ +.joyride-tip-guide { + position: absolute; + background: #000; + background: rgba(0,0,0,0.8); + display: none; + color: #fff; + width: 300px; + z-index: 101; + top: 0; /* keeps the page from scrolling when calculating position */ + left: 0; + font-family: "HelveticaNeue", "Helvetica Neue", "Helvetica", Helvetica, Arial, Lucida, sans-serif; + font-weight: normal; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +.joyride-content-wrapper { + padding: 10px 10px 15px 15px; +} + +/* Mobile */ +@media only screen and (max-width: 767px) { + .joyride-tip-guide { + width: 95% !important; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + left: 2.5% !important; + } + .joyride-tip-guide-wrapper { + width: 100%; + } +} + + +/* Add a little css triangle pip, older browser just miss out on the fanciness of it */ +.joyride-tip-guide span.joyride-nub { + display: block; + position: absolute; + left: 22px; + width: 0; + height: 0; + border: solid 14px; + border: solid 14px; +} + +.joyride-tip-guide span.joyride-nub.top { + /* + IE7/IE8 Don't support rgba so we set the fallback + border color here. However, IE7/IE8 are also buggy + in that the fallback color doesn't work for + border-bottom-color so here we set the border-color + and override the top,left,right colors below. + */ + border-color: #000; + border-color: rgba(0,0,0,0.8); + border-top-color: transparent !important; + border-left-color: transparent !important; + border-right-color: transparent !important; + top: -28px; + bottom: none; +} + +.joyride-tip-guide span.joyride-nub.bottom { + /* + IE7/IE8 Don't support rgba so we set the fallback + border color here. However, IE7/IE8 are also buggy + in that the fallback color doesn't work for + border-top-color so here we set the border-color + and override the bottom,left,right colors below. + */ + border-color: #000; + border-color: rgba(0,0,0,0.8) !important; + border-bottom-color: transparent !important; + border-left-color: transparent !important; + border-right-color: transparent !important; + bottom: -28px; + bottom: none; +} + +.joyride-tip-guide span.joyride-nub.right { + border-color: #000; + border-color: rgba(0,0,0,0.8) !important; + border-top-color: transparent !important; + border-right-color: transparent !important; + border-bottom-color: transparent !important; + top: 22px; + bottom: none; + left: auto; + right: -28px; +} + +.joyride-tip-guide span.joyride-nub.left { + border-color: #000; + border-color: rgba(0,0,0,0.8) !important; + border-top-color: transparent !important; + border-left-color: transparent !important; + border-bottom-color: transparent !important; + top: 22px; + left: -28px; + right: auto; + bottom: none; +} + +/* Typography */ +.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6 { + line-height: 1.25; + margin: 0; + font-weight: bold; + color: #fff; +} +.joyride-tip-guide h1 { font-size: 30px; } +.joyride-tip-guide h2 { font-size: 26px; } +.joyride-tip-guide h3 { font-size: 22px; } +.joyride-tip-guide h4 { font-size: 18px; } +.joyride-tip-guide h5 { font-size: 16px; } +.joyride-tip-guide h6 { font-size: 14px; } +.joyride-tip-guide p { + margin: 0 0 18px 0; + font-size: 14px; + line-height: 18px; +} +.joyride-tip-guide a { + color: rgb(255,255,255); + text-decoration: none; + border-bottom: dotted 1px rgba(255,255,255,0.6); +} +.joyride-tip-guide a:hover { + color: rgba(255,255,255,0.8); + border-bottom: none; +} + +/* Button Style */ +.joyride-tip-guide .joyride-next-tip { + width: auto; + padding: 6px 18px 4px; + font-size: 13px; + text-decoration: none; + color: rgb(255,255,255); + border: solid 1px rgb(0,60,180); + background: rgb(0,99,255); + background: -moz-linear-gradient(top, rgb(0,99,255) 0%, rgb(0,85,214) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(0,99,255)), color-stop(100%,rgb(0,85,214))); + background: -webkit-linear-gradient(top, rgb(0,99,255) 0%,rgb(0,85,214) 100%); + background: -o-linear-gradient(top, rgb(0,99,255) 0%,rgb(0,85,214) 100%); + background: -ms-linear-gradient(top, rgb(0,99,255) 0%,rgb(0,85,214) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#0063ff', endColorstr='#0055d6',GradientType=0 ); + background: linear-gradient(top, rgb(0,99,255) 0%,rgb(0,85,214) 100%); + text-shadow: 0 -1px 0 rgba(0,0,0,0.5); + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: 0px 1px 0px rgba(255,255,255,0.3) inset; + -moz-box-shadow: 0px 1px 0px rgba(255,255,255,0.3) inset; + box-shadow: 0px 1px 0px rgba(255,255,255,0.3) inset; +} + +.joyride-next-tip:hover { + color: rgb(255,255,255) !important; + border: solid 1px rgb(0,60,180) !important; + background: rgb(43,128,255); + background: -moz-linear-gradient(top, rgb(43,128,255) 0%, rgb(29,102,211) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(43,128,255)), color-stop(100%,rgb(29,102,211))); + background: -webkit-linear-gradient(top, rgb(43,128,255) 0%,rgb(29,102,211) 100%); + background: -o-linear-gradient(top, rgb(43,128,255) 0%,rgb(29,102,211) 100%); + background: -ms-linear-gradient(top, rgb(43,128,255) 0%,rgb(29,102,211) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#2b80ff', endColorstr='#1d66d3',GradientType=0 ); + background: linear-gradient(top, rgb(43,128,255) 0%,rgb(29,102,211) 100%); +} + +.joyride-timer-indicator-wrap { + width: 50px; + height: 3px; + border: solid 1px rgba(255,255,255,0.1); + position: absolute; + right: 17px; + bottom: 16px; +} +.joyride-timer-indicator { + display: block; + width: 0; + height: inherit; + background: rgba(255,255,255,0.25); +} + +.joyride-close-tip { + position: absolute; + right: 10px; + top: 10px; + color: rgba(255,255,255,0.4) !important; + text-decoration: none; + font-family: Verdana, sans-serif; + font-size: 10px; + font-weight: bold; + border-bottom: none !important; +} + +.joyride-close-tip:hover { + color: rgba(255,255,255,0.9) !important; +} + +.joyride-modal-bg { + position: fixed; + height: 100%; + width: 100%; + background: rgb(0,0,0); + background: transparent; + background: rgba(0,0,0, 0.5); + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: 0.5; + z-index: 100; + display: none; + top: 0; + left: 0; + cursor: pointer; +} diff --git a/core/modules/tour/css/tour.css b/core/modules/tour/css/tour.css new file mode 100644 index 0000000..6c35005 --- /dev/null +++ b/core/modules/tour/css/tour.css @@ -0,0 +1,17 @@ +#tour-items { + display: none; +} + +.toolbar .icon-help.tour-no-items-hidden { + display: none; +} + +.joyride-tip-guide { + z-index: 502 +} + +.tour-progress { + position: absolute; + bottom: 10px; + right: 15px; +} diff --git a/core/modules/tour/js/jquery.joyride-2.0.3.js b/core/modules/tour/js/jquery.joyride-2.0.3.js new file mode 100644 index 0000000..b144fb9 --- /dev/null +++ b/core/modules/tour/js/jquery.joyride-2.0.3.js @@ -0,0 +1,675 @@ +/* + * jQuery Foundation Joyride Plugin 2.0.3 + * http://foundation.zurb.com + * Copyright 2012, ZURB + * Free to use under the MIT license. + * http://www.opensource.org/licenses/mit-license.php +*/ + +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, undefined) { + 'use strict'; + + var defaults = { + 'version' : '2.0.3', + 'tipLocation' : 'bottom', // 'top' or 'bottom' in relation to parent + 'nubPosition' : 'auto', // override on a per tooltip bases + 'scrollSpeed' : 300, // Page scrolling speed in milliseconds + 'timer' : 0, // 0 = no timer , all other numbers = timer in milliseconds + 'startTimerOnClick' : true, // true or false - true requires clicking the first button start the timer + 'startOffset' : 0, // the index of the tooltip you want to start on (index of the li) + 'nextButton' : true, // true or false to control whether a next button is used + 'tipAnimation' : 'fade', // 'pop' or 'fade' in each tip + 'pauseAfter' : [], // array of indexes where to pause the tour after + 'tipAnimationFadeSpeed': 300, // when tipAnimation = 'fade' this is speed in milliseconds for the transition + 'cookieMonster' : false, // true or false to control whether cookies are used + 'cookieName' : 'joyride', // Name the cookie you'll use + 'cookieDomain' : false, // Will this cookie be attached to a domain, ie. '.notableapp.com' + 'tipContainer' : 'body', // Where will the tip be attached + 'postRideCallback' : $.noop, // A method to call once the tour closes (canceled or complete) + 'postStepCallback' : $.noop, // A method to call after each step + 'template' : { // HTML segments for tip layout + 'link' : 'X', + 'timer' : '
', + 'tip' : '
', + 'wrapper' : '', + 'button' : '' + } + }, + + Modernizr = Modernizr || false, + + settings = {}, + + methods = { + + init : function (opts) { + return this.each(function () { + + if ($.isEmptyObject(settings)) { + settings = $.extend(true, defaults, opts); + + // non configurable settings + settings.document = window.document; + settings.$document = $(settings.document); + settings.$window = $(window); + settings.$content_el = $(this); + settings.body_offset = $(settings.tipContainer).position(); + settings.$tip_content = $('> li', settings.$content_el); + settings.paused = false; + settings.attempts = 0; + + settings.tipLocationPatterns = { + top: ['bottom'], + bottom: [], // bottom should not need to be repositioned + left: ['right', 'top', 'bottom'], + right: ['left', 'top', 'bottom'] + }; + + // are we using jQuery 1.7+ + methods.jquery_check(); + + // can we create cookies? + if (!$.isFunction($.cookie)) { + settings.cookieMonster = false; + } + + // generate the tips and insert into dom. + if (!settings.cookieMonster || !$.cookie(settings.cookieName)) { + + settings.$tip_content.each(function (index) { + methods.create({$li : $(this), index : index}); + }); + + // show first tip + if (!settings.startTimerOnClick && settings.timer > 0) { + methods.show('init'); + methods.startTimer(); + } else { + methods.show('init'); + } + + } + + settings.$document.on('click.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) { + e.preventDefault(); + + if (settings.$li.next().length < 1) { + methods.end(); + } else if (settings.timer > 0) { + clearTimeout(settings.automate); + methods.hide(); + methods.show(); + methods.startTimer(); + } else { + methods.hide(); + methods.show(); + } + + }); + + settings.$document.on('click.joyride', '.joyride-close-tip', function (e) { + e.preventDefault(); + methods.end(); + }); + + settings.$window.bind('resize.joyride', function (e) { + if (methods.is_phone()) { + methods.pos_phone(); + } else { + methods.pos_default(); + } + }); + } else { + methods.restart(); + } + + }); + }, + + // call this method when you want to resume the tour + resume : function () { + methods.set_li(); + methods.show(); + }, + + tip_template : function (opts) { + var $blank, content, $wrapper; + + opts.tip_class = opts.tip_class || ''; + + $blank = $(settings.template.tip).addClass(opts.tip_class); + content = $.trim($(opts.li).html()) + + methods.button_text(opts.button_text) + + settings.template.link + + methods.timer_instance(opts.index); + + $wrapper = $(settings.template.wrapper); + if (opts.li.attr('data-aria-labelledby')) { + $wrapper.attr('aria-labelledby', opts.li.attr('data-aria-labelledby')) + } + if (opts.li.attr('data-aria-describedby')) { + $wrapper.attr('aria-describedby', opts.li.attr('data-aria-describedby')) + } + $blank.append($wrapper); + $blank.first().attr('data-index', opts.index); + $('.joyride-content-wrapper', $blank).append(content); + + return $blank[0]; + }, + + timer_instance : function (index) { + var txt; + + if ((index === 0 && settings.startTimerOnClick && settings.timer > 0) || settings.timer === 0) { + txt = ''; + } else { + txt = methods.outerHTML($(settings.template.timer)[0]); + } + return txt; + }, + + button_text : function (txt) { + if (settings.nextButton) { + txt = $.trim(txt) || 'Next'; + txt = methods.outerHTML($(settings.template.button).append(txt)[0]); + } else { + txt = ''; + } + return txt; + }, + + create : function (opts) { + // backwards compatibility with data-text attribute + var buttonText = opts.$li.attr('data-button') || opts.$li.attr('data-text'), + tipClass = opts.$li.attr('class'), + $tip_content = $(methods.tip_template({ + tip_class : tipClass, + index : opts.index, + button_text : buttonText, + li : opts.$li + })); + + $(settings.tipContainer).append($tip_content); + }, + + show : function (init) { + var opts = {}, ii, opts_arr = [], opts_len = 0, p, + $timer = null; + + // are we paused? + if (settings.$li === undefined || ($.inArray(settings.$li.index(), settings.pauseAfter) === -1)) { + + // don't go to the next li if the tour was paused + if (settings.paused) { + settings.paused = false; + } else { + methods.set_li(init); + } + + settings.attempts = 0; + + if (settings.$li.length && settings.$target.length > 0) { + opts_arr = (settings.$li.data('options') || ':').split(';'); + opts_len = opts_arr.length; + + // parse options + for (ii = opts_len - 1; ii >= 0; ii--) { + p = opts_arr[ii].split(':'); + + if (p.length === 2) { + opts[$.trim(p[0])] = $.trim(p[1]); + } + } + + settings.tipSettings = $.extend({}, settings, opts); + + settings.tipSettings.tipLocationPattern = settings.tipLocationPatterns[settings.tipSettings.tipLocation]; + + // scroll if not modal + if (!/body/i.test(settings.$target.selector)) { + methods.scroll_to(); + } + + if (methods.is_phone()) { + methods.pos_phone(true); + } else { + methods.pos_default(true); + } + + $timer = $('.joyride-timer-indicator', settings.$next_tip); + + if (/pop/i.test(settings.tipAnimation)) { + + $timer.outerWidth(0); + + if (settings.timer > 0) { + + settings.$next_tip.show(); + $timer.animate({ + width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth() + }, settings.timer); + + } else { + + settings.$next_tip.show(); + + } + + + } else if (/fade/i.test(settings.tipAnimation)) { + + $timer.outerWidth(0); + + if (settings.timer > 0) { + + settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed); + + settings.$next_tip.show(); + $timer.animate({ + width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth() + }, settings.timer); + + } else { + + settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed); + + } + } + + settings.$current_tip = settings.$next_tip; + $('.joyride-next-tip', settings.$current_tip).focus(); + methods.tabbable(settings.$current_tip); + + // skip non-existent targets + } else if (settings.$li && settings.$target.length < 1) { + + methods.show(); + + } else { + + methods.end(); + + } + } else { + + settings.paused = true; + + } + + }, + + // detect phones with media queries if supported. + is_phone : function () { + if (Modernizr) { + return Modernizr.mq('only screen and (max-width: 767px)'); + } + + return (settings.$window.width() < 767) ? true : false; + }, + + hide : function () { + settings.postStepCallback(settings.$li.index(), settings.$current_tip); + $('.joyride-modal-bg').hide(); + settings.$current_tip.hide(); + }, + + set_li : function (init) { + if (init) { + settings.$li = settings.$tip_content.eq(settings.startOffset); + methods.set_next_tip(); + settings.$current_tip = settings.$next_tip; + } else { + settings.$li = settings.$li.next(); + methods.set_next_tip(); + } + + methods.set_target(); + }, + + set_next_tip : function () { + settings.$next_tip = $('.joyride-tip-guide[data-index=' + settings.$li.index() + ']'); + }, + + set_target : function () { + var cl = settings.$li.attr('data-class'), + id = settings.$li.attr('data-id'), + $sel = function () { + if (id) { + return $(settings.document.getElementById(id)); + } else if (cl) { + return $('.' + cl).first(); + } else { + return $('body'); + } + }; + + settings.$target = $sel(); + }, + + scroll_to : function () { + var window_half, tipOffset; + + window_half = settings.$window.height() / 2; + tipOffset = Math.ceil(settings.$target.offset().top - window_half + settings.$next_tip.outerHeight()); + + $("html, body").stop().animate({ + scrollTop: tipOffset + }, settings.scrollSpeed); + }, + + paused : function () { + if (($.inArray((settings.$li.index() + 1), settings.pauseAfter) === -1)) { + return true; + } + + return false; + }, + + destroy : function () { + settings.$document.off('.joyride'); + $(window).off('.joyride'); + $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride'); + $('.joyride-tip-guide, .joyride-modal-bg').remove(); + clearTimeout(settings.automate); + settings = {}; + }, + + restart : function () { + methods.hide(); + settings.$li = undefined; + methods.show('init'); + }, + + pos_default : function (init) { + var half_fold = Math.ceil(settings.$window.height() / 2), + tip_position = settings.$next_tip.offset(), + $nub = $('.joyride-nub', settings.$next_tip), + nub_height = Math.ceil($nub.outerHeight() / 2), + toggle = init || false; + + // tip must not be "display: none" to calculate position + if (toggle) { + settings.$next_tip.css('visibility', 'hidden'); + settings.$next_tip.show(); + } + + if (!/body/i.test(settings.$target.selector)) { + + if (methods.bottom()) { + settings.$next_tip.css({ + top: (settings.$target.offset().top + nub_height + settings.$target.outerHeight()), + left: settings.$target.offset().left}); + + methods.nub_position($nub, settings.tipSettings.nubPosition, 'top'); + + } else if (methods.top()) { + + settings.$next_tip.css({ + top: (settings.$target.offset().top - settings.$next_tip.outerHeight() - nub_height), + left: settings.$target.offset().left}); + + methods.nub_position($nub, settings.tipSettings.nubPosition, 'bottom'); + + } else if (methods.right()) { + + settings.$next_tip.css({ + top: settings.$target.offset().top, + left: (settings.$target.outerWidth() + settings.$target.offset().left)}); + + methods.nub_position($nub, settings.tipSettings.nubPosition, 'left'); + + } else if (methods.left()) { + + settings.$next_tip.css({ + top: settings.$target.offset().top, + left: (settings.$target.offset().left - settings.$next_tip.outerWidth() - nub_height)}); + + methods.nub_position($nub, settings.tipSettings.nubPosition, 'right'); + + } + + if (!methods.visible(methods.corners(settings.$next_tip)) && settings.attempts < settings.tipSettings.tipLocationPattern.length) { + + $nub.removeClass('bottom') + .removeClass('top') + .removeClass('right') + .removeClass('left'); + + settings.tipSettings.tipLocation = settings.tipSettings.tipLocationPattern[settings.attempts]; + + settings.attempts++; + + methods.pos_default(true); + + } + + } else if (settings.$li.length) { + + methods.pos_modal($nub); + + } + + if (toggle) { + settings.$next_tip.hide(); + settings.$next_tip.css('visibility', 'visible'); + } + + }, + + pos_phone : function (init) { + var tip_height = settings.$next_tip.outerHeight(), + tip_offset = settings.$next_tip.offset(), + target_height = settings.$target.outerHeight(), + $nub = $('.joyride-nub', settings.$next_tip), + nub_height = Math.ceil($nub.outerHeight() / 2), + toggle = init || false; + + $nub.removeClass('bottom') + .removeClass('top') + .removeClass('right') + .removeClass('left'); + + if (toggle) { + settings.$next_tip.css('visibility', 'hidden'); + settings.$next_tip.show(); + } + + if (!/body/i.test(settings.$target.selector)) { + + if (methods.top()) { + + settings.$next_tip.offset({top: settings.$target.offset().top - tip_height - nub_height}); + $nub.addClass('bottom'); + + } else { + + settings.$next_tip.offset({top: settings.$target.offset().top + target_height + nub_height}); + $nub.addClass('top'); + + } + + } else if (settings.$li.length) { + + methods.pos_modal($nub); + + } + + if (toggle) { + settings.$next_tip.hide(); + settings.$next_tip.css('visibility', 'visible'); + } + }, + + pos_modal : function ($nub) { + methods.center(); + $nub.hide(); + + if ($('.joyride-modal-bg').length < 1) { + $('body').append('
').show(); + } + + if (/pop/i.test(settings.tipAnimation)) { + $('.joyride-modal-bg').show(); + } else { + $('.joyride-modal-bg').fadeIn(settings.tipAnimationFadeSpeed); + } + }, + + center : function () { + var $w = settings.$window; + + settings.$next_tip.css({ + top : ((($w.height() - settings.$next_tip.outerHeight()) / 2) + $w.scrollTop()), + left : ((($w.width() - settings.$next_tip.outerWidth()) / 2) + $w.scrollLeft()) + }); + + return true; + }, + + bottom : function () { + return /bottom/i.test(settings.tipSettings.tipLocation); + }, + + top : function () { + return /top/i.test(settings.tipSettings.tipLocation); + }, + + right : function () { + return /right/i.test(settings.tipSettings.tipLocation); + }, + + left : function () { + return /left/i.test(settings.tipSettings.tipLocation); + }, + + corners : function (el) { + var w = settings.$window, + right = w.width() + w.scrollLeft(), + bottom = w.width() + w.scrollTop(); + + return [ + el.offset().top <= w.scrollTop(), + right <= el.offset().left + el.outerWidth(), + bottom <= el.offset().top + el.outerHeight(), + w.scrollLeft() >= el.offset().left + ]; + }, + + visible : function (hidden_corners) { + var i = hidden_corners.length; + + while (i--) { + if (hidden_corners[i]) return false; + } + + return true; + }, + + nub_position : function (nub, pos, def) { + if (pos === 'auto') { + nub.addClass(def); + } else { + nub.addClass(pos); + } + }, + + startTimer : function () { + if (settings.$li.length) { + settings.automate = setTimeout(function () { + methods.hide(); + methods.show(); + methods.startTimer(); + }, settings.timer); + } else { + clearTimeout(settings.automate); + } + }, + + end : function () { + if (settings.cookieMonster) { + $.cookie(settings.cookieName, 'ridden', { expires: 365, domain: settings.cookieDomain }); + } + + if (settings.timer > 0) { + clearTimeout(settings.automate); + } + + $('.joyride-modal-bg').hide(); + settings.$current_tip.hide(); + settings.postStepCallback(settings.$li.index(), settings.$current_tip); + settings.postRideCallback(settings.$li.index(), settings.$current_tip); + }, + + jquery_check : function () { + // define on() and off() for older jQuery + if (!$.isFunction($.fn.on)) { + + $.fn.on = function (types, sel, fn) { + + return this.delegate(sel, types, fn); + + }; + + $.fn.off = function (types, sel, fn) { + + return this.undelegate(sel, types, fn); + + }; + + return false; + } + + return true; + }, + + outerHTML : function (el) { + // support FireFox < 11 + return el.outerHTML || new XMLSerializer().serializeToString(el); + }, + + version : function () { + return settings.version; + }, + + tabbable : function (el) { + $(el).on('keydown', function( event ) { + if (!event.isDefaultPrevented() && event.keyCode && + // Escape key. + event.keyCode === 27 ) { + event.preventDefault(); + methods.end(); + return; + } + + // Prevent tabbing out of tour items. + if ( event.keyCode !== 9 ) { + return; + } + var tabbables = $(el).find(":tabbable"), + first = tabbables.filter(":first"), + last = tabbables.filter(":last"); + if ( event.target === last[0] && !event.shiftKey ) { + first.focus( 1 ); + event.preventDefault(); + } else if ( event.target === first[0] && event.shiftKey ) { + last.focus( 1 ); + event.preventDefault(); + } + }); + } + + }; + + $.fn.joyride = function (method) { + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist on jQuery.joyride'); + } + }; + +}(jQuery, this)); diff --git a/core/modules/tour/js/tour.js b/core/modules/tour/js/tour.js new file mode 100644 index 0000000..ef2eb12 --- /dev/null +++ b/core/modules/tour/js/tour.js @@ -0,0 +1,115 @@ +(function ($, Drupal) { + + "use strict"; + + Drupal.behaviors.tour = { + attach: function(context) { + + var toolbar = "#toolbar-tab-tour"; + var toolbar_class = "tour-no-items-hidden"; + var tour_items = "#tour-items"; + var overlay = "#overlay-container"; + var overlay_content = "#overlay-content " + tour_items; + + /** + * Initializes the tour. + */ + function setupTour(items, scope) { + console.log('setting up'); + if ($(items).length) { + $(toolbar, scope).removeClass(toolbar_class); + + // Loop over items and remove missing tour items. + $(items + " li").each(function( index ) { + var tour_item_id = $(this).attr('data-id'); + var tour_item_class = $(this).attr('data-class'); + if (!$("#" + tour_item_id).length && !$("." + tour_item_class).length) { + $(this).remove(); + } + else { + $(this).attr('data-text', Drupal.t('Next')); + } + + }); + + // Update the last item to have "Close" as the button. + $(items + " li").last().attr('data-text', Drupal.t('End tour')); + } + else { + $(toolbar, scope).addClass(toolbar_class); + } + $(toolbar, scope).bind('click.tour', function() { + if ($(overlay).length || !window) { + return false; + } + if ($(toolbar + ".touring", scope).length) { + $(items).attr('hidden', true).joyride('destroy'); + closeTour(scope); + } + else { + $(toolbar, scope).addClass('touring'); + $(items).attr('hidden', false).joyride({ + 'postRideCallback': function() { + $(toolbar, scope).removeClass('touring'); + } + }); + } + return false; + }); + } + + /** + * Cleans up the tour. + */ + function closeTour(scope) { + var toolbar = "#toolbar-tab-tour"; + $(toolbar, scope).removeClass('touring'); + } + + /** + * Detaches up the tour. + */ + function detachTour(scope) { + var toolbar = "#toolbar-tab-tour"; + closeTour(scope); + $(toolbar, scope).unbind('click.tour'); + } + + /** + * Initialize the appropriate tour. + */ + if ($(overlay_content).length) { + // Remove tour events associated with overlay. + detachTour(window.parent.document); + // Attach it based on outer content. + setupTour(overlay_content, window.parent.document); + } + else { + $(document).bind('drupalOverlayBeforeClose', function() { + // Remove tour events associated with overlay. + detachTour(window.document); + // Attach it based on outer content. + setupTour(tour_items, window.document); + console.log('closing'); + }); + $(document).bind('drupalOverlayBeforeLoad', function() { + // Remove tour events associated with overlay. + detachTour(window.document); + console.log('closing before move'); + }); + + // Don't run this in overlay. + // This only runs once in the parent. + if (window == window.parent) { + $('body').once('tour', function() { + setupTour(tour_items, window.document); + }); + } + else { + setupTour(tour_items, window.document); + } + } + } + }; + +})(jQuery, Drupal); diff --git a/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/TourTooltip.php b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/TourTooltip.php new file mode 100644 index 0000000..91f3d58 --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/TourTooltip.php @@ -0,0 +1,96 @@ +derivatives) && !empty($this->derivatives[$derivative_id])) { + return $this->derivatives[$derivative_id]; + } + $this->getDerivativeDefinitions($base_plugin_definition); + return $this->derivatives[$derivative_id]; + } + + /** + * Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinitions(). + * + * Retrieves tooltips. + */ + public function getDerivativeDefinitions(array $base_plugin_definition) { + $configs = config_get_storage_names_with_prefix('tour.tooltip.'); + $derivatives = array(); + $ids = array(); + foreach ($configs as $key) { + $config = config($key); + $id = $config->get('id'); + $this->derivatives[$id] = array('id' => $id) + $base_plugin_definition; + } + return $this->derivatives; + } +} diff --git a/core/modules/tour/lib/Drupal/tour/Plugin/tour/tour/Tooltip.php b/core/modules/tour/lib/Drupal/tour/Plugin/tour/tour/Tooltip.php new file mode 100644 index 0000000..a02d058 --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/Plugin/tour/tour/Tooltip.php @@ -0,0 +1,91 @@ +entity = entity_load('tour_tooltip', $configuration['id']); + $this->weight = $this->entity->weight; + } + + /** + * Implements \Drupal\tour\TourInterface::getOutput(). + */ + public function getOutput() { + return theme('tour_tooltip', array('tooltip' => $this->entity)); + } + + /** + * Implements \Drupal\tour\TourInterface::getAttributes(). + */ + public function getAttributes() { + $attributes = $this->entity->attributes; + if (!empty($this->entity->location)) { + $attributes['data-options'] = 'tipLocation:' . $this->entity->location; + } + // Add aria support. + $this->entity->ariaId = drupal_html_id($this->entity->id()); + $attributes['data-aria-describedby'] = 'tour-tooltip-' . $this->entity->ariaId . '-contents'; + $attributes['data-aria-labelledby'] = 'tour-tooltip-' . $this->entity->ariaId . '-label'; + return $attributes; + } + + /** + * Implements \Drupal\tour\TourInterface::getPaths(). + */ + public function getPaths() { + return $this->entity->paths; + } + + /** + * Implements \Drupal\tour\TourInterface::setAttribute(). + */ + public function setAttribute($key, $value) { + $this->entity->attributes[$key] = $value; + } + + /** + * Implements \Drupal\tour\TourInterface::getAttachments(). + */ + public function getAttachments() { + return array(); + } + + /** + * Implements \Drupal\tour\TourInterface::getLanguage(). + */ + public function getLanguage() { + return $this->entity->langcode; + } + + /** + * Implements \Drupal\tour\TourInterface::label(). + */ + public function label() { + return $this->entity->label(); + } +} diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php new file mode 100644 index 0000000..cd52f59 --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php @@ -0,0 +1,168 @@ + 'Tour tests', + 'description' => "Test the functionality of tour tooltips.", + 'group' => 'Tour' + ); + } + + /** + * Sets up the test environment. + */ + public function setUp() { + parent::setUp(); + $this->webUser = $this->drupalCreateUser(array('access tour', 'administer languages')); + $this->drupalLogin($this->webUser); + } + + /** + * Test tour functionality. + */ + public function testTourFunctionality() { + // Navigate to tour-test-1 and verify the tour_test_1 tip is found. + $this->drupalGet('tour-test-1'); + $this->assertRaw('data-id="tour-test-1" data-text="Close" class="custom"'); + + // Verify Italian variant is not found. + $this->assertNoText('La pioggia cade in spagna'); + + // Navigate to tour-test-2/subpath and verify the tour_test_2 tip is found. + $this->drupalGet('tour-test-2/subpath'); + $this->assertRaw('data-id="tour-test-2" data-text="Close" class="custom"'); + + // Enable Italian language and navigate to it/tour-test1 and verify italian + // version of tip is found. + language_save(new Language(array('langcode' => 'it'))); + drupal_static_reset(); + entity_info_cache_clear(); + menu_router_rebuild(); + + $this->drupalGet('it/tour-test-1'); + $this->assertText('La pioggia cade in spagna'); + + // Verify English version is not found. + $this->assertNoText('Falls mostly on the plain'); + + // Revert back to english. + language_save(new Language(array('langcode' => 'en'))); + drupal_static_reset(); + entity_info_cache_clear(); + menu_router_rebuild(); + + // Create a new tip (tip-3) visible on tour-test-1 with low weight. + $code_tip = array( + 'id' => 'tour-code-test-1', + 'label' => 'The rain in spain', + 'langcode' => 'en', + 'body' => 'Falls mostly on the plain.', + 'weight' => '-100', + 'paths' => array( + 'tour-test-1', + ), + 'attributes' => array( + 'data-id' => 'tour-code-test-1', + 'data-text' => 'Next', + 'class' => 'custom', + ), + ); + entity_create('tour_tooltip', $code_tip); + + // Load it back from the database and verify storage worked. + $this->drupalGet('tour-test-1'); + + // Create a new tip (tip-4) visible on tour-test-1 with low weight. + $tip4 = entity_create('tour_tooltip', array( + 'id' => 'tip4', + 'label' => 'Tip 4', + 'langcode' => 'en', + 'weight' => 100, + 'body' => 'Body of tip 4', + 'paths' => array('tour-test-1'), + 'attributes' => array( + 'data-id' => 'tour-test-4', + 'data-text' => 'Next', + 'class' => 'custom' + ) + ))->save(); + + // Load it back from the database and verify storage worked. + $tip4_loaded = entity_load('tour_tooltip', 'tip4'); + + // Verify that hook_ENTITY_TYPE_load() integration worked. + $this->assertEqual($tip4_loaded->loaded, 'load hooks work'); + // Verify that hook_ENTITY_TYPE_presave() integration worked. + $this->assertEqual($tip4_loaded->label(), 'Tip 4 alter'); + + // Naviate to tour-test-1 and verify the new tip is found. + $this->drupalGet('tour-test-1'); + $elements = $this->xpath('//li[@class=:class and @data-id=:data_id and ./h2[contains(., :text)]]', array( + ':data_id' => 'tour-test-4', + ':class' => 'custom', + ':text' => 'Tip 4 alter' + )); + $this->assertEqual(count($elements), 1, 'Found the required tip markup for tip 4'); + + // Change the name of tip 4 and verify new value found. + // Note that tour_test_tour_tooltip_presave() will rewrite this to add alter + // again. + $tip4_loaded->save(); + $this->drupalGet('tour-test-1'); + $elements = $this->xpath('//li[@class=:class and @data-id=:data_id and ./h2[contains(., :text)]]', array( + ':data_id' => 'tour-test-4', + ':class' => 'custom', + ':text' => 'Tip 4 alter alter' + )); + $this->assertEqual(count($elements), 1, 'Found the new tip markup for tip 4'); + + // Verify that the weight sorting works by ensuring the lower weight item + // (tip 4) has the close button. + $elements = $this->xpath('//li[@class=:class and @data-id=:data_id and @data-text=:text]', array( + ':data_id' => 'tour-test-4', + ':class' => 'custom', + ':text' => 'Close' + )); + $this->assertEqual(count($elements), 1, 'Found tip 4 was weighted last and had close button'); + + // Navigate to tour-test-3 and verify the TestTour plugin provides expected + // markup. + $this->drupalGet('tour-test-3'); + $this->assertText('this is the output of TourTest plugin'); + + // Test hook_tour_alter(). + // @todo. + } +} diff --git a/core/modules/tour/lib/Drupal/tour/TourBase.php b/core/modules/tour/lib/Drupal/tour/TourBase.php new file mode 100644 index 0000000..a6f1a1d --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/TourBase.php @@ -0,0 +1,88 @@ +attributes; + } + + /** + * Implements \Drupal\tour\TourInterface::getOutput(). + */ + public function getOutput() {} + + /** + * Implements \Drupal\tour\TourInterface::setAttribute(). + */ + public function setAttribute($key, $value) { + $this->attributes[$key] = $value; + } + + /** + * Implements \Drupal\tour\TourInterface::getPaths(). + */ + public function getPaths() { + return $this->paths; + } + + /** + * Implements \Drupal\tour\TourInterface::getLanguage(). + */ + public function getLanguage() { + return $this->langcode; + } + + /** + * Implements \Drupal\tour\TourInterface::label(). + */ + public function label() { + return $this->label; + } +} diff --git a/core/modules/tour/lib/Drupal/tour/TourBundle.php b/core/modules/tour/lib/Drupal/tour/TourBundle.php new file mode 100644 index 0000000..4b771a5 --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/TourBundle.php @@ -0,0 +1,27 @@ +register('plugin.manager.tour', 'Drupal\tour\TourManager'); + } + +} diff --git a/core/modules/tour/lib/Drupal/tour/TourInterface.php b/core/modules/tour/lib/Drupal/tour/TourInterface.php new file mode 100644 index 0000000..10fb680 --- /dev/null +++ b/core/modules/tour/lib/Drupal/tour/TourInterface.php @@ -0,0 +1,69 @@ +discovery = new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('tour', 'tour')); + $this->discovery = new AlterDecorator($this->discovery, 'tour_info'); + $this->discovery = new CacheDecorator($this->discovery, 'tour'); + $this->factory = new DefaultFactory($this->discovery); + } + + /** + * Overrides \Drupal\Component\Plugin\PluginManagerBase::createInstance(). + */ + public function createInstance($plugin_id, array $configuration = array(), Tour $entity = NULL) { + $plugin_class = DefaultFactory::getPluginClass($plugin_id, $this->discovery); + return new $plugin_class($configuration, $plugin_id, $this->discovery, $entity); + } + + /** + * Get all options for a given path. + * + * @param string $path + * The path for which the tour is valid. + * + * @return array + * Array of tour elements + * + * @todo add weight and sort. + */ + public function getByPath($path = '') { + // @todo caching. + $language_content = language(LANGUAGE_TYPE_CONTENT); + $cid = 'tour:' . $language_content->langcode . ':' . $path; + if ($cache = cache('cache')->get($cid)) { + return $cache->data; + } + + $tours = array(); + $path_alias = drupal_strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path)); + foreach ($this->getDefinitions() as $key => $definition) { + $item = $this->createInstance($key, $definition); + $pages = implode("\n", $item->getPaths()); + if ((drupal_match_path($path_alias, $pages) || + (($path != $path_alias) && drupal_match_path($path, $pages))) && + $item->getLanguage() == $language_content->langcode) { + $tours[$key] = $item; + } + } + // Allow other modules to alter. + drupal_container()->get('module_handler')->alter('tour_items', $tours, $path); + // Cache the results. + cache('cache')->set($cid, $tours, CacheBackendInterface::CACHE_PERMANENT, array('tour')); + return $tours; + } + +} diff --git a/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1-it.yml b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1-it.yml new file mode 100644 index 0000000..d987df4 --- /dev/null +++ b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1-it.yml @@ -0,0 +1,11 @@ +id: tour-test-1-it +label: La pioggia cade in spagna +langcode: it +weight: "1" +body: Per lo piĆ¹ in pianura. +paths: + - tour-test-1 +attributes: + data-id: tour-test-1 + data-text: Next + class: custom diff --git a/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1.yml b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1.yml new file mode 100644 index 0000000..f19147a --- /dev/null +++ b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-1.yml @@ -0,0 +1,11 @@ +id: tour-test-1 +label: The rain in spain +langcode: en +body: Falls mostly on the plain. +weight: "1" +paths: + - tour-test-1 +attributes: + data-id: tour-test-1 + data-text: Next + class: custom diff --git a/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-2.yml b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-2.yml new file mode 100644 index 0000000..8b12bbb --- /dev/null +++ b/core/modules/tour/tests/tour_test/config/tour.tooltip.tour-test-2.yml @@ -0,0 +1,11 @@ +id: tour-test-2 +label: The quick brown fox +langcode: en +body: Jumps over the lazy dog. +weight: "2" +paths: + - tour-test-2/* +attributes: + data-id: tour-test-2 + data-text: Next + class: custom diff --git a/core/modules/tour/tests/tour_test/lib/Drupal/tour_test/Plugin/tour/tour/TestTour.php b/core/modules/tour/tests/tour_test/lib/Drupal/tour_test/Plugin/tour/tour/TestTour.php new file mode 100644 index 0000000..da6b7c4 --- /dev/null +++ b/core/modules/tour/tests/tour_test/lib/Drupal/tour_test/Plugin/tour/tour/TestTour.php @@ -0,0 +1,54 @@ + 'tour-test-3', + 'data-options' => 'tipLocation:top', + 'data-text' => 'Next', + 'class' => 'custom' + ); + + /** + * Set the language of this tour. + * @var string + */ + public $langcode = 'en'; + + /** + * Defines the output. + */ + public function getOutput() { + return 'this is the output of TourTest plugin'; + } +} diff --git a/core/modules/tour/tests/tour_test/tour_test.info b/core/modules/tour/tests/tour_test/tour_test.info new file mode 100644 index 0000000..d20c1d7 --- /dev/null +++ b/core/modules/tour/tests/tour_test/tour_test.info @@ -0,0 +1,5 @@ +name = Tests for tour module +hidden = TRUE +core = 8.x +description = Tests for tour module +dependencies[] = tour diff --git a/core/modules/tour/tests/tour_test/tour_test.module b/core/modules/tour/tests/tour_test/tour_test.module new file mode 100644 index 0000000..71816a5 --- /dev/null +++ b/core/modules/tour/tests/tour_test/tour_test.module @@ -0,0 +1,79 @@ + 'tour_test_1', + 'access callback' => TRUE, + 'title' => 'Tour test 1' + ); + $items['tour-test-2/subpath'] = array( + 'page callback' => 'tour_test_2', + 'access callback' => TRUE, + 'title' => 'Tour test 2' + ); + $items['tour-test-3'] = array( + 'page callback' => 'tour_test_3', + 'access callback' => TRUE, + 'title' => 'Tour test 3' + ); + return $items; +} + +/** + * Implements hook_ENTITY_TYPE_load() for tour_tooltip. + */ +function tour_test_tour_tooltip_load($entities) { + if (isset($entities['tip4'])) { + $entities['tip4']->loaded = 'load hooks work'; + } +} + +/** + * Implements hook_ENTITY_TYPE_presave() for tour_tooltip. + */ +function tour_test_tour_tooltip_presave($entity) { + if ($entity->id() == 'tip4') { + $entity->label = $entity->label . ' alter'; + } +} + +/** + * Page callback: output some content for testing tours. + */ +function tour_test_1() { + return array( + array( + '#markup' => '
Where does the rain in Spain fail?
' . + '
Tip created later?
' + ) + ); +} + +/** + * Page callback: output some content for testing tours. + */ +function tour_test_2() { + return array( + array( + '#markup' => '
Pangram example
' + ) + ); +} + +/** + * Page callback: output some content for testing tours. + */ +function tour_test_3() { + return array( + array( + '#markup' => '
Third test
' + ) + ); +} diff --git a/core/modules/tour/tour.api.php b/core/modules/tour/tour.api.php new file mode 100644 index 0000000..da2c179 --- /dev/null +++ b/core/modules/tour/tour.api.php @@ -0,0 +1,12 @@ + array( + 'title' => 'Tooltip', + 'description' => 'Generic tooltip class', + 'class' => 'Drupal\tour\Plugin\tour\tour\Tooltip', + 'derivative' => 'Drupal\tour\Plugin\Derivative\Tooltip', + ), + ); +} + +/** + * Implements hook_permission(). + */ +function tour_permission() { + return array( + 'access tour' => array( + 'title' => t('Access tour'), + 'description' => t('View tour tips.'), + ), + ); +} + +/** + * Implements hook_library_info(). + */ +function tour_library_info() { + // Library definition for tour. + $libraries['tour'] = array( + 'title' => 'Tour', + 'website' => '', + 'version' => '', + 'js' => array( + drupal_get_path('module', 'tour') . '/js/tour.js' => array(), + ), + 'css' => array( + drupal_get_path('module', 'tour') . '/css/tour.css' => array( + 'type' => 'file', + 'media' => 'screen', + ), + ), + ); + + // Library definition of joyride. + $libraries['jquery.joyride'] = array( + 'title' => 'Joyride', + 'website' => 'https://github.com/zurb/joyride', + 'version' => '2.0.3', + 'js' => array( + drupal_get_path('module', 'tour') . '/js/jquery.joyride-2.0.3.js' => array(), + ), + 'css' => array( + drupal_get_path('module', 'tour') . '/css/joyride-2.0.3.css' => array( + 'type' => 'file', + 'media' => 'screen', + ), + ), + 'dependencies' => array( + // Require jQuery core by System module. + array('system', 'jquery'), + // Require jQuery cookie by System module. + array('system', 'jquery.cookie'), + // Require custom tour library. + array('tour', 'tour'), + ), + ); + + return $libraries; +} + +/** + * Implements hook_toolbar(). + */ +function tour_toolbar() { + if (!user_access('access tour')) { + return; + } + + $tab['tour'] = array( + '#type' => 'toolbar_item', + 'tab' => array( + '#type' => 'link', + '#title' => t('Tour'), + '#href' => '', + '#options' => array( + 'html' => FALSE, + 'attributes' => array( + 'class' => array('icon', 'icon-help', 'tour-no-items-hidden'), + 'id' => 'toolbar-tab-tour', + ), + ), + '#attached' => array( + 'library' => array( + array('tour', 'tour'), + ), + ), + ), + ); + + return $tab; +} + +/** + * Implements hook_theme(). + */ +function tour_theme($existing, $type, $theme, $path) { + return array( + 'tour_items' => array( + 'variables' => array('tour_items' => NULL), + ), + 'tour_tooltip' => array( + 'variables' => array('tooltip' => NULL), + ), + ); +} + +/** + * Default implementation of theme_tour_items(). + */ +function theme_tour_items($vars) { + $output = ""; + return $output; +} + +/** + * Default implementation of theme_tour_tooltip(). + */ +function theme_tour_tooltip($vars) { + $tooltip = $vars['tooltip']; + return '

' . check_plain($tooltip->label()) . '

+

' . filter_xss_admin($tooltip->body) . '

'; +} + +/** + * Implements hook_page_build(). + */ +function tour_page_build(&$page) { + $page['#attached']['library'][] = array('tour', 'tour'); +} + +/** + * Implements hook_preprocess_page(). + */ +function tour_preprocess_page(&$variables) { + $manager = drupal_container()->get('plugin.manager.tour'); + $items = $manager->getByPath(current_path()); + if (empty($items)) { + return; + } + + // Re-use built-in sort from ConfigEntityBase. + uasort($items, array('\Drupal\tour\Plugin\Core\Entity\TourTooltip', 'sort')); + drupal_add_library('tour', 'jquery.joyride'); + $variables['page']['help']['tour']['#markup'] = theme('tour_items', array('tour_items' => $items)); +} + +/** + * Implements hook_tour_tooltip_insert(). + */ +function tour_tour_tooltip_insert($entities) { + drupal_container()->get('plugin.manager.tour')->clearCachedDefinitions(); + cache('cache')->deleteTags(array('tour')); +} + +/** + * Implements hook_tour_tooltip_update(). + */ +function tour_tour_tooltip_update($entities) { + drupal_container()->get('plugin.manager.tour')->clearCachedDefinitions(); + cache('cache')->deleteTags(array('tour')); +} diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-active-display-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-active-display-en.yml new file mode 100644 index 0000000..e57046d --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-active-display-en.yml @@ -0,0 +1,11 @@ +id: views-ui-active-display-en +label: Active display +langcode: en +body: This is the active display in the view. When there are multiple displays, one link for each display is shown and you can switch displays simply by clicking on the display link. +paths: + - admin/structure/views/view/*/edit +weight: "2" +attributes: + data-class: views-display-top li.active + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-displays-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-displays-en.yml new file mode 100644 index 0000000..885773d --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-displays-en.yml @@ -0,0 +1,11 @@ +id: views-ui-displays-en +label: Displays in this view +langcode: en +body: A view can consist of multiple displays. A display is a way of outputting the results E.g. as a page or in a block. The available displays in your view are show here. +paths: + - admin/structure/views/view/*/edit +weight: "1" +attributes: + data-id: views-display-top + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-fields-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-fields-en.yml new file mode 100644 index 0000000..0dd47a6 --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-fields-en.yml @@ -0,0 +1,11 @@ +id: views-ui-fields-en +label: Fields +langcode: en +body: This section shows the fields output for each result. Depending on the format selected for the view, you may not see anything listed here. If the format of your view uses fields, you can click on each field displayed to configure it. +paths: + - admin/structure/views/view/*/edit +weight: "5" +attributes: + data-class: views-ui-display-tab-bucket.fields + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-en.yml new file mode 100644 index 0000000..3f2b8cf --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-en.yml @@ -0,0 +1,11 @@ +id: views-ui-filter-en +label: Filter your view +langcode: en +body: This section displays the filters you have active in your view. A filter is used to limit the results available in the output. E.g. to only show content that was published, you would add a filter for Published and select Yes. +paths: + - admin/structure/views/view/*/edit +weight: "6" +attributes: + data-class: views-ui-display-tab-bucket.filter-criteria + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-operations-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-operations-en.yml new file mode 100644 index 0000000..8bfe974 --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-filter-operations-en.yml @@ -0,0 +1,11 @@ +id: views-ui-filter-operations-en +label: Filter actions +langcode: en +body: Use this drop-button to add and re-arrange filters +paths: + - admin/structure/views/view/*/edit +weight: "7" +attributes: + data-class: views-ui-display-tab-bucket.filter-criteria .dropbutton-widget + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-format-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-format-en.yml new file mode 100644 index 0000000..d5337cb --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-format-en.yml @@ -0,0 +1,11 @@ +id: views-ui-format-en +label: Output format +langcode: en +body: Use this section to manage the format of the output results. You can choose different ways in which the matching results are output. E.g. Choose Content to output each item completely, using your configured display settings. Other options include Fields which allows you to output only specific fields on each matching result. Additional formats can be added by installing additional module to extend Drupal's base functionality. +paths: + - admin/structure/views/view/*/edit +weight: "4" +attributes: + data-class: views-ui-display-tab-bucket.format + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-preview-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-preview-en.yml new file mode 100644 index 0000000..729cf8b --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-preview-en.yml @@ -0,0 +1,11 @@ +id: views-ui-preview-en +label: Preview +langcode: en +body: Use this button to show a preview of the view output +paths: + - admin/structure/views/view/*/edit +weight: "10" +attributes: + data-id: preview-submit + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-en.yml new file mode 100644 index 0000000..0464f2d --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-en.yml @@ -0,0 +1,11 @@ +id: views-ui-sorts-en +label: Sort Criteria +langcode: en +body: This section shows the enabled sorting criteria for the view. Sorting criteria are used to control the order in which the results are output. Clicking on any of the active sorting criteria shown in this section enables you to configure it. +paths: + - admin/structure/views/view/*/edit +weight: "8" +attributes: + data-class: views-ui-display-tab-bucket.sort-criteria + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-operations-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-operations-en.yml new file mode 100644 index 0000000..ee83988 --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-sorts-operations-en.yml @@ -0,0 +1,11 @@ +id: views-ui-sorts-operations-en +label: Sort actions +langcode: en +body: Use this drop-button to add and re-arrange the sorting criteria. +paths: + - admin/structure/views/view/*/edit +weight: "9" +attributes: + data-class: views-ui-display-tab-bucket.sort-criteria .dropbutton-widget + data-text: Next + class: custom diff --git a/core/modules/views/views_ui/config/tour.tooltip.views-ui-view-admin-en.yml b/core/modules/views/views_ui/config/tour.tooltip.views-ui-view-admin-en.yml new file mode 100644 index 0000000..9b7eee0 --- /dev/null +++ b/core/modules/views/views_ui/config/tour.tooltip.views-ui-view-admin-en.yml @@ -0,0 +1,12 @@ +id: views-ui-view-admin-en +label: View administration +langcode: en +body: Use this drop-button to perform administrative tasks on the view, including adding a description and creating a clone. Click the drop button to view the available options. +paths: + - admin/structure/views/view/*/edit +weight: "3" +location: left +attributes: + data-id: views-display-extra-actions + data-text: Next + class: custom diff --git a/core/profiles/standard/standard.info b/core/profiles/standard/standard.info index 2773abe..ad9a398 100644 --- a/core/profiles/standard/standard.info +++ b/core/profiles/standard/standard.info @@ -30,3 +30,4 @@ dependencies[] = file dependencies[] = rdf dependencies[] = views dependencies[] = views_ui +dependencies[] = tour