1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611 |
- /**
- * @file
- * Adapted from underscore.js with the addition Drupal namespace.
- */
- /**
- * Limits the invocations of a function in a given time frame.
- *
- * The debounce function wrapper should be used sparingly. One clear use case
- * is limiting the invocation of a callback attached to the window resize event.
- *
- * Before using the debounce function wrapper, consider first whether the
- * callback could be attached to an event that fires less frequently or if the
- * function can be written in such a way that it is only invoked under specific
- * conditions.
- *
- * @param {function} func
- * The function to be invoked.
- * @param {number} wait
- * The time period within which the callback function should only be
- * invoked once. For example if the wait period is 250ms, then the callback
- * will only be called at most 4 times per second.
- * @param {bool} immediate
- * Whether we wait at the beginning or end to execute the function.
- *
- * @return {function}
- * The debounced function.
- */
- Drupal.debounce = function (func, wait, immediate) {
- 'use strict';
- var timeout;
- var result;
- return function () {
- var context = this;
- var args = arguments;
- var later = function () {
- timeout = null;
- if (!immediate) {
- result = func.apply(context, args);
- }
- };
- var callNow = immediate && !timeout;
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- if (callNow) {
- result = func.apply(context, args);
- }
- return result;
- };
- };
- ;
- /**
- * @file
- * Adds an HTML element and method to trigger audio UAs to read system messages.
- *
- * Use {@link Drupal.announce} to indicate to screen reader users that an
- * element on the page has changed state. For instance, if clicking a link
- * loads 10 more items into a list, one might announce the change like this.
- *
- * @example
- * $('#search-list')
- * .on('itemInsert', function (event, data) {
- * // Insert the new items.
- * $(data.container.el).append(data.items.el);
- * // Announce the change to the page contents.
- * Drupal.announce(Drupal.t('@count items added to @container',
- * {'@count': data.items.length, '@container': data.container.title}
- * ));
- * });
- */
- (function (Drupal, debounce) {
- 'use strict';
- var liveElement;
- var announcements = [];
- /**
- * Builds a div element with the aria-live attribute and add it to the DOM.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches the behavior for drupalAnnouce.
- */
- Drupal.behaviors.drupalAnnounce = {
- attach: function (context) {
- // Create only one aria-live element.
- if (!liveElement) {
- liveElement = document.createElement('div');
- liveElement.id = 'drupal-live-announce';
- liveElement.className = 'visually-hidden';
- liveElement.setAttribute('aria-live', 'polite');
- liveElement.setAttribute('aria-busy', 'false');
- document.body.appendChild(liveElement);
- }
- }
- };
- /**
- * Concatenates announcements to a single string; appends to the live region.
- */
- function announce() {
- var text = [];
- var priority = 'polite';
- var announcement;
- // Create an array of announcement strings to be joined and appended to the
- // aria live region.
- var il = announcements.length;
- for (var i = 0; i < il; i++) {
- announcement = announcements.pop();
- text.unshift(announcement.text);
- // If any of the announcements has a priority of assertive then the group
- // of joined announcements will have this priority.
- if (announcement.priority === 'assertive') {
- priority = 'assertive';
- }
- }
- if (text.length) {
- // Clear the liveElement so that repeated strings will be read.
- liveElement.innerHTML = '';
- // Set the busy state to true until the node changes are complete.
- liveElement.setAttribute('aria-busy', 'true');
- // Set the priority to assertive, or default to polite.
- liveElement.setAttribute('aria-live', priority);
- // Print the text to the live region. Text should be run through
- // Drupal.t() before being passed to Drupal.announce().
- liveElement.innerHTML = text.join('\n');
- // The live text area is updated. Allow the AT to announce the text.
- liveElement.setAttribute('aria-busy', 'false');
- }
- }
- /**
- * Triggers audio UAs to read the supplied text.
- *
- * The aria-live region will only read the text that currently populates its
- * text node. Replacing text quickly in rapid calls to announce results in
- * only the text from the most recent call to {@link Drupal.announce} being
- * read. By wrapping the call to announce in a debounce function, we allow for
- * time for multiple calls to {@link Drupal.announce} to queue up their
- * messages. These messages are then joined and append to the aria-live region
- * as one text node.
- *
- * @param {string} text
- * A string to be read by the UA.
- * @param {string} [priority='polite']
- * A string to indicate the priority of the message. Can be either
- * 'polite' or 'assertive'.
- *
- * @return {function}
- * The return of the call to debounce.
- *
- * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops
- */
- Drupal.announce = function (text, priority) {
- // Save the text and priority into a closure variable. Multiple simultaneous
- // announcements will be concatenated and read in sequence.
- announcements.push({
- text: text,
- priority: priority
- });
- // Immediately invoke the function that debounce returns. 200 ms is right at
- // the cusp where humans notice a pause, so we will wait
- // at most this much time before the set of queued announcements is read.
- return (debounce(announce, 200)());
- };
- }(Drupal, Drupal.debounce));
- ;
- window.matchMedia||(window.matchMedia=function(){"use strict";var e=window.styleMedia||window.media;if(!e){var t=document.createElement("style"),i=document.getElementsByTagName("script")[0],n=null;t.type="text/css";t.id="matchmediajs-test";i.parentNode.insertBefore(t,i);n="getComputedStyle"in window&&window.getComputedStyle(t,null)||t.currentStyle;e={matchMedium:function(e){var i="@media "+e+"{ #matchmediajs-test { width: 1px; } }";if(t.styleSheet){t.styleSheet.cssText=i}else{t.textContent=i}return n.width==="1px"}}}return function(t){return{matches:e.matchMedium(t||"all"),media:t||"all"}}}());
- ;
- (function(){if(window.matchMedia&&window.matchMedia("all").addListener){return false}var e=window.matchMedia,i=e("only all").matches,n=false,t=0,a=[],r=function(i){clearTimeout(t);t=setTimeout(function(){for(var i=0,n=a.length;i<n;i++){var t=a[i].mql,r=a[i].listeners||[],o=e(t.media).matches;if(o!==t.matches){t.matches=o;for(var s=0,l=r.length;s<l;s++){r[s].call(window,t)}}}},30)};window.matchMedia=function(t){var o=e(t),s=[],l=0;o.addListener=function(e){if(!i){return}if(!n){n=true;window.addEventListener("resize",r,true)}if(l===0){l=a.push({mql:o,listeners:s})}s.push(e)};o.removeListener=function(e){for(var i=0,n=s.length;i<n;i++){if(s[i]===e){s.splice(i,1)}}};return o}})();
- ;
- /**
- * @file
- * Manages elements that can offset the size of the viewport.
- *
- * Measures and reports viewport offset dimensions from elements like the
- * toolbar that can potentially displace the positioning of other elements.
- */
- /**
- * @typedef {object} Drupal~displaceOffset
- *
- * @prop {number} top
- * @prop {number} left
- * @prop {number} right
- * @prop {number} bottom
- */
- /**
- * Triggers when layout of the page changes.
- *
- * This is used to position fixed element on the page during page resize and
- * Toolbar toggling.
- *
- * @event drupalViewportOffsetChange
- */
- (function ($, Drupal, debounce) {
- 'use strict';
- /**
- * @name Drupal.displace.offsets
- *
- * @type {Drupal~displaceOffset}
- */
- var offsets = {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0
- };
- /**
- * Registers a resize handler on the window.
- *
- * @type {Drupal~behavior}
- */
- Drupal.behaviors.drupalDisplace = {
- attach: function () {
- // Mark this behavior as processed on the first pass.
- if (this.displaceProcessed) {
- return;
- }
- this.displaceProcessed = true;
- $(window).on('resize.drupalDisplace', debounce(displace, 200));
- }
- };
- /**
- * Informs listeners of the current offset dimensions.
- *
- * @function Drupal.displace
- *
- * @prop {Drupal~displaceOffset} offsets
- *
- * @param {bool} [broadcast]
- * When true or undefined, causes the recalculated offsets values to be
- * broadcast to listeners.
- *
- * @return {Drupal~displaceOffset}
- * An object whose keys are the for sides an element -- top, right, bottom
- * and left. The value of each key is the viewport displacement distance for
- * that edge.
- *
- * @fires event:drupalViewportOffsetChange
- */
- function displace(broadcast) {
- offsets = Drupal.displace.offsets = calculateOffsets();
- if (typeof broadcast === 'undefined' || broadcast) {
- $(document).trigger('drupalViewportOffsetChange', offsets);
- }
- return offsets;
- }
- /**
- * Determines the viewport offsets.
- *
- * @return {Drupal~displaceOffset}
- * An object whose keys are the for sides an element -- top, right, bottom
- * and left. The value of each key is the viewport displacement distance for
- * that edge.
- */
- function calculateOffsets() {
- return {
- top: calculateOffset('top'),
- right: calculateOffset('right'),
- bottom: calculateOffset('bottom'),
- left: calculateOffset('left')
- };
- }
- /**
- * Gets a specific edge's offset.
- *
- * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
- * be considered in the viewport offset calculations. If the attribute has a
- * numeric value, that value will be used. If no value is provided, one will
- * be calculated using the element's dimensions and placement.
- *
- * @function Drupal.displace.calculateOffset
- *
- * @param {string} edge
- * The name of the edge to calculate. Can be 'top', 'right',
- * 'bottom' or 'left'.
- *
- * @return {number}
- * The viewport displacement distance for the requested edge.
- */
- function calculateOffset(edge) {
- var edgeOffset = 0;
- var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
- var n = displacingElements.length;
- for (var i = 0; i < n; i++) {
- var el = displacingElements[i];
- // If the element is not visible, do consider its dimensions.
- if (el.style.display === 'none') {
- continue;
- }
- // If the offset data attribute contains a displacing value, use it.
- var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
- // If the element's offset data attribute exits
- // but is not a valid number then get the displacement
- // dimensions directly from the element.
- if (isNaN(displacement)) {
- displacement = getRawOffset(el, edge);
- }
- // If the displacement value is larger than the current value for this
- // edge, use the displacement value.
- edgeOffset = Math.max(edgeOffset, displacement);
- }
- return edgeOffset;
- }
- /**
- * Calculates displacement for element based on its dimensions and placement.
- *
- * @param {HTMLElement} el
- * The jQuery element whose dimensions and placement will be measured.
- *
- * @param {string} edge
- * The name of the edge of the viewport that the element is associated
- * with.
- *
- * @return {number}
- * The viewport displacement distance for the requested edge.
- */
- function getRawOffset(el, edge) {
- var $el = $(el);
- var documentElement = document.documentElement;
- var displacement = 0;
- var horizontal = (edge === 'left' || edge === 'right');
- // Get the offset of the element itself.
- var placement = $el.offset()[horizontal ? 'left' : 'top'];
- // Subtract scroll distance from placement to get the distance
- // to the edge of the viewport.
- placement -= window['scroll' + (horizontal ? 'X' : 'Y')] || document.documentElement['scroll' + (horizontal ? 'Left' : 'Top')] || 0;
- // Find the displacement value according to the edge.
- switch (edge) {
- // Left and top elements displace as a sum of their own offset value
- // plus their size.
- case 'top':
- // Total displacement is the sum of the elements placement and size.
- displacement = placement + $el.outerHeight();
- break;
- case 'left':
- // Total displacement is the sum of the elements placement and size.
- displacement = placement + $el.outerWidth();
- break;
- // Right and bottom elements displace according to their left and
- // top offset. Their size isn't important.
- case 'bottom':
- displacement = documentElement.clientHeight - placement;
- break;
- case 'right':
- displacement = documentElement.clientWidth - placement;
- break;
- default:
- displacement = 0;
- }
- return displacement;
- }
- /**
- * Assign the displace function to a property of the Drupal global object.
- *
- * @ignore
- */
- Drupal.displace = displace;
- $.extend(Drupal.displace, {
- /**
- * Expose offsets to other scripts to avoid having to recalculate offsets.
- *
- * @ignore
- */
- offsets: offsets,
- /**
- * Expose method to compute a single edge offsets.
- *
- * @ignore
- */
- calculateOffset: calculateOffset
- });
- })(jQuery, Drupal, Drupal.debounce);
- ;
- /**
- * @file
- * Builds a nested accordion widget.
- *
- * Invoke on an HTML list element with the jQuery plugin pattern.
- *
- * @example
- * $('.toolbar-menu').drupalToolbarMenu();
- */
- (function ($, Drupal, drupalSettings) {
- 'use strict';
- /**
- * Store the open menu tray.
- */
- var activeItem = Drupal.url(drupalSettings.path.currentPath);
- $.fn.drupalToolbarMenu = function () {
- var ui = {
- handleOpen: Drupal.t('Extend'),
- handleClose: Drupal.t('Collapse')
- };
- /**
- * Handle clicks from the disclosure button on an item with sub-items.
- *
- * @param {Object} event
- * A jQuery Event object.
- */
- function toggleClickHandler(event) {
- var $toggle = $(event.target);
- var $item = $toggle.closest('li');
- // Toggle the list item.
- toggleList($item);
- // Close open sibling menus.
- var $openItems = $item.siblings().filter('.open');
- toggleList($openItems, false);
- }
- /**
- * Handle clicks from a menu item link.
- *
- * @param {Object} event
- * A jQuery Event object.
- */
- function linkClickHandler(event) {
- // If the toolbar is positioned fixed (and therefore hiding content
- // underneath), then users expect clicks in the administration menu tray
- // to take them to that destination but for the menu tray to be closed
- // after clicking: otherwise the toolbar itself is obstructing the view
- // of the destination they chose.
- if (!Drupal.toolbar.models.toolbarModel.get('isFixed')) {
- Drupal.toolbar.models.toolbarModel.set('activeTab', null);
- }
- // Stopping propagation to make sure that once a toolbar-box is clicked
- // (the whitespace part), the page is not redirected anymore.
- event.stopPropagation();
- }
- /**
- * Toggle the open/close state of a list is a menu.
- *
- * @param {jQuery} $item
- * The li item to be toggled.
- *
- * @param {Boolean} switcher
- * A flag that forces toggleClass to add or a remove a class, rather than
- * simply toggling its presence.
- */
- function toggleList($item, switcher) {
- var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
- switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
- // Toggle the item open state.
- $item.toggleClass('open', switcher);
- // Twist the toggle.
- $toggle.toggleClass('open', switcher);
- // Adjust the toggle text.
- $toggle
- .find('.action')
- // Expand Structure, Collapse Structure.
- .text((switcher) ? ui.handleClose : ui.handleOpen);
- }
- /**
- * Add markup to the menu elements.
- *
- * Items with sub-elements have a list toggle attached to them. Menu item
- * links and the corresponding list toggle are wrapped with in a div
- * classed with .toolbar-box. The .toolbar-box div provides a positioning
- * context for the item list toggle.
- *
- * @param {jQuery} $menu
- * The root of the menu to be initialized.
- */
- function initItems($menu) {
- var options = {
- class: 'toolbar-icon toolbar-handle',
- action: ui.handleOpen,
- text: ''
- };
- // Initialize items and their links.
- $menu.find('li > a').wrap('<div class="toolbar-box">');
- // Add a handle to each list item if it has a menu.
- $menu.find('li').each(function (index, element) {
- var $item = $(element);
- if ($item.children('ul.toolbar-menu').length) {
- var $box = $item.children('.toolbar-box');
- options.text = Drupal.t('@label', {'@label': $box.find('a').text()});
- $item.children('.toolbar-box')
- .append(Drupal.theme('toolbarMenuItemToggle', options));
- }
- });
- }
- /**
- * Adds a level class to each list based on its depth in the menu.
- *
- * This function is called recursively on each sub level of lists elements
- * until the depth of the menu is exhausted.
- *
- * @param {jQuery} $lists
- * A jQuery object of ul elements.
- *
- * @param {number} level
- * The current level number to be assigned to the list elements.
- */
- function markListLevels($lists, level) {
- level = (!level) ? 1 : level;
- var $lis = $lists.children('li').addClass('level-' + level);
- $lists = $lis.children('ul');
- if ($lists.length) {
- markListLevels($lists, level + 1);
- }
- }
- /**
- * On page load, open the active menu item.
- *
- * Marks the trail of the active link in the menu back to the root of the
- * menu with .menu-item--active-trail.
- *
- * @param {jQuery} $menu
- * The root of the menu.
- */
- function openActiveItem($menu) {
- var pathItem = $menu.find('a[href="' + location.pathname + '"]');
- if (pathItem.length && !activeItem) {
- activeItem = location.pathname;
- }
- if (activeItem) {
- var $activeItem = $menu.find('a[href="' + activeItem + '"]').addClass('menu-item--active');
- var $activeTrail = $activeItem.parentsUntil('.root', 'li').addClass('menu-item--active-trail');
- toggleList($activeTrail, true);
- }
- }
- // Return the jQuery object.
- return this.each(function (selector) {
- var $menu = $(this).once('toolbar-menu');
- if ($menu.length) {
- // Bind event handlers.
- $menu
- .on('click.toolbar', '.toolbar-box', toggleClickHandler)
- .on('click.toolbar', '.toolbar-box a', linkClickHandler);
- $menu.addClass('root');
- initItems($menu);
- markListLevels($menu);
- // Restore previous and active states.
- openActiveItem($menu);
- }
- });
- };
- /**
- * A toggle is an interactive element often bound to a click handler.
- *
- * @param {object} options
- * Options for the button.
- * @param {string} options.class
- * Class to set on the button.
- * @param {string} options.action
- * Action for the button.
- * @param {string} options.text
- * Used as label for the button.
- *
- * @return {string}
- * A string representing a DOM fragment.
- */
- Drupal.theme.toolbarMenuItemToggle = function (options) {
- return '<button class="' + options['class'] + '"><span class="action">' + options.action + '</span><span class="label">' + options.text + '</span></button>';
- };
- }(jQuery, Drupal, drupalSettings));
- ;
- /**
- * @file
- * Defines the behavior of the Drupal administration toolbar.
- */
- (function ($, Drupal, drupalSettings) {
- 'use strict';
- // Merge run-time settings with the defaults.
- var options = $.extend(
- {
- breakpoints: {
- 'toolbar.narrow': '',
- 'toolbar.standard': '',
- 'toolbar.wide': ''
- }
- },
- drupalSettings.toolbar,
- // Merge strings on top of drupalSettings so that they are not mutable.
- {
- strings: {
- horizontal: Drupal.t('Horizontal orientation'),
- vertical: Drupal.t('Vertical orientation')
- }
- }
- );
- /**
- * Registers tabs with the toolbar.
- *
- * The Drupal toolbar allows modules to register top-level tabs. These may
- * point directly to a resource or toggle the visibility of a tray.
- *
- * Modules register tabs with hook_toolbar().
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches the toolbar rendering functionality to the toolbar element.
- */
- Drupal.behaviors.toolbar = {
- attach: function (context) {
- // Verify that the user agent understands media queries. Complex admin
- // toolbar layouts require media query support.
- if (!window.matchMedia('only screen').matches) {
- return;
- }
- // Process the administrative toolbar.
- $(context).find('#toolbar-administration').once('toolbar').each(function () {
- // Establish the toolbar models and views.
- var model = Drupal.toolbar.models.toolbarModel = new Drupal.toolbar.ToolbarModel({
- locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false,
- activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')))
- });
- Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
- el: this,
- model: model,
- strings: options.strings
- });
- Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView({
- el: this,
- model: model,
- strings: options.strings
- });
- Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView({
- el: this,
- model: model
- });
- // Render collapsible menus.
- var menuModel = Drupal.toolbar.models.menuModel = new Drupal.toolbar.MenuModel();
- Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
- el: $(this).find('.toolbar-menu-administration').get(0),
- model: menuModel,
- strings: options.strings
- });
- // Handle the resolution of Drupal.toolbar.setSubtrees.
- // This is handled with a deferred so that the function may be invoked
- // asynchronously.
- Drupal.toolbar.setSubtrees.done(function (subtrees) {
- menuModel.set('subtrees', subtrees);
- var theme = drupalSettings.ajaxPageState.theme;
- localStorage.setItem('Drupal.toolbar.subtrees.' + theme, JSON.stringify(subtrees));
- // Indicate on the toolbarModel that subtrees are now loaded.
- model.set('areSubtreesLoaded', true);
- });
- // Attach a listener to the configured media query breakpoints.
- for (var label in options.breakpoints) {
- if (options.breakpoints.hasOwnProperty(label)) {
- var mq = options.breakpoints[label];
- var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
- // Curry the model and the label of the media query breakpoint to
- // the mediaQueryChangeHandler function.
- mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
- // Fire the mediaQueryChangeHandler for each configured breakpoint
- // so that they process once.
- Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
- }
- }
- // Trigger an initial attempt to load menu subitems. This first attempt
- // is made after the media query handlers have had an opportunity to
- // process. The toolbar starts in the vertical orientation by default,
- // unless the viewport is wide enough to accommodate a horizontal
- // orientation. Thus we give the Toolbar a chance to determine if it
- // should be set to horizontal orientation before attempting to load
- // menu subtrees.
- Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
- $(document)
- // Update the model when the viewport offset changes.
- .on('drupalViewportOffsetChange.toolbar', function (event, offsets) {
- model.set('offsets', offsets);
- });
- // Broadcast model changes to other modules.
- model
- .on('change:orientation', function (model, orientation) {
- $(document).trigger('drupalToolbarOrientationChange', orientation);
- })
- .on('change:activeTab', function (model, tab) {
- $(document).trigger('drupalToolbarTabChange', tab);
- })
- .on('change:activeTray', function (model, tray) {
- $(document).trigger('drupalToolbarTrayChange', tray);
- });
- // If the toolbar's orientation is horizontal and no active tab is
- // defined then show the tray of the first toolbar tab by default (but
- // not the first 'Home' toolbar tab).
- if (Drupal.toolbar.models.toolbarModel.get('orientation') === 'horizontal' && Drupal.toolbar.models.toolbarModel.get('activeTab') === null) {
- Drupal.toolbar.models.toolbarModel.set({
- activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0)
- });
- }
- });
- }
- };
- /**
- * Toolbar methods of Backbone objects.
- *
- * @namespace
- */
- Drupal.toolbar = {
- /**
- * A hash of View instances.
- *
- * @type {object.<string, Backbone.View>}
- */
- views: {},
- /**
- * A hash of Model instances.
- *
- * @type {object.<string, Backbone.Model>}
- */
- models: {},
- /**
- * A hash of MediaQueryList objects tracked by the toolbar.
- *
- * @type {object.<string, object>}
- */
- mql: {},
- /**
- * Accepts a list of subtree menu elements.
- *
- * A deferred object that is resolved by an inlined JavaScript callback.
- *
- * @type {jQuery.Deferred}
- *
- * @see toolbar_subtrees_jsonp().
- */
- setSubtrees: new $.Deferred(),
- /**
- * Respond to configured narrow media query changes.
- *
- * @param {Drupal.toolbar.ToolbarModel} model
- * A toolbar model
- * @param {string} label
- * Media query label.
- * @param {object} mql
- * A MediaQueryList object.
- */
- mediaQueryChangeHandler: function (model, label, mql) {
- switch (label) {
- case 'toolbar.narrow':
- model.set({
- isOriented: mql.matches,
- isTrayToggleVisible: false
- });
- // If the toolbar doesn't have an explicit orientation yet, or if the
- // narrow media query doesn't match then set the orientation to
- // vertical.
- if (!mql.matches || !model.get('orientation')) {
- model.set({orientation: 'vertical'}, {validate: true});
- }
- break;
- case 'toolbar.standard':
- model.set({
- isFixed: mql.matches
- });
- break;
- case 'toolbar.wide':
- model.set({
- orientation: ((mql.matches) ? 'horizontal' : 'vertical')
- }, {validate: true});
- // The tray orientation toggle visibility does not need to be
- // validated.
- model.set({
- isTrayToggleVisible: mql.matches
- });
- break;
- default:
- break;
- }
- }
- };
- /**
- * A toggle is an interactive element often bound to a click handler.
- *
- * @return {string}
- * A string representing a DOM fragment.
- */
- Drupal.theme.toolbarOrientationToggle = function () {
- return '<div class="toolbar-toggle-orientation"><div class="toolbar-lining">' +
- '<button class="toolbar-icon" type="button"></button>' +
- '</div></div>';
- };
- /**
- * Ajax command to set the toolbar subtrees.
- *
- * @param {Drupal.Ajax} ajax
- * {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
- * @param {object} response
- * JSON response from the Ajax request.
- * @param {number} [status]
- * XMLHttpRequest status.
- */
- Drupal.AjaxCommands.prototype.setToolbarSubtrees = function (ajax, response, status) {
- Drupal.toolbar.setSubtrees.resolve(response.subtrees);
- };
- }(jQuery, Drupal, drupalSettings));
- ;
- /**
- * @file
- * A Backbone Model for collapsible menus.
- */
- (function (Backbone, Drupal) {
- 'use strict';
- /**
- * Backbone Model for collapsible menus.
- *
- * @constructor
- *
- * @augments Backbone.Model
- */
- Drupal.toolbar.MenuModel = Backbone.Model.extend(/** @lends Drupal.toolbar.MenuModel# */{
- /**
- * @type {object}
- *
- * @prop {object} subtrees
- */
- defaults: /** @lends Drupal.toolbar.MenuModel# */{
- /**
- * @type {object}
- */
- subtrees: {}
- }
- });
- }(Backbone, Drupal));
- ;
- /**
- * @file
- * A Backbone Model for the toolbar.
- */
- (function (Backbone, Drupal) {
- 'use strict';
- /**
- * Backbone model for the toolbar.
- *
- * @constructor
- *
- * @augments Backbone.Model
- */
- Drupal.toolbar.ToolbarModel = Backbone.Model.extend(/** @lends Drupal.toolbar.ToolbarModel# */{
- /**
- * @type {object}
- *
- * @prop activeTab
- * @prop activeTray
- * @prop isOriented
- * @prop isFixed
- * @prop areSubtreesLoaded
- * @prop isViewportOverflowConstrained
- * @prop orientation
- * @prop locked
- * @prop isTrayToggleVisible
- * @prop height
- * @prop offsets
- */
- defaults: /** @lends Drupal.toolbar.ToolbarModel# */{
- /**
- * The active toolbar tab. All other tabs should be inactive under
- * normal circumstances. It will remain active across page loads. The
- * active item is stored as an ID selector e.g. '#toolbar-item--1'.
- *
- * @type {string}
- */
- activeTab: null,
- /**
- * Represents whether a tray is open or not. Stored as an ID selector e.g.
- * '#toolbar-item--1-tray'.
- *
- * @type {string}
- */
- activeTray: null,
- /**
- * Indicates whether the toolbar is displayed in an oriented fashion,
- * either horizontal or vertical.
- *
- * @type {bool}
- */
- isOriented: false,
- /**
- * Indicates whether the toolbar is positioned absolute (false) or fixed
- * (true).
- *
- * @type {bool}
- */
- isFixed: false,
- /**
- * Menu subtrees are loaded through an AJAX request only when the Toolbar
- * is set to a vertical orientation.
- *
- * @type {bool}
- */
- areSubtreesLoaded: false,
- /**
- * If the viewport overflow becomes constrained, isFixed must be true so
- * that elements in the trays aren't lost off-screen and impossible to
- * get to.
- *
- * @type {bool}
- */
- isViewportOverflowConstrained: false,
- /**
- * The orientation of the active tray.
- *
- * @type {string}
- */
- orientation: 'vertical',
- /**
- * A tray is locked if a user toggled it to vertical. Otherwise a tray
- * will switch between vertical and horizontal orientation based on the
- * configured breakpoints. The locked state will be maintained across page
- * loads.
- *
- * @type {bool}
- */
- locked: false,
- /**
- * Indicates whether the tray orientation toggle is visible.
- *
- * @type {bool}
- */
- isTrayToggleVisible: false,
- /**
- * The height of the toolbar.
- *
- * @type {number}
- */
- height: null,
- /**
- * The current viewport offsets determined by {@link Drupal.displace}. The
- * offsets suggest how a module might position is components relative to
- * the viewport.
- *
- * @type {object}
- *
- * @prop {number} top
- * @prop {number} right
- * @prop {number} bottom
- * @prop {number} left
- */
- offsets: {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0
- }
- },
- /**
- * @inheritdoc
- *
- * @param {object} attributes
- * Attributes for the toolbar.
- * @param {object} options
- * Options for the toolbar.
- *
- * @return {string|undefined}
- * Returns an error message if validation failed.
- */
- validate: function (attributes, options) {
- // Prevent the orientation being set to horizontal if it is locked, unless
- // override has not been passed as an option.
- if (attributes.orientation === 'horizontal' && this.get('locked') && !options.override) {
- return Drupal.t('The toolbar cannot be set to a horizontal orientation when it is locked.');
- }
- }
- });
- }(Backbone, Drupal));
- ;
- /**
- * @file
- * A Backbone view for the body element.
- */
- (function ($, Drupal, Backbone) {
- 'use strict';
- Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{
- /**
- * Adjusts the body element with the toolbar position and dimension changes.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize: function () {
- this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render);
- },
- /**
- * @inheritdoc
- */
- render: function () {
- var $body = $('body');
- var orientation = this.model.get('orientation');
- var isOriented = this.model.get('isOriented');
- var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
- $body
- // We are using JavaScript to control media-query handling for two
- // reasons: (1) Using JavaScript let's us leverage the breakpoint
- // configurations and (2) the CSS is really complex if we try to hide
- // some styling from browsers that don't understand CSS media queries.
- // If we drive the CSS from classes added through JavaScript,
- // then the CSS becomes simpler and more robust.
- .toggleClass('toolbar-vertical', (orientation === 'vertical'))
- .toggleClass('toolbar-horizontal', (isOriented && orientation === 'horizontal'))
- // When the toolbar is fixed, it will not scroll with page scrolling.
- .toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')))
- // Toggle the toolbar-tray-open class on the body element. The class is
- // applied when a toolbar tray is active. Padding might be applied to
- // the body element to prevent the tray from overlapping content.
- .toggleClass('toolbar-tray-open', !!this.model.get('activeTray'))
- // Apply padding to the top of the body to offset the placement of the
- // toolbar bar element.
- .css('padding-top', this.model.get('offsets').top);
- }
- });
- }(jQuery, Drupal, Backbone));
- ;
- /**
- * @file
- * A Backbone view for the collapsible menus.
- */
- (function ($, Backbone, Drupal) {
- 'use strict';
- Drupal.toolbar.MenuVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.MenuVisualView# */{
- /**
- * Backbone View for collapsible menus.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize: function () {
- this.listenTo(this.model, 'change:subtrees', this.render);
- },
- /**
- * @inheritdoc
- */
- render: function () {
- var subtrees = this.model.get('subtrees');
- // Add subtrees.
- for (var id in subtrees) {
- if (subtrees.hasOwnProperty(id)) {
- this.$el
- .find('#toolbar-link-' + id)
- .once('toolbar-subtrees')
- .after(subtrees[id]);
- }
- }
- // Render the main menu as a nested, collapsible accordion.
- if ('drupalToolbarMenu' in $.fn) {
- this.$el
- .children('.toolbar-menu')
- .drupalToolbarMenu();
- }
- }
- });
- }(jQuery, Backbone, Drupal));
- ;
- /**
- * @file
- * A Backbone view for the aural feedback of the toolbar.
- */
- (function (Backbone, Drupal) {
- 'use strict';
- Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarAuralView# */{
- /**
- * Backbone view for the aural feedback of the toolbar.
- *
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * Options for the view.
- * @param {object} options.strings
- * Various strings to use in the view.
- */
- initialize: function (options) {
- this.strings = options.strings;
- this.listenTo(this.model, 'change:orientation', this.onOrientationChange);
- this.listenTo(this.model, 'change:activeTray', this.onActiveTrayChange);
- },
- /**
- * Announces an orientation change.
- *
- * @param {Drupal.toolbar.ToolbarModel} model
- * The toolbar model in question.
- * @param {string} orientation
- * The new value of the orientation attribute in the model.
- */
- onOrientationChange: function (model, orientation) {
- Drupal.announce(Drupal.t('Tray orientation changed to @orientation.', {
- '@orientation': orientation
- }));
- },
- /**
- * Announces a changed active tray.
- *
- * @param {Drupal.toolbar.ToolbarModel} model
- * The toolbar model in question.
- * @param {HTMLElement} tray
- * The new value of the tray attribute in the model.
- */
- onActiveTrayChange: function (model, tray) {
- var relevantTray = (tray === null) ? model.previous('activeTray') : tray;
- var action = (tray === null) ? Drupal.t('closed') : Drupal.t('opened');
- var trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
- var text;
- if (trayNameElement !== null) {
- text = Drupal.t('Tray "@tray" @action.', {
- '@tray': trayNameElement.textContent, '@action': action
- });
- }
- else {
- text = Drupal.t('Tray @action.', {'@action': action});
- }
- Drupal.announce(text);
- }
- });
- }(Backbone, Drupal));
- ;
- /**
- * @file
- * A Backbone view for the toolbar element. Listens to mouse & touch.
- */
- (function ($, Drupal, drupalSettings, Backbone) {
- 'use strict';
- Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{
- /**
- * Event map for the `ToolbarVisualView`.
- *
- * @return {object}
- * A map of events.
- */
- events: function () {
- // Prevents delay and simulated mouse events.
- var touchEndToClick = function (event) {
- event.preventDefault();
- event.target.click();
- };
- return {
- 'click .toolbar-bar .toolbar-tab .trigger': 'onTabClick',
- 'click .toolbar-toggle-orientation button': 'onOrientationToggleClick',
- 'touchend .toolbar-bar .toolbar-tab .trigger': touchEndToClick,
- 'touchend .toolbar-toggle-orientation button': touchEndToClick
- };
- },
- /**
- * Backbone view for the toolbar element. Listens to mouse & touch.
- *
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * Options for the view object.
- * @param {object} options.strings
- * Various strings to use in the view.
- */
- initialize: function (options) {
- this.strings = options.strings;
- this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
- this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
- this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
- // Add the tray orientation toggles.
- this.$el
- .find('.toolbar-tray .toolbar-lining')
- .append(Drupal.theme('toolbarOrientationToggle'));
- // Trigger an activeTab change so that listening scripts can respond on
- // page load. This will call render.
- this.model.trigger('change:activeTab');
- },
- /**
- * @inheritdoc
- *
- * @return {Drupal.toolbar.ToolbarVisualView}
- * The `ToolbarVisualView` instance.
- */
- render: function () {
- this.updateTabs();
- this.updateTrayOrientation();
- this.updateBarAttributes();
- // Load the subtrees if the orientation of the toolbar is changed to
- // vertical. This condition responds to the case that the toolbar switches
- // from horizontal to vertical orientation. The toolbar starts in a
- // vertical orientation by default and then switches to horizontal during
- // initialization if the media query conditions are met. Simply checking
- // that the orientation is vertical here would result in the subtrees
- // always being loaded, even when the toolbar initialization ultimately
- // results in a horizontal orientation.
- //
- // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
- // loading is invoked during initialization after media query conditions
- // have been processed.
- if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
- this.loadSubtrees();
- }
- // Trigger a recalculation of viewport displacing elements. Use setTimeout
- // to ensure this recalculation happens after changes to visual elements
- // have processed.
- window.setTimeout(function () {
- Drupal.displace(true);
- }, 0);
- return this;
- },
- /**
- * Responds to a toolbar tab click.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- onTabClick: function (event) {
- // If this tab has a tray associated with it, it is considered an
- // activatable tab.
- if (event.target.hasAttribute('data-toolbar-tray')) {
- var activeTab = this.model.get('activeTab');
- var clickedTab = event.target;
- // Set the event target as the active item if it is not already.
- this.model.set('activeTab', (!activeTab || clickedTab !== activeTab) ? clickedTab : null);
- event.preventDefault();
- event.stopPropagation();
- }
- },
- /**
- * Toggles the orientation of a toolbar tray.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- onOrientationToggleClick: function (event) {
- var orientation = this.model.get('orientation');
- // Determine the toggle-to orientation.
- var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
- var locked = antiOrientation === 'vertical';
- // Remember the locked state.
- if (locked) {
- localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true');
- }
- else {
- localStorage.removeItem('Drupal.toolbar.trayVerticalLocked');
- }
- // Update the model.
- this.model.set({
- locked: locked,
- orientation: antiOrientation
- }, {
- validate: true,
- override: true
- });
- event.preventDefault();
- event.stopPropagation();
- },
- /**
- * Updates the display of the tabs: toggles a tab and the associated tray.
- */
- updateTabs: function () {
- var $tab = $(this.model.get('activeTab'));
- // Deactivate the previous tab.
- $(this.model.previous('activeTab'))
- .removeClass('is-active')
- .prop('aria-pressed', false);
- // Deactivate the previous tray.
- $(this.model.previous('activeTray'))
- .removeClass('is-active');
- // Activate the selected tab.
- if ($tab.length > 0) {
- $tab
- .addClass('is-active')
- // Mark the tab as pressed.
- .prop('aria-pressed', true);
- var name = $tab.attr('data-toolbar-tray');
- // Store the active tab name or remove the setting.
- var id = $tab.get(0).id;
- if (id) {
- localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify(id));
- }
- // Activate the associated tray.
- var $tray = this.$el.find('[data-toolbar-tray="' + name + '"].toolbar-tray');
- if ($tray.length) {
- $tray.addClass('is-active');
- this.model.set('activeTray', $tray.get(0));
- }
- else {
- // There is no active tray.
- this.model.set('activeTray', null);
- }
- }
- else {
- // There is no active tray.
- this.model.set('activeTray', null);
- localStorage.removeItem('Drupal.toolbar.activeTabID');
- }
- },
- /**
- * Update the attributes of the toolbar bar element.
- */
- updateBarAttributes: function () {
- var isOriented = this.model.get('isOriented');
- if (isOriented) {
- this.$el.find('.toolbar-bar').attr('data-offset-top', '');
- }
- else {
- this.$el.find('.toolbar-bar').removeAttr('data-offset-top');
- }
- // Toggle between a basic vertical view and a more sophisticated
- // horizontal and vertical display of the toolbar bar and trays.
- this.$el.toggleClass('toolbar-oriented', isOriented);
- },
- /**
- * Updates the orientation of the active tray if necessary.
- */
- updateTrayOrientation: function () {
- var orientation = this.model.get('orientation');
- // The antiOrientation is used to render the view of action buttons like
- // the tray orientation toggle.
- var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
- // Update the orientation of the trays.
- var $trays = this.$el.find('.toolbar-tray')
- .removeClass('toolbar-tray-horizontal toolbar-tray-vertical')
- .addClass('toolbar-tray-' + orientation);
- // Update the tray orientation toggle button.
- var iconClass = 'toolbar-icon-toggle-' + orientation;
- var iconAntiClass = 'toolbar-icon-toggle-' + antiOrientation;
- var $orientationToggle = this.$el.find('.toolbar-toggle-orientation')
- .toggle(this.model.get('isTrayToggleVisible'));
- $orientationToggle.find('button')
- .val(antiOrientation)
- .attr('title', this.strings[antiOrientation])
- .text(this.strings[antiOrientation])
- .removeClass(iconClass)
- .addClass(iconAntiClass);
- // Update data offset attributes for the trays.
- var dir = document.documentElement.dir;
- var edge = (dir === 'rtl') ? 'right' : 'left';
- // Remove data-offset attributes from the trays so they can be refreshed.
- $trays.removeAttr('data-offset-left data-offset-right data-offset-top');
- // If an active vertical tray exists, mark it as an offset element.
- $trays.filter('.toolbar-tray-vertical.is-active').attr('data-offset-' + edge, '');
- // If an active horizontal tray exists, mark it as an offset element.
- $trays.filter('.toolbar-tray-horizontal.is-active').attr('data-offset-top', '');
- },
- /**
- * Sets the tops of the trays so that they align with the bottom of the bar.
- */
- adjustPlacement: function () {
- var $trays = this.$el.find('.toolbar-tray');
- if (!this.model.get('isOriented')) {
- $trays.css('margin-top', 0);
- $trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
- }
- else {
- // The toolbar container is invisible. Its placement is used to
- // determine the container for the trays.
- $trays.css('margin-top', this.$el.find('.toolbar-bar').outerHeight());
- }
- },
- /**
- * Calls the endpoint URI that builds an AJAX command with the rendered
- * subtrees.
- *
- * The rendered admin menu subtrees HTML is cached on the client in
- * localStorage until the cache of the admin menu subtrees on the server-
- * side is invalidated. The subtreesHash is stored in localStorage as well
- * and compared to the subtreesHash in drupalSettings to determine when the
- * admin menu subtrees cache has been invalidated.
- */
- loadSubtrees: function () {
- var $activeTab = $(this.model.get('activeTab'));
- var orientation = this.model.get('orientation');
- // Only load and render the admin menu subtrees if:
- // (1) They have not been loaded yet.
- // (2) The active tab is the administration menu tab, indicated by the
- // presence of the data-drupal-subtrees attribute.
- // (3) The orientation of the tray is vertical.
- if (!this.model.get('areSubtreesLoaded') && typeof $activeTab.data('drupal-subtrees') !== 'undefined' && orientation === 'vertical') {
- var subtreesHash = drupalSettings.toolbar.subtreesHash;
- var theme = drupalSettings.ajaxPageState.theme;
- var endpoint = Drupal.url('toolbar/subtrees/' + subtreesHash);
- var cachedSubtreesHash = localStorage.getItem('Drupal.toolbar.subtreesHash.' + theme);
- var cachedSubtrees = JSON.parse(localStorage.getItem('Drupal.toolbar.subtrees.' + theme));
- var isVertical = this.model.get('orientation') === 'vertical';
- // If we have the subtrees in localStorage and the subtree hash has not
- // changed, then use the cached data.
- if (isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees) {
- Drupal.toolbar.setSubtrees.resolve(cachedSubtrees);
- }
- // Only make the call to get the subtrees if the orientation of the
- // toolbar is vertical.
- else if (isVertical) {
- // Remove the cached menu information.
- localStorage.removeItem('Drupal.toolbar.subtreesHash.' + theme);
- localStorage.removeItem('Drupal.toolbar.subtrees.' + theme);
- // The AJAX response's command will trigger the resolve method of the
- // Drupal.toolbar.setSubtrees Promise.
- Drupal.ajax({url: endpoint}).execute();
- // Cache the hash for the subtrees locally.
- localStorage.setItem('Drupal.toolbar.subtreesHash.' + theme, subtreesHash);
- }
- }
- }
- });
- }(jQuery, Drupal, drupalSettings, Backbone));
- ;
- /*! jquery.cookie v1.4.1 | MIT */
- !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});;
- /* jQuery Foundation Joyride Plugin 2.1 | Copyright 2012, ZURB | www.opensource.org/licenses/mit-license.php */
- (function(e,t,n){"use strict";var r={version:"2.0.3",tipLocation:"bottom",nubPosition:"auto",scroll:!0,scrollSpeed:300,timer:0,autoStart:!1,startTimerOnClick:!0,startOffset:0,nextButton:!0,tipAnimation:"fade",pauseAfter:[],tipAnimationFadeSpeed:300,cookieMonster:!1,cookieName:"joyride",cookieDomain:!1,cookiePath:!1,localStorage:!1,localStorageKey:"joyride",tipContainer:"body",modal:!1,expose:!1,postExposeCallback:e.noop,preRideCallback:e.noop,postRideCallback:e.noop,preStepCallback:e.noop,postStepCallback:e.noop,template:{link:'<a href="#close" class="joyride-close-tip">X</a>',timer:'<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',tip:'<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',wrapper:'<div class="joyride-content-wrapper" role="dialog"></div>',button:'<a href="#" class="joyride-next-tip"></a>',modal:'<div class="joyride-modal-bg"></div>',expose:'<div class="joyride-expose-wrapper"></div>',exposeCover:'<div class="joyride-expose-cover"></div>'}},i=i||!1,s={},o={init:function(n){return this.each(function(){e.isEmptyObject(s)?(s=e.extend(!0,r,n),s.document=t.document,s.$document=e(s.document),s.$window=e(t),s.$content_el=e(this),s.$body=e(s.tipContainer),s.body_offset=e(s.tipContainer).position(),s.$tip_content=e("> li",s.$content_el),s.paused=!1,s.attempts=0,s.tipLocationPatterns={top:["bottom"],bottom:[],left:["right","top","bottom"],right:["left","top","bottom"]},o.jquery_check(),e.isFunction(e.cookie)||(s.cookieMonster=!1),(!s.cookieMonster||!e.cookie(s.cookieName))&&(!s.localStorage||!o.support_localstorage()||!localStorage.getItem(s.localStorageKey))&&(s.$tip_content.each(function(t){o.create({$li:e(this),index:t})}),s.autoStart&&(!s.startTimerOnClick&&s.timer>0?(o.show("init"),o.startTimer()):o.show("init"))),s.$document.on("click.joyride",".joyride-next-tip, .joyride-modal-bg",function(e){e.preventDefault(),s.$li.next().length<1?o.end():s.timer>0?(clearTimeout(s.automate),o.hide(),o.show(),o.startTimer()):(o.hide(),o.show())}),s.$document.on("click.joyride",".joyride-close-tip",function(e){e.preventDefault(),o.end()}),s.$window.bind("resize.joyride",function(t){if(s.$li){if(s.exposed&&s.exposed.length>0){var n=e(s.exposed);n.each(function(){var t=e(this);o.un_expose(t),o.expose(t)})}o.is_phone()?o.pos_phone():o.pos_default()}})):o.restart()})},resume:function(){o.set_li(),o.show()},nextTip:function(){s.$li.next().length<1?o.end():s.timer>0?(clearTimeout(s.automate),o.hide(),o.show(),o.startTimer()):(o.hide(),o.show())},tip_template:function(t){var n,r,i;return t.tip_class=t.tip_class||"",n=e(s.template.tip).addClass(t.tip_class),r=e.trim(e(t.li).html())+o.button_text(t.button_text)+s.template.link+o.timer_instance(t.index),i=e(s.template.wrapper),t.li.attr("data-aria-labelledby")&&i.attr("aria-labelledby",t.li.attr("data-aria-labelledby")),t.li.attr("data-aria-describedby")&&i.attr("aria-describedby",t.li.attr("data-aria-describedby")),n.append(i),n.first().attr("data-index",t.index),e(".joyride-content-wrapper",n).append(r),n[0]},timer_instance:function(t){var n;return t===0&&s.startTimerOnClick&&s.timer>0||s.timer===0?n="":n=o.outerHTML(e(s.template.timer)[0]),n},button_text:function(t){return s.nextButton?(t=e.trim(t)||"Next",t=o.outerHTML(e(s.template.button).append(t)[0])):t="",t},create:function(t){var n=t.$li.attr("data-button")||t.$li.attr("data-text"),r=t.$li.attr("class"),i=e(o.tip_template({tip_class:r,index:t.index,button_text:n,li:t.$li}));e(s.tipContainer).append(i)},show:function(t){var r={},i,u=[],a=0,f,l=null;if(s.$li===n||e.inArray(s.$li.index(),s.pauseAfter)===-1){s.paused?s.paused=!1:o.set_li(t),s.attempts=0;if(s.$li.length&&s.$target.length>0){t&&(s.preRideCallback(s.$li.index(),s.$next_tip),s.modal&&o.show_modal()),s.preStepCallback(s.$li.index(),s.$next_tip),u=(s.$li.data("options")||":").split(";"),a=u.length;for(i=a-1;i>=0;i--)f=u[i].split(":"),f.length===2&&(r[e.trim(f[0])]=e.trim(f[1]));s.tipSettings=e.extend({},s,r),s.tipSettings.tipLocationPattern=s.tipLocationPatterns[s.tipSettings.tipLocation],s.modal&&s.expose&&o.expose(),!/body/i.test(s.$target.selector)&&s.scroll&&o.scroll_to(),o.is_phone()?o.pos_phone(!0):o.pos_default(!0),l=e(".joyride-timer-indicator",s.$next_tip),/pop/i.test(s.tipAnimation)?(l.outerWidth(0),s.timer>0?(s.$next_tip.show(),l.animate({width:e(".joyride-timer-indicator-wrap",s.$next_tip).outerWidth()},s.timer)):s.$next_tip.show()):/fade/i.test(s.tipAnimation)&&(l.outerWidth(0),s.timer>0?(s.$next_tip.fadeIn(s.tipAnimationFadeSpeed),s.$next_tip.show(),l.animate({width:e(".joyride-timer-indicator-wrap",s.$next_tip).outerWidth()},s.timer)):s.$next_tip.fadeIn(s.tipAnimationFadeSpeed)),s.$current_tip=s.$next_tip,e(".joyride-next-tip",s.$current_tip).focus(),o.tabbable(s.$current_tip)}else s.$li&&s.$target.length<1?o.show():o.end()}else s.paused=!0},is_phone:function(){return i?i.mq("only screen and (max-width: 767px)"):s.$window.width()<767?!0:!1},support_localstorage:function(){return i?i.localstorage:!!t.localStorage},hide:function(){s.modal&&s.expose&&o.un_expose(),s.modal||e(".joyride-modal-bg").hide(),s.$current_tip.hide(),s.postStepCallback(s.$li.index(),s.$current_tip)},set_li:function(e){e?(s.$li=s.$tip_content.eq(s.startOffset),o.set_next_tip(),s.$current_tip=s.$next_tip):(s.$li=s.$li.next(),o.set_next_tip()),o.set_target()},set_next_tip:function(){s.$next_tip=e(".joyride-tip-guide[data-index="+s.$li.index()+"]")},set_target:function(){var t=s.$li.attr("data-class"),n=s.$li.attr("data-id"),r=function(){return n?e(s.document.getElementById(n)):t?e("."+t).filter(":visible").first():e("body")};s.$target=r()},scroll_to:function(){var t,n;t=s.$window.height()/2,n=Math.ceil(s.$target.offset().top-t+s.$next_tip.outerHeight()),e("html, body").stop().animate({scrollTop:n},s.scrollSpeed)},paused:function(){return e.inArray(s.$li.index()+1,s.pauseAfter)===-1?!0:!1},destroy:function(){e.isEmptyObject(s)||s.$document.off(".joyride"),e(t).off(".joyride"),e(".joyride-close-tip, .joyride-next-tip, .joyride-modal-bg").off(".joyride"),e(".joyride-tip-guide, .joyride-modal-bg").remove(),clearTimeout(s.automate),s={}},restart:function(){s.autoStart?(o.hide(),s.$li=n,o.show("init")):(!s.startTimerOnClick&&s.timer>0?(o.show("init"),o.startTimer()):o.show("init"),s.autoStart=!0)},pos_default:function(t){var n=Math.ceil(s.$window.height()/2),r=s.$next_tip.offset(),i=e(".joyride-nub",s.$next_tip),u=Math.ceil(i.outerWidth()/2),a=Math.ceil(i.outerHeight()/2),f=t||!1;f&&(s.$next_tip.css("visibility","hidden"),s.$next_tip.show());if(!/body/i.test(s.$target.selector)){var l=s.tipSettings.tipAdjustmentY?parseInt(s.tipSettings.tipAdjustmentY):0,c=s.tipSettings.tipAdjustmentX?parseInt(s.tipSettings.tipAdjustmentX):0;o.bottom()?(s.$next_tip.css({top:s.$target.offset().top+a+s.$target.outerHeight()+l,left:s.$target.offset().left+c}),/right/i.test(s.tipSettings.nubPosition)&&s.$next_tip.css("left",s.$target.offset().left-s.$next_tip.outerWidth()+s.$target.outerWidth()),o.nub_position(i,s.tipSettings.nubPosition,"top")):o.top()?(s.$next_tip.css({top:s.$target.offset().top-s.$next_tip.outerHeight()-a+l,left:s.$target.offset().left+c}),o.nub_position(i,s.tipSettings.nubPosition,"bottom")):o.right()?(s.$next_tip.css({top:s.$target.offset().top+l,left:s.$target.outerWidth()+s.$target.offset().left+u+c}),o.nub_position(i,s.tipSettings.nubPosition,"left")):o.left()&&(s.$next_tip.css({top:s.$target.offset().top+l,left:s.$target.offset().left-s.$next_tip.outerWidth()-u+c}),o.nub_position(i,s.tipSettings.nubPosition,"right")),!o.visible(o.corners(s.$next_tip))&&s.attempts<s.tipSettings.tipLocationPattern.length&&(i.removeClass("bottom").removeClass("top").removeClass("right").removeClass("left"),s.tipSettings.tipLocation=s.tipSettings.tipLocationPattern[s.attempts],s.attempts++,o.pos_default(!0))}else s.$li.length&&o.pos_modal(i);f&&(s.$next_tip.hide(),s.$next_tip.css("visibility","visible"))},pos_phone:function(t){var n=s.$next_tip.outerHeight(),r=s.$next_tip.offset(),i=s.$target.outerHeight(),u=e(".joyride-nub",s.$next_tip),a=Math.ceil(u.outerHeight()/2),f=t||!1;u.removeClass("bottom").removeClass("top").removeClass("right").removeClass("left"),f&&(s.$next_tip.css("visibility","hidden"),s.$next_tip.show()),/body/i.test(s.$target.selector)?s.$li.length&&o.pos_modal(u):o.top()?(s.$next_tip.offset({top:s.$target.offset().top-n-a}),u.addClass("bottom")):(s.$next_tip.offset({top:s.$target.offset().top+i+a}),u.addClass("top")),f&&(s.$next_tip.hide(),s.$next_tip.css("visibility","visible"))},pos_modal:function(e){o.center(),e.hide(),o.show_modal()},show_modal:function(){e(".joyride-modal-bg").length<1&&e("body").append(s.template.modal).show(),/pop/i.test(s.tipAnimation)?e(".joyride-modal-bg").show():e(".joyride-modal-bg").fadeIn(s.tipAnimationFadeSpeed)},expose:function(){var n,r,i,u,a="expose-"+Math.floor(Math.random()*1e4);if(arguments.length>0&&arguments[0]instanceof e)i=arguments[0];else{if(!s.$target||!!/body/i.test(s.$target.selector))return!1;i=s.$target}if(i.length<1)return t.console&&console.error("element not valid",i),!1;n=e(s.template.expose),s.$body.append(n),n.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),r=e(s.template.exposeCover),u={zIndex:i.css("z-index"),position:i.css("position")},i.css("z-index",n.css("z-index")*1+1),u.position=="static"&&i.css("position","relative"),i.data("expose-css",u),r.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),s.$body.append(r),n.addClass(a),r.addClass(a),s.tipSettings.exposeClass&&(n.addClass(s.tipSettings.exposeClass),r.addClass(s.tipSettings.exposeClass)),i.data("expose",a),s.postExposeCallback(s.$li.index(),s.$next_tip,i),o.add_exposed(i)},un_expose:function(){var n,r,i,u,a=!1;if(arguments.length>0&&arguments[0]instanceof e)r=arguments[0];else{if(!s.$target||!!/body/i.test(s.$target.selector))return!1;r=s.$target}if(r.length<1)return t.console&&console.error("element not valid",r),!1;n=r.data("expose"),i=e("."+n),arguments.length>1&&(a=arguments[1]),a===!0?e(".joyride-expose-wrapper,.joyride-expose-cover").remove():i.remove(),u=r.data("expose-css"),u.zIndex=="auto"?r.css("z-index",""):r.css("z-index",u.zIndex),u.position!=r.css("position")&&(u.position=="static"?r.css("position",""):r.css("position",u.position)),r.removeData("expose"),r.removeData("expose-z-index"),o.remove_exposed(r)},add_exposed:function(t){s.exposed=s.exposed||[],t instanceof e?s.exposed.push(t[0]):typeof t=="string"&&s.exposed.push(t)},remove_exposed:function(t){var n;t instanceof e?n=t[0]:typeof t=="string"&&(n=t),s.exposed=s.exposed||[];for(var r=0;r<s.exposed.length;r++)if(s.exposed[r]==n){s.exposed.splice(r,1);return}},center:function(){var e=s.$window;return s.$next_tip.css({top:(e.height()-s.$next_tip.outerHeight())/2+e.scrollTop(),left:(e.width()-s.$next_tip.outerWidth())/2+e.scrollLeft()}),!0},bottom:function(){return/bottom/i.test(s.tipSettings.tipLocation)},top:function(){return/top/i.test(s.tipSettings.tipLocation)},right:function(){return/right/i.test(s.tipSettings.tipLocation)},left:function(){return/left/i.test(s.tipSettings.tipLocation)},corners:function(e){var t=s.$window,n=t.height()/2,r=Math.ceil(s.$target.offset().top-n+s.$next_tip.outerHeight()),i=t.width()+t.scrollLeft(),o=t.height()+r,u=t.height()+t.scrollTop(),a=t.scrollTop();return r<a&&(r<0?a=0:a=r),o>u&&(u=o),[e.offset().top<a,i<e.offset().left+e.outerWidth(),u<e.offset().top+e.outerHeight(),t.scrollLeft()>e.offset().left]},visible:function(e){var t=e.length;while(t--)if(e[t])return!1;return!0},nub_position:function(e,t,n){t==="auto"?e.addClass(n):e.addClass(t)},startTimer:function(){s.$li.length?s.automate=setTimeout(function(){o.hide(),o.show(),o.startTimer()},s.timer):clearTimeout(s.automate)},end:function(){s.cookieMonster&&e.cookie(s.cookieName,"ridden",{expires:365,domain:s.cookieDomain,path:s.cookiePath}),s.localStorage&&localStorage.setItem(s.localStorageKey,!0),s.timer>0&&clearTimeout(s.automate),s.modal&&s.expose&&o.un_expose(),s.$current_tip&&s.$current_tip.hide(),s.$li&&(s.postStepCallback(s.$li.index(),s.$current_tip),s.postRideCallback(s.$li.index(),s.$current_tip)),e(".joyride-modal-bg").hide()},jquery_check:function(){return e.isFunction(e.fn.on)?!0:(e.fn.on=function(e,t,n){return this.delegate(t,e,n)},e.fn.off=function(e,t,n){return this.undelegate(t,e,n)},!1)},outerHTML:function(e){return e.outerHTML||(new XMLSerializer).serializeToString(e)},version:function(){return s.version},tabbable:function(t){e(t).on("keydown",function(n){if(!n.isDefaultPrevented()&&n.keyCode&&n.keyCode===27){n.preventDefault(),o.end();return}if(n.keyCode!==9)return;var r=e(t).find(":tabbable"),i=r.filter(":first"),s=r.filter(":last");n.target===s[0]&&!n.shiftKey?(i.focus(1),n.preventDefault()):n.target===i[0]&&n.shiftKey&&(s.focus(1),n.preventDefault())})}};e.fn.joyride=function(t){if(o[t])return o[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return o.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.joyride")}})(jQuery,this);
- ;
- /**
- * @file
- * Attaches behaviors for the Tour module's toolbar tab.
- */
- (function ($, Backbone, Drupal, document) {
- 'use strict';
- var queryString = decodeURI(window.location.search);
- /**
- * Attaches the tour's toolbar tab behavior.
- *
- * It uses the query string for:
- * - tour: When ?tour=1 is present, the tour will start automatically after
- * the page has loaded.
- * - tips: Pass ?tips=class in the url to filter the available tips to the
- * subset which match the given class.
- *
- * @example
- * http://example.com/foo?tour=1&tips=bar
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attach tour functionality on `tour` events.
- */
- Drupal.behaviors.tour = {
- attach: function (context) {
- $('body').once('tour').each(function () {
- var model = new Drupal.tour.models.StateModel();
- new Drupal.tour.views.ToggleTourView({
- el: $(context).find('#toolbar-tab-tour'),
- model: model
- });
- model
- // Allow other scripts to respond to tour events.
- .on('change:isActive', function (model, isActive) {
- $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped');
- })
- // Initialization: check whether a tour is available on the current
- // page.
- .set('tour', $(context).find('ol#tour'));
- // Start the tour immediately if toggled via query string.
- if (/tour=?/i.test(queryString)) {
- model.set('isActive', true);
- }
- });
- }
- };
- /**
- * @namespace
- */
- Drupal.tour = Drupal.tour || {
- /**
- * @namespace Drupal.tour.models
- */
- models: {},
- /**
- * @namespace Drupal.tour.views
- */
- views: {}
- };
- /**
- * Backbone Model for tours.
- *
- * @constructor
- *
- * @augments Backbone.Model
- */
- Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{
- /**
- * @type {object}
- */
- defaults: /** @lends Drupal.tour.models.StateModel# */{
- /**
- * Indicates whether the Drupal root window has a tour.
- *
- * @type {Array}
- */
- tour: [],
- /**
- * Indicates whether the tour is currently running.
- *
- * @type {bool}
- */
- isActive: false,
- /**
- * Indicates which tour is the active one (necessary to cleanly stop).
- *
- * @type {Array}
- */
- activeTour: []
- }
- });
- Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{
- /**
- * @type {object}
- */
- events: {click: 'onClick'},
- /**
- * Handles edit mode toggle interactions.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize: function () {
- this.listenTo(this.model, 'change:tour change:isActive', this.render);
- this.listenTo(this.model, 'change:isActive', this.toggleTour);
- },
- /**
- * @inheritdoc
- *
- * @return {Drupal.tour.views.ToggleTourView}
- * The `ToggleTourView` view.
- */
- render: function () {
- // Render the visibility.
- this.$el.toggleClass('hidden', this._getTour().length === 0);
- // Render the state.
- var isActive = this.model.get('isActive');
- this.$el.find('button')
- .toggleClass('is-active', isActive)
- .prop('aria-pressed', isActive);
- return this;
- },
- /**
- * Model change handler; starts or stops the tour.
- */
- toggleTour: function () {
- if (this.model.get('isActive')) {
- var $tour = this._getTour();
- this._removeIrrelevantTourItems($tour, this._getDocument());
- var that = this;
- if ($tour.find('li').length) {
- $tour.joyride({
- autoStart: true,
- postRideCallback: function () { that.model.set('isActive', false); },
- // HTML segments for tip layout.
- template: {
- link: '<a href=\"#close\" class=\"joyride-close-tip\">×</a>',
- button: '<a href=\"#\" class=\"button button--primary joyride-next-tip\"></a>'
- }
- });
- this.model.set({isActive: true, activeTour: $tour});
- }
- }
- else {
- this.model.get('activeTour').joyride('destroy');
- this.model.set({isActive: false, activeTour: []});
- }
- },
- /**
- * Toolbar tab click event handler; toggles isActive.
- *
- * @param {jQuery.Event} event
- * The click event.
- */
- onClick: function (event) {
- this.model.set('isActive', !this.model.get('isActive'));
- event.preventDefault();
- event.stopPropagation();
- },
- /**
- * Gets the tour.
- *
- * @return {jQuery}
- * A jQuery element pointing to a `<ol>` containing tour items.
- */
- _getTour: function () {
- return this.model.get('tour');
- },
- /**
- * Gets the relevant document as a jQuery element.
- *
- * @return {jQuery}
- * A jQuery element pointing to the document within which a tour would be
- * started given the current state.
- */
- _getDocument: function () {
- return $(document);
- },
- /**
- * Removes tour items for elements that don't have matching page elements.
- *
- * Or that are explicitly filtered out via the 'tips' query string.
- *
- * @example
- * <caption>This will filter out tips that do not have a matching
- * page element or don't have the "bar" class.</caption>
- * http://example.com/foo?tips=bar
- *
- * @param {jQuery} $tour
- * A jQuery element pointing to a `<ol>` containing tour items.
- * @param {jQuery} $document
- * A jQuery element pointing to the document within which the elements
- * should be sought.
- *
- * @see Drupal.tour.views.ToggleTourView#_getDocument
- */
- _removeIrrelevantTourItems: function ($tour, $document) {
- var removals = false;
- var tips = /tips=([^&]+)/.exec(queryString);
- $tour
- .find('li')
- .each(function () {
- var $this = $(this);
- var itemId = $this.attr('data-id');
- var itemClass = $this.attr('data-class');
- // If the query parameter 'tips' is set, remove all tips that don't
- // have the matching class.
- if (tips && !$(this).hasClass(tips[1])) {
- removals = true;
- $this.remove();
- return;
- }
- // Remove tip from the DOM if there is no corresponding page element.
- if ((!itemId && !itemClass) ||
- (itemId && $document.find('#' + itemId).length) ||
- (itemClass && $document.find('.' + itemClass).length)) {
- return;
- }
- removals = true;
- $this.remove();
- });
- // If there were removals, we'll have to do some clean-up.
- if (removals) {
- var total = $tour.find('li').length;
- if (!total) {
- this.model.set({tour: []});
- }
- $tour
- .find('li')
- // Rebuild the progress data.
- .each(function (index) {
- var progress = Drupal.t('!tour_item of !total', {'!tour_item': index + 1, '!total': total});
- $(this).find('.tour-progress').text(progress);
- })
- // Update the last item to have "End tour" as the button.
- .eq(-1)
- .attr('data-text', Drupal.t('End tour'));
- }
- }
- });
- })(jQuery, Backbone, Drupal, document);
- ;
- /**
- * @file
- * Manages page tabbing modifications made by modules.
- */
- /**
- * Allow modules to respond to the constrain event.
- *
- * @event drupalTabbingConstrained
- */
- /**
- * Allow modules to respond to the tabbingContext release event.
- *
- * @event drupalTabbingContextReleased
- */
- /**
- * Allow modules to respond to the constrain event.
- *
- * @event drupalTabbingContextActivated
- */
- /**
- * Allow modules to respond to the constrain event.
- *
- * @event drupalTabbingContextDeactivated
- */
- (function ($, Drupal) {
- 'use strict';
- /**
- * Provides an API for managing page tabbing order modifications.
- *
- * @constructor Drupal~TabbingManager
- */
- function TabbingManager() {
- /**
- * Tabbing sets are stored as a stack. The active set is at the top of the
- * stack. We use a JavaScript array as if it were a stack; we consider the
- * first element to be the bottom and the last element to be the top. This
- * allows us to use JavaScript's built-in Array.push() and Array.pop()
- * methods.
- *
- * @type {Array.<Drupal~TabbingContext>}
- */
- this.stack = [];
- }
- /**
- * Add public methods to the TabbingManager class.
- */
- $.extend(TabbingManager.prototype, /** @lends Drupal~TabbingManager# */{
- /**
- * Constrain tabbing to the specified set of elements only.
- *
- * Makes elements outside of the specified set of elements unreachable via
- * the tab key.
- *
- * @param {jQuery} elements
- * The set of elements to which tabbing should be constrained. Can also
- * be a jQuery-compatible selector string.
- *
- * @return {Drupal~TabbingContext}
- * The TabbingContext instance.
- *
- * @fires event:drupalTabbingConstrained
- */
- constrain: function (elements) {
- // Deactivate all tabbingContexts to prepare for the new constraint. A
- // tabbingContext instance will only be reactivated if the stack is
- // unwound to it in the _unwindStack() method.
- var il = this.stack.length;
- for (var i = 0; i < il; i++) {
- this.stack[i].deactivate();
- }
- // The "active tabbing set" are the elements tabbing should be constrained
- // to.
- var $elements = $(elements).find(':tabbable').addBack(':tabbable');
- var tabbingContext = new TabbingContext({
- // The level is the current height of the stack before this new
- // tabbingContext is pushed on top of the stack.
- level: this.stack.length,
- $tabbableElements: $elements
- });
- this.stack.push(tabbingContext);
- // Activates the tabbingContext; this will manipulate the DOM to constrain
- // tabbing.
- tabbingContext.activate();
- // Allow modules to respond to the constrain event.
- $(document).trigger('drupalTabbingConstrained', tabbingContext);
- return tabbingContext;
- },
- /**
- * Restores a former tabbingContext when an active one is released.
- *
- * The TabbingManager stack of tabbingContext instances will be unwound
- * from the top-most released tabbingContext down to the first non-released
- * tabbingContext instance. This non-released instance is then activated.
- */
- release: function () {
- // Unwind as far as possible: find the topmost non-released
- // tabbingContext.
- var toActivate = this.stack.length - 1;
- while (toActivate >= 0 && this.stack[toActivate].released) {
- toActivate--;
- }
- // Delete all tabbingContexts after the to be activated one. They have
- // already been deactivated, so their effect on the DOM has been reversed.
- this.stack.splice(toActivate + 1);
- // Get topmost tabbingContext, if one exists, and activate it.
- if (toActivate >= 0) {
- this.stack[toActivate].activate();
- }
- },
- /**
- * Makes all elements outside of the tabbingContext's set untabbable.
- *
- * Elements made untabbable have their original tabindex and autofocus
- * values stored so that they might be restored later when this
- * tabbingContext is deactivated.
- *
- * @param {Drupal~TabbingContext} tabbingContext
- * The TabbingContext instance that has been activated.
- */
- activate: function (tabbingContext) {
- var $set = tabbingContext.$tabbableElements;
- var level = tabbingContext.level;
- // Determine which elements are reachable via tabbing by default.
- var $disabledSet = $(':tabbable')
- // Exclude elements of the active tabbing set.
- .not($set);
- // Set the disabled set on the tabbingContext.
- tabbingContext.$disabledElements = $disabledSet;
- // Record the tabindex for each element, so we can restore it later.
- var il = $disabledSet.length;
- for (var i = 0; i < il; i++) {
- this.recordTabindex($disabledSet.eq(i), level);
- }
- // Make all tabbable elements outside of the active tabbing set
- // unreachable.
- $disabledSet
- .prop('tabindex', -1)
- .prop('autofocus', false);
- // Set focus on an element in the tabbingContext's set of tabbable
- // elements. First, check if there is an element with an autofocus
- // attribute. Select the last one from the DOM order.
- var $hasFocus = $set.filter('[autofocus]').eq(-1);
- // If no element in the tabbable set has an autofocus attribute, select
- // the first element in the set.
- if ($hasFocus.length === 0) {
- $hasFocus = $set.eq(0);
- }
- $hasFocus.trigger('focus');
- },
- /**
- * Restores that tabbable state of a tabbingContext's disabled elements.
- *
- * Elements that were made untabbable have their original tabindex and
- * autofocus values restored.
- *
- * @param {Drupal~TabbingContext} tabbingContext
- * The TabbingContext instance that has been deactivated.
- */
- deactivate: function (tabbingContext) {
- var $set = tabbingContext.$disabledElements;
- var level = tabbingContext.level;
- var il = $set.length;
- for (var i = 0; i < il; i++) {
- this.restoreTabindex($set.eq(i), level);
- }
- },
- /**
- * Records the tabindex and autofocus values of an untabbable element.
- *
- * @param {jQuery} $el
- * The set of elements that have been disabled.
- * @param {number} level
- * The stack level for which the tabindex attribute should be recorded.
- */
- recordTabindex: function ($el, level) {
- var tabInfo = $el.data('drupalOriginalTabIndices') || {};
- tabInfo[level] = {
- tabindex: $el[0].getAttribute('tabindex'),
- autofocus: $el[0].hasAttribute('autofocus')
- };
- $el.data('drupalOriginalTabIndices', tabInfo);
- },
- /**
- * Restores the tabindex and autofocus values of a reactivated element.
- *
- * @param {jQuery} $el
- * The element that is being reactivated.
- * @param {number} level
- * The stack level for which the tabindex attribute should be restored.
- */
- restoreTabindex: function ($el, level) {
- var tabInfo = $el.data('drupalOriginalTabIndices');
- if (tabInfo && tabInfo[level]) {
- var data = tabInfo[level];
- if (data.tabindex) {
- $el[0].setAttribute('tabindex', data.tabindex);
- }
- // If the element did not have a tabindex at this stack level then
- // remove it.
- else {
- $el[0].removeAttribute('tabindex');
- }
- if (data.autofocus) {
- $el[0].setAttribute('autofocus', 'autofocus');
- }
- // Clean up $.data.
- if (level === 0) {
- // Remove all data.
- $el.removeData('drupalOriginalTabIndices');
- }
- else {
- // Remove the data for this stack level and higher.
- var levelToDelete = level;
- while (tabInfo.hasOwnProperty(levelToDelete)) {
- delete tabInfo[levelToDelete];
- levelToDelete++;
- }
- $el.data('drupalOriginalTabIndices', tabInfo);
- }
- }
- }
- });
- /**
- * Stores a set of tabbable elements.
- *
- * This constraint can be removed with the release() method.
- *
- * @constructor Drupal~TabbingContext
- *
- * @param {object} options
- * A set of initiating values
- * @param {number} options.level
- * The level in the TabbingManager's stack of this tabbingContext.
- * @param {jQuery} options.$tabbableElements
- * The DOM elements that should be reachable via the tab key when this
- * tabbingContext is active.
- * @param {jQuery} options.$disabledElements
- * The DOM elements that should not be reachable via the tab key when this
- * tabbingContext is active.
- * @param {bool} options.released
- * A released tabbingContext can never be activated again. It will be
- * cleaned up when the TabbingManager unwinds its stack.
- * @param {bool} options.active
- * When true, the tabbable elements of this tabbingContext will be reachable
- * via the tab key and the disabled elements will not. Only one
- * tabbingContext can be active at a time.
- */
- function TabbingContext(options) {
- $.extend(this, /** @lends Drupal~TabbingContext# */{
- /**
- * @type {?number}
- */
- level: null,
- /**
- * @type {jQuery}
- */
- $tabbableElements: $(),
- /**
- * @type {jQuery}
- */
- $disabledElements: $(),
- /**
- * @type {bool}
- */
- released: false,
- /**
- * @type {bool}
- */
- active: false
- }, options);
- }
- /**
- * Add public methods to the TabbingContext class.
- */
- $.extend(TabbingContext.prototype, /** @lends Drupal~TabbingContext# */{
- /**
- * Releases this TabbingContext.
- *
- * Once a TabbingContext object is released, it can never be activated
- * again.
- *
- * @fires event:drupalTabbingContextReleased
- */
- release: function () {
- if (!this.released) {
- this.deactivate();
- this.released = true;
- Drupal.tabbingManager.release(this);
- // Allow modules to respond to the tabbingContext release event.
- $(document).trigger('drupalTabbingContextReleased', this);
- }
- },
- /**
- * Activates this TabbingContext.
- *
- * @fires event:drupalTabbingContextActivated
- */
- activate: function () {
- // A released TabbingContext object can never be activated again.
- if (!this.active && !this.released) {
- this.active = true;
- Drupal.tabbingManager.activate(this);
- // Allow modules to respond to the constrain event.
- $(document).trigger('drupalTabbingContextActivated', this);
- }
- },
- /**
- * Deactivates this TabbingContext.
- *
- * @fires event:drupalTabbingContextDeactivated
- */
- deactivate: function () {
- if (this.active) {
- this.active = false;
- Drupal.tabbingManager.deactivate(this);
- // Allow modules to respond to the constrain event.
- $(document).trigger('drupalTabbingContextDeactivated', this);
- }
- }
- });
- // Mark this behavior as processed on the first pass and return if it is
- // already processed.
- if (Drupal.tabbingManager) {
- return;
- }
- /**
- * @type {Drupal~TabbingManager}
- */
- Drupal.tabbingManager = new TabbingManager();
- }(jQuery, Drupal));
- ;
- /**
- * @file
- * Attaches behaviors for the Contextual module's edit toolbar tab.
- */
- (function ($, Drupal, Backbone) {
- 'use strict';
- var strings = {
- tabbingReleased: Drupal.t('Tabbing is no longer constrained by the Contextual module.'),
- tabbingConstrained: Drupal.t('Tabbing is constrained to a set of @contextualsCount and the edit mode toggle.'),
- pressEsc: Drupal.t('Press the esc key to exit.')
- };
- /**
- * Initializes a contextual link: updates its DOM, sets up model and views.
- *
- * @param {HTMLElement} context
- * A contextual links DOM element as rendered by the server.
- */
- function initContextualToolbar(context) {
- if (!Drupal.contextual || !Drupal.contextual.collection) {
- return;
- }
- var contextualToolbar = Drupal.contextualToolbar;
- var model = contextualToolbar.model = new contextualToolbar.StateModel({
- // Checks whether localStorage indicates we should start in edit mode
- // rather than view mode.
- // @see Drupal.contextualToolbar.VisualView.persist
- isViewing: localStorage.getItem('Drupal.contextualToolbar.isViewing') !== 'false'
- }, {
- contextualCollection: Drupal.contextual.collection
- });
- var viewOptions = {
- el: $('.toolbar .toolbar-bar .contextual-toolbar-tab'),
- model: model,
- strings: strings
- };
- new contextualToolbar.VisualView(viewOptions);
- new contextualToolbar.AuralView(viewOptions);
- }
- /**
- * Attaches contextual's edit toolbar tab behavior.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches contextual toolbar behavior on a contextualToolbar-init event.
- */
- Drupal.behaviors.contextualToolbar = {
- attach: function (context) {
- if ($('body').once('contextualToolbar-init').length) {
- initContextualToolbar(context);
- }
- }
- };
- /**
- * Namespace for the contextual toolbar.
- *
- * @namespace
- */
- Drupal.contextualToolbar = {
- /**
- * The {@link Drupal.contextualToolbar.StateModel} instance.
- *
- * @type {?Drupal.contextualToolbar.StateModel}
- */
- model: null
- };
- })(jQuery, Drupal, Backbone);
- ;
- /**
- * @file
- * A Backbone Model for the state of Contextual module's edit toolbar tab.
- */
- (function (Drupal, Backbone) {
- 'use strict';
- Drupal.contextualToolbar.StateModel = Backbone.Model.extend(/** @lends Drupal.contextualToolbar.StateModel# */{
- /**
- * @type {object}
- *
- * @prop {bool} isViewing
- * @prop {bool} isVisible
- * @prop {number} contextualCount
- * @prop {Drupal~TabbingContext} tabbingContext
- */
- defaults: /** @lends Drupal.contextualToolbar.StateModel# */{
- /**
- * Indicates whether the toggle is currently in "view" or "edit" mode.
- *
- * @type {bool}
- */
- isViewing: true,
- /**
- * Indicates whether the toggle should be visible or hidden. Automatically
- * calculated, depends on contextualCount.
- *
- * @type {bool}
- */
- isVisible: false,
- /**
- * Tracks how many contextual links exist on the page.
- *
- * @type {number}
- */
- contextualCount: 0,
- /**
- * A TabbingContext object as returned by {@link Drupal~TabbingManager}:
- * the set of tabbable elements when edit mode is enabled.
- *
- * @type {?Drupal~TabbingContext}
- */
- tabbingContext: null
- },
- /**
- * Models the state of the edit mode toggle.
- *
- * @constructs
- *
- * @augments Backbone.Model
- *
- * @param {object} attrs
- * Attributes for the backbone model.
- * @param {object} options
- * An object with the following option:
- * @param {Backbone.collection} options.contextualCollection
- * The collection of {@link Drupal.contextual.StateModel} models that
- * represent the contextual links on the page.
- */
- initialize: function (attrs, options) {
- // Respond to new/removed contextual links.
- this.listenTo(options.contextualCollection, 'reset remove add', this.countContextualLinks);
- this.listenTo(options.contextualCollection, 'add', this.lockNewContextualLinks);
- // Automatically determine visibility.
- this.listenTo(this, 'change:contextualCount', this.updateVisibility);
- // Whenever edit mode is toggled, lock all contextual links.
- this.listenTo(this, 'change:isViewing', function (model, isViewing) {
- options.contextualCollection.each(function (contextualModel) {
- contextualModel.set('isLocked', !isViewing);
- });
- });
- },
- /**
- * Tracks the number of contextual link models in the collection.
- *
- * @param {Drupal.contextual.StateModel} contextualModel
- * The contextual links model that was added or removed.
- * @param {Backbone.Collection} contextualCollection
- * The collection of contextual link models.
- */
- countContextualLinks: function (contextualModel, contextualCollection) {
- this.set('contextualCount', contextualCollection.length);
- },
- /**
- * Lock newly added contextual links if edit mode is enabled.
- *
- * @param {Drupal.contextual.StateModel} contextualModel
- * The contextual links model that was added.
- * @param {Backbone.Collection} [contextualCollection]
- * The collection of contextual link models.
- */
- lockNewContextualLinks: function (contextualModel, contextualCollection) {
- if (!this.get('isViewing')) {
- contextualModel.set('isLocked', true);
- }
- },
- /**
- * Automatically updates visibility of the view/edit mode toggle.
- */
- updateVisibility: function () {
- this.set('isVisible', this.get('contextualCount') > 0);
- }
- });
- })(Drupal, Backbone);
- ;
- /**
- * @file
- * A Backbone View that provides the aural view of the edit mode toggle.
- */
- (function ($, Drupal, Backbone, _) {
- 'use strict';
- Drupal.contextualToolbar.AuralView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.AuralView# */{
- /**
- * Tracks whether the tabbing constraint announcement has been read once.
- *
- * @type {bool}
- */
- announcedOnce: false,
- /**
- * Renders the aural view of the edit mode toggle (screen reader support).
- *
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * Options for the view.
- */
- initialize: function (options) {
- this.options = options;
- this.listenTo(this.model, 'change', this.render);
- this.listenTo(this.model, 'change:isViewing', this.manageTabbing);
- $(document).on('keyup', _.bind(this.onKeypress, this));
- },
- /**
- * @inheritdoc
- *
- * @return {Drupal.contextualToolbar.AuralView}
- * The current contextual toolbar aural view.
- */
- render: function () {
- // Render the state.
- this.$el.find('button').attr('aria-pressed', !this.model.get('isViewing'));
- return this;
- },
- /**
- * Limits tabbing to the contextual links and edit mode toolbar tab.
- */
- manageTabbing: function () {
- var tabbingContext = this.model.get('tabbingContext');
- // Always release an existing tabbing context.
- if (tabbingContext) {
- tabbingContext.release();
- Drupal.announce(this.options.strings.tabbingReleased);
- }
- // Create a new tabbing context when edit mode is enabled.
- if (!this.model.get('isViewing')) {
- tabbingContext = Drupal.tabbingManager.constrain($('.contextual-toolbar-tab, .contextual'));
- this.model.set('tabbingContext', tabbingContext);
- this.announceTabbingConstraint();
- this.announcedOnce = true;
- }
- },
- /**
- * Announces the current tabbing constraint.
- */
- announceTabbingConstraint: function () {
- var strings = this.options.strings;
- Drupal.announce(Drupal.formatString(strings.tabbingConstrained, {
- '@contextualsCount': Drupal.formatPlural(Drupal.contextual.collection.length, '@count contextual link', '@count contextual links')
- }));
- Drupal.announce(strings.pressEsc);
- },
- /**
- * Responds to esc and tab key press events.
- *
- * @param {jQuery.Event} event
- * The keypress event.
- */
- onKeypress: function (event) {
- // The first tab key press is tracked so that an annoucement about tabbing
- // constraints can be raised if edit mode is enabled when the page is
- // loaded.
- if (!this.announcedOnce && event.keyCode === 9 && !this.model.get('isViewing')) {
- this.announceTabbingConstraint();
- // Set announce to true so that this conditional block won't run again.
- this.announcedOnce = true;
- }
- // Respond to the ESC key. Exit out of edit mode.
- if (event.keyCode === 27) {
- this.model.set('isViewing', true);
- }
- }
- });
- })(jQuery, Drupal, Backbone, _);
- ;
- /**
- * @file
- * A Backbone View that provides the visual view of the edit mode toggle.
- */
- (function (Drupal, Backbone) {
- 'use strict';
- Drupal.contextualToolbar.VisualView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.VisualView# */{
- /**
- * Events for the Backbone view.
- *
- * @return {object}
- * A mapping of events to be used in the view.
- */
- events: function () {
- // Prevents delay and simulated mouse events.
- var touchEndToClick = function (event) {
- event.preventDefault();
- event.target.click();
- };
- return {
- click: function () {
- this.model.set('isViewing', !this.model.get('isViewing'));
- },
- touchend: touchEndToClick
- };
- },
- /**
- * Renders the visual view of the edit mode toggle.
- *
- * Listens to mouse & touch and handles edit mode toggle interactions.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize: function () {
- this.listenTo(this.model, 'change', this.render);
- this.listenTo(this.model, 'change:isViewing', this.persist);
- },
- /**
- * @inheritdoc
- *
- * @return {Drupal.contextualToolbar.VisualView}
- * The current contextual toolbar visual view.
- */
- render: function () {
- // Render the visibility.
- this.$el.toggleClass('hidden', !this.model.get('isVisible'));
- // Render the state.
- this.$el.find('button').toggleClass('is-active', !this.model.get('isViewing'));
- return this;
- },
- /**
- * Model change handler; persists the isViewing value to localStorage.
- *
- * `isViewing === true` is the default, so only stores in localStorage when
- * it's not the default value (i.e. false).
- *
- * @param {Drupal.contextualToolbar.StateModel} model
- * A {@link Drupal.contextualToolbar.StateModel} model.
- * @param {bool} isViewing
- * The value of the isViewing attribute in the model.
- */
- persist: function (model, isViewing) {
- if (!isViewing) {
- localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false');
- }
- else {
- localStorage.removeItem('Drupal.contextualToolbar.isViewing');
- }
- }
- });
- })(Drupal, Backbone);
- ;
- /**
- * @file
- * Replaces the home link in toolbar with a back to site link.
- */
- (function ($, Drupal, drupalSettings) {
- 'use strict';
- var pathInfo = drupalSettings.path;
- var escapeAdminPath = sessionStorage.getItem('escapeAdminPath');
- var windowLocation = window.location;
- // Saves the last non-administrative page in the browser to be able to link
- // back to it when browsing administrative pages. If there is a destination
- // parameter there is not need to save the current path because the page is
- // loaded within an existing "workflow".
- if (!pathInfo.currentPathIsAdmin && !/destination=/.test(windowLocation.search)) {
- sessionStorage.setItem('escapeAdminPath', windowLocation);
- }
- /**
- * Replaces the "Home" link with "Back to site" link.
- *
- * Back to site link points to the last non-administrative page the user
- * visited within the same browser tab.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches the replacement functionality to the toolbar-escape-admin element.
- */
- Drupal.behaviors.escapeAdmin = {
- attach: function () {
- var $toolbarEscape = $('[data-toolbar-escape-admin]').once('escapeAdmin');
- if ($toolbarEscape.length && pathInfo.currentPathIsAdmin) {
- if (escapeAdminPath !== null) {
- $toolbarEscape.attr('href', escapeAdminPath);
- }
- else {
- $toolbarEscape.text(Drupal.t('Home'));
- }
- $toolbarEscape.closest('.toolbar-tab').removeClass('hidden');
- }
- }
- };
- })(jQuery, Drupal, drupalSettings);
- ;
|