/* Detect-zoom * ----------- * Cross Browser Zoom and Pixel Ratio Detector * Version 1.0.4 | Apr 1 2013 * dual-licensed under the WTFPL and MIT license * Maintained by https://github/tombigel * Original developer https://github.com/yonran */ //AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js (function (root, ns, factory) { "use strict"; if (typeof (module) !== 'undefined' && module.exports) { // CommonJS module.exports = factory(ns, root); } else if (typeof (define) === 'function' && define.amd) { // AMD define("factory", function () { return factory(ns, root); }); } else { root[ns] = factory(ns, root); } }(window, 'detectZoom', function () { /** * Use devicePixelRatio if supported by the browser * @return {Number} * @private */ var devicePixelRatio = function () { return window.devicePixelRatio || 1; }; /** * Fallback function to set default values * @return {Object} * @private */ var fallback = function () { return { zoom: 1, devicePxPerCssPx: 1 }; }; /** * IE 8 and 9: no trick needed! * TODO: Test on IE10 and Windows 8 RT * @return {Object} * @private **/ var ie8 = function () { var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * For IE10 we need to change our technique again... * thanks https://github.com/stefanvanburen * @return {Object} * @private */ var ie10 = function () { var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Mobile WebKit * the trick: window.innerWIdth is in CSS pixels, while * screen.width and screen.height are in system pixels. * And there are no scrollbars to mess up the measurement. * @return {Object} * @private */ var webkitMobile = function () { var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width; var zoom = deviceWidth / window.innerWidth; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Desktop Webkit * the trick: an element's clientHeight is in CSS pixels, while you can * set its line-height in system pixels using font-size and * -webkit-text-size-adjust:none. * device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/ * * Previous trick (used before http://trac.webkit.org/changeset/100847): * documentElement.scrollWidth is in CSS pixels, while * document.width was in system pixels. Note that this is the * layout width of the document, which is slightly different from viewport * because document width does not include scrollbars and might be wider * due to big elements. * @return {Object} * @private */ var webkit = function () { var important = function (str) { return str.replace(/;/g, " !important;"); }; var div = document.createElement('div'); div.innerHTML = "1
2
3
4
5
6
7
8
9
0"; div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;')); // The container exists so that the div will be laid out in its own flow // while not impacting the layout, viewport size, or display of the // webpage as a whole. // Add !important and relevant CSS rule resets // so that other rules cannot affect the results. var container = document.createElement('div'); container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;')); container.appendChild(div); document.body.appendChild(container); var zoom = 1000 / div.clientHeight; zoom = Math.round(zoom * 100) / 100; document.body.removeChild(container); return{ zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi. * (Note that this is a different interpretation than Webkit's device * pixel ratio, which is the ratio device dpi / system dpi). * * Also, for Mozilla, there is no difference between the zoom factor and the device ratio. * * @return {Object} * @private */ var firefox4 = function () { var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001); zoom = Math.round(zoom * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom }; }; /** * Firefox 18.x * Mozilla added support for devicePixelRatio to Firefox 18, * but it is affected by the zoom level, so, like in older * Firefox we can't tell if we are in zoom mode or in a device * with a different pixel ratio * @return {Object} * @private */ var firefox18 = function () { return { zoom: firefox4().zoom, devicePxPerCssPx: devicePixelRatio() }; }; /** * works starting Opera 11.11 * the trick: outerWidth is the viewport width including scrollbars in * system px, while innerWidth is the viewport width including scrollbars * in CSS px * @return {Object} * @private */ var opera11 = function () { var zoom = window.top.outerWidth / window.top.innerWidth; zoom = Math.round(zoom * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Use a binary search through media queries to find zoom level in Firefox * @param property * @param unit * @param a * @param b * @param maxIter * @param epsilon * @return {Number} */ var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) { var matchMedia; var head, style, div; if (window.matchMedia) { matchMedia = window.matchMedia; } else { head = document.getElementsByTagName('head')[0]; style = document.createElement('style'); head.appendChild(style); div = document.createElement('div'); div.className = 'mediaQueryBinarySearch'; div.style.display = 'none'; document.body.appendChild(div); matchMedia = function (query) { style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0); var matched = getComputedStyle(div, null).textDecoration == 'underline'; style.sheet.deleteRule(0); return {matches: matched}; }; } var ratio = binarySearch(a, b, maxIter); if (div) { head.removeChild(style); document.body.removeChild(div); } return ratio; function binarySearch(a, b, maxIter) { var mid = (a + b) / 2; if (maxIter <= 0 || b - a < epsilon) { return mid; } var query = "(" + property + ":" + mid + unit + ")"; if (matchMedia(query).matches) { return binarySearch(mid, b, maxIter - 1); } else { return binarySearch(a, mid, maxIter - 1); } } }; /** * Generate detection function * @private */ var detectFunction = (function () { var func = fallback; //IE8+ if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { func = ie8; } // IE10+ / Touch else if (window.navigator.msMaxTouchPoints) { func = ie10; } //Mobile Webkit else if ('orientation' in window && typeof document.body.style.webkitMarquee === 'string') { func = webkitMobile; } //WebKit else if (typeof document.body.style.webkitMarquee === 'string') { func = webkit; } //Opera else if (navigator.userAgent.indexOf('Opera') >= 0) { func = opera11; } //Last one is Firefox //FF 18.x else if (window.devicePixelRatio) { func = firefox18; } //FF 4.0 - 17.x else if (firefox4().zoom > 0.001) { func = firefox4; } return func; }()); return ({ /** * Ratios.zoom shorthand * @return {Number} Zoom level */ zoom: function () { return detectFunction().zoom; }, /** * Ratios.devicePxPerCssPx shorthand * @return {Number} devicePxPerCssPx level */ device: function () { return detectFunction().devicePxPerCssPx; } }); })); var wpcom_img_zoomer = { clientHintSupport: { gravatar: false, files: false, photon: false, mshots: false, staticAssets: false, latex: false, imgpress: false, }, useHints: false, zoomed: false, timer: null, interval: 1000, // zoom polling interval in millisecond // Should we apply width/height attributes to control the image size? imgNeedsSizeAtts: function( img ) { // Do not overwrite existing width/height attributes. if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null ) return false; // Do not apply the attributes if the image is already constrained by a parent element. if ( img.width < img.naturalWidth || img.height < img.naturalHeight ) return false; return true; }, hintsFor: function( service ) { if ( this.useHints === false ) { return false; } if ( this.hints() === false ) { return false; } if ( typeof this.clientHintSupport[service] === "undefined" ) { return false; } if ( this.clientHintSupport[service] === true ) { return true; } return false; }, hints: function() { try { var chrome = window.navigator.userAgent.match(/\sChrome\/([0-9]+)\.[.0-9]+\s/) if (chrome !== null) { var version = parseInt(chrome[1], 10) if (isNaN(version) === false && version >= 46) { return true } } } catch (e) { return false } return false }, init: function() { var t = this; try{ t.zoomImages(); t.timer = setInterval( function() { t.zoomImages(); }, t.interval ); } catch(e){ } }, stop: function() { if ( this.timer ) clearInterval( this.timer ); }, getScale: function() { var scale = detectZoom.device(); // Round up to 1.5 or the next integer below the cap. if ( scale <= 1.0 ) scale = 1.0; else if ( scale <= 1.5 ) scale = 1.5; else if ( scale <= 2.0 ) scale = 2.0; else if ( scale <= 3.0 ) scale = 3.0; else if ( scale <= 4.0 ) scale = 4.0; else scale = 5.0; return scale; }, shouldZoom: function( scale ) { var t = this; // Do not operate on hidden frames. if ( "innerWidth" in window && !window.innerWidth ) return false; // Don't do anything until scale > 1 if ( scale == 1.0 && t.zoomed == false ) return false; return true; }, zoomImages: function() { var t = this; var scale = t.getScale(); if ( ! t.shouldZoom( scale ) ){ return; } t.zoomed = true; // Loop through all the elements on the page. var imgs = document.getElementsByTagName("img"); for ( var i = 0; i < imgs.length; i++ ) { // Wait for original images to load if ( "complete" in imgs[i] && ! imgs[i].complete ) continue; // Skip images that have srcset attributes. if ( imgs[i].hasAttribute('srcset') ) { continue; } // Skip images that don't need processing. var imgScale = imgs[i].getAttribute("scale"); if ( imgScale == scale || imgScale == "0" ) continue; // Skip images that have already failed at this scale var scaleFail = imgs[i].getAttribute("scale-fail"); if ( scaleFail && scaleFail <= scale ) continue; // Skip images that have no dimensions yet. if ( ! ( imgs[i].width && imgs[i].height ) ) continue; // Skip images from Lazy Load plugins if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src"))) continue; if ( t.scaleImage( imgs[i], scale ) ) { // Mark the img as having been processed at this scale. imgs[i].setAttribute("scale", scale); } else { // Set the flag to skip this image. imgs[i].setAttribute("scale", "0"); } } }, scaleImage: function( img, scale ) { var t = this; var newSrc = img.src; var isFiles = false; var isLatex = false; var isPhoton = false; // Skip slideshow images if ( img.parentNode.className.match(/slideshow-slide/) ) return false; // Skip CoBlocks Lightbox images if ( img.parentNode.className.match(/coblocks-lightbox__image/) ) return false; // Scale gravatars that have ?s= or ?size= if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) { if ( this.hintsFor( "gravatar" ) === true ) { return false; } newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) { // Stash the original size var originalAtt = "originals", originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $3; img.setAttribute(originalAtt, originalSize); if ( t.imgNeedsSizeAtts( img ) ) { // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; } } // Get the width/height of the image in CSS pixels var size = img.clientWidth; // Convert CSS pixels to device pixels var targetSize = Math.ceil(img.clientWidth * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go larger than the service supports targetSize = Math.min( targetSize, 512 ); return $1 + targetSize; }); } // Scale mshots that have width else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) { if ( this.hintsFor( "mshots" ) === true ) { return false; } newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) { // Stash the original size var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $2; img.setAttribute(originalAtt, originalSize); if ( t.imgNeedsSizeAtts( img ) ) { // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; } } // Get the width of the image in CSS pixels var size = img.clientWidth; // Convert CSS pixels to device pixels var targetSize = Math.ceil(size * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go bigger unless the current one is actually lacking if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth ) targetSize = $2; if ( $2 != targetSize ) return $1 + targetSize; return $0; }); // Update height attribute to match width newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) { if ( newSrc == img.src ) { return $0; } // Stash the original size var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $2; img.setAttribute(originalAtt, originalSize); } // Get the height of the image in CSS pixels var size = img.clientHeight; // Convert CSS pixels to device pixels var targetSize = Math.ceil(size * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go bigger unless the current one is actually lacking if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight ) targetSize = $2; if ( $2 != targetSize ) return $1 + targetSize; return $0; }); } // Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) { if ( this.hintsFor( "imgpress" ) === true ) { return false; } var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"]; // Search the query string for unsupported functions. var qs = RegExp.$3.split('&'); for ( var q in qs ) { q = qs[q].split('=')[0]; if ( imgpressSafeFunctions.indexOf(q) == -1 ) { return false; } } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; // Compute new src if ( scale == 1 ) newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); else newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&'); } // Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com) else if ( ( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) || ( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) || ( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) ) ) { if ( false !== isFiles && this.hintsFor( "files" ) === true ) { return false } if ( false !== isLatex && this.hintsFor( "latex" ) === true ) { return false } if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) { return false } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; // Compute new src if ( scale == 1 ) { newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); } else { newSrc = img.src; var url_var = newSrc.match( /([?&]w=)(\d+)/ ); if ( url_var !== null && url_var[2] ) { newSrc = newSrc.replace( url_var[0], url_var[1] + img.width ); } url_var = newSrc.match( /([?&]h=)(\d+)/ ); if ( url_var !== null && url_var[2] ) { newSrc = newSrc.replace( url_var[0], url_var[1] + img.height ); } var zoom_arg = '&zoom=2'; if ( !newSrc.match( /\?/ ) ) { zoom_arg = '?zoom=2'; } img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' ); } } // Scale static assets that have a name matching *-1x.png or *@1x.png else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) { if ( this.hintsFor( "staticAssets" ) === true ) { return false; } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; var currentSize = RegExp.$1, newSize = currentSize; if ( scale <= 1 ) newSize = 1; else newSize = 2; if ( currentSize != newSize ) newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3'); } else { return false; } // Don't set img.src unless it has changed. This avoids unnecessary reloads. if ( newSrc != img.src ) { // Store the original img.src var prevSrc, origSrc = img.getAttribute("src-orig"); if ( !origSrc ) { origSrc = img.src; img.setAttribute("src-orig", origSrc); } // In case of error, revert img.src prevSrc = img.src; img.onerror = function(){ img.src = prevSrc; if ( img.getAttribute("scale-fail") < scale ) img.setAttribute("scale-fail", scale); img.onerror = null; }; // Finally load the new image img.src = newSrc; } return true; } }; wpcom_img_zoomer.init(); ; /*! This file is auto-generated */ !function(e,t){if("function"==typeof define&&define.amd)define("hoverintent",["module"],t);else if("undefined"!=typeof exports)t(module);else{var n={exports:{}};t(n),e.hoverintent=n.exports}}(this,function(e){"use strict";var t=Object.assign||function(e){for(var t=1;t .ab-item").focus(),y(e,"hover"))}function f(e){var t;13!==e.which||w(e.target,".ab-sub-wrapper")||(t=w(e.target,".menupop"))&&(e.preventDefault(),(o(t,"hover")?y:b)(t,"hover"))}function p(e){var t;13===e.which&&(t=e.target.getAttribute("href"),-1 li').not('.adminbar-handle'), size = Math.floor( ( $(window).height() - 100 ) / 30 ), // approx, based on current styling topHandle, bottomHandle, interval, speed = 100; if ( ! ul.length || li.length < size + 1 || ul.find('li:first').hasClass('.adminbar-handle') ) return; function move( direction ) { var hide, show, next, visible = li.filter(':visible'), first = visible.first(), last = visible.last(); if ( 'up' == direction ) { show = last.next().not('.adminbar-handle'); hide = first; if ( topHandle.hasClass('scrollend') ) { topHandle.removeClass('scrollend'); } if ( show.next().hasClass('adminbar-handle') ) { bottomHandle.addClass('scrollend'); } if ( ! show.length ) { window.clearInterval( interval ); return; } } else if ( 'down' == direction ) { show = first.prev().not('.adminbar-handle'); hide = last; if ( bottomHandle.hasClass('scrollend') ) { bottomHandle.removeClass('scrollend'); } if ( show.prev().hasClass('adminbar-handle') ) { topHandle.addClass('scrollend'); } if ( ! show.length ) { window.clearInterval( interval ); return; } } else { return; } if ( hide.length && show.length ) { // Maybe add some sliding animation? hide.hide() show.show() } } // hide the extra items li.slice(size).hide(); topHandle = $('
  • '); bottomHandle = topHandle.clone().addClass('handle-bottom'); ul.prepend( topHandle.addClass('handle-top scrollend') ).append( bottomHandle ); topHandle.on( 'mouseenter', function() { interval = window.setInterval( function() { move('down'); }, speed ); }).on( 'click', function() { move('down'); }); bottomHandle.on( 'mouseenter', function() { interval = window.setInterval( function() { move('up'); }, speed ); }).on( 'click', function() { move('up'); }); topHandle.add( bottomHandle ).on( 'mouseleave click', function() { window.clearInterval( interval ); }); }); ; /** * Comment Likes - JavaScript * * This handles liking and unliking comments, as well as viewing who has * liked a particular comment. * * @dependency Swipe (dynamically loaded when needed) * * @package Comment_Likes * @subpackage JavaScript */ (function () { function init() { let extWin; let extWinCheck; let commentLikeEvent; // Only run once. if (window.comment_likes_loaded) { return; } window.comment_likes_loaded = true; // Client-side cache of who liked a particular comment to avoid // having to hit the server multiple times for the same data. const commentLikeCache = {}; let swipeLibPromise; // Load the Swipe library, if it's not already loaded. function swipeLibLoader() { if (!swipeLibPromise) { swipeLibPromise = new Promise((resolve, reject) => { if (window.Swipe) { resolve(window.Swipe); } else { const swipeScript = document.createElement('script'); swipeScript.src = comment_like_text.swipeUrl; swipeScript.async = true; document.body.appendChild(swipeScript); swipeScript.addEventListener('load', () => resolve(window.Swipe)); swipeScript.addEventListener('error', error => reject(error)); } }); } return swipeLibPromise; } /** * Parse the comment ID from a comment like link. */ function getCommentId(link) { const commentId = link && link.getAttribute('href') && link.getAttribute('href').split('like_comment='); return commentId[1].split('&_wpnonce=')[0]; } /** * Handle an ajax action on the comment like link. */ function handleLinkAction(link, action, commentId, callback) { const nonce = link && link.getAttribute('href') && link.getAttribute('href').split('_wpnonce=')[1]; fetch('/wp-admin/admin-ajax.php', { method: 'POST', body: new URLSearchParams({ action: action, _wpnonce: nonce, like_comment: commentId, blog_id: Number(link.dataset.blog), }), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'cache-control': 'no-cache', pragma: 'no-cache', }, }) .then(response => response.json()) .then(callback); } function startPolling() { // Append cookie polling login iframe to this window to wait for user to finish logging in (or cancel) const loginIframe = document.createElement('iframe'); loginIframe.id = 'wp-login-polling-iframe'; loginIframe.src = 'https://wordpress.com/public.api/connect/?iframe=true'; document.body.appendChild(loginIframe); loginIframe.style.display = 'none'; } function stopPolling() { const iframe = document.querySelector('#wp-login-polling-iframe'); if (iframe) { iframe.remove(); } } function hide(el) { if (el && el.style) { el.style.display = 'none'; } } function show(el) { if (el && el.style) { el.style.removeProperty('display'); } } // Overlay used for displaying comment like info. class Overlay { constructor() { // Overlay element. this.el = document.createElement('div'); this.el.classList.add('comment-likes-overlay'); document.body.appendChild(this.el); hide(this.el); this.el.addEventListener('mouseenter', () => { // Don't hide the overlay if the user is mousing over it. overlay.cancelHide(); }); this.el.addEventListener('mouseleave', () => overlay.requestHide()); // Inner contents of overlay. this.innerEl = null; // Instance of the Swipe library. this.swipe = null; // Timeout used for hiding the overlay. this.hideTimeout = null; } // Initialise the overlay for use, removing any old content. clear() { // Unload any previous instance of Swipe (to avoid leaking a global // event handler). This is done before clearing the contents of the // overlay because Swipe expects the slides to still be present. if (this.swipe) { this.swipe.kill(); this.swipe = null; } this.el.innerHTML = ''; this.innerEl = document.createElement('div'); this.innerEl.classList.add('inner'); this.el.appendChild(this.innerEl); } /** * Construct a list (
      ) of user (gravatar, name) details. * * @param data liker data returned from the server * @param klass CSS class to apply to the
        element * @param start index of user to start at * @param length number of users to include in the list * * @return A container element with the list */ getUserBits(data, klass, start, length) { start = start || 0; let last = start + (length || data.length); last = last > data.length ? data.length : last; const container = document.createElement('div'); container.classList.add('liker-list'); let html = `'; container.innerHTML = html; return container; } /** * Render the display of who has liked this comment. The type of * display depends on how many people have liked the comment. * If more than 10 people have liked the comment, this function * renders navigation controls and sets up the Swipe library for * changing between pages. * * @param link the element over which the user is hovering * @param data the results retrieved from the server */ showLikes(link, data) { this.clear(); link.dataset.likeCount = data.length; if (data.length === 0) { // No likers after all. hide(this.el); return; } this.innerEl.style.padding = '12px'; if (data.length < 6) { // Only one column needed. this.innerEl.style.maxWidth = '200px'; this.innerEl.innerHTML = ''; this.innerEl.appendChild(this.getUserBits(data, 'single')); this.setPosition(link); } else if (data.length < 11) { // Two columns, but only one page. this.innerEl.innerHTML = ''; this.innerEl.appendChild(this.getUserBits(data, 'double')); this.setPosition(link); } else { // Multiple pages. this.renderLikesWithPagination(data, link); } } /** * Render multiple pages of likes with pagination controls. * This function is intended to be called by `showLikes` above. * * @param data the results retrieved from the server */ renderLikesWithPagination(data, link) { swipeLibLoader().then(() => { const page_count = Math.ceil(data.length / 10); // Swipe requires two nested containers. const swipe = document.createElement('div'); swipe.classList.add('swipe'); this.innerEl.appendChild(swipe); const wrap = document.createElement('div'); wrap.classList.add('swipe-wrap'); swipe.appendChild(wrap); for (let i = 0; i < page_count; ++i) { wrap.appendChild(this.getUserBits(data, 'double', i * 10, 10)); } /** * Navigation controls. * This is based on the Newdash controls found in * reader/recommendations-templates.php */ const nav = document.createElement('nav'); nav.classList.add('slider-nav'); let navContents = ` `; for (let i = 0; i < page_count; ++i) { navContents += ``; } navContents += ` `; this.innerEl.appendChild(nav); nav.innerHTML = navContents; /** Set up Swipe. **/ // Swipe cannot be set up successfully unless its container // is visible, so we show it now. show(this.el); this.setPosition(link); this.swipe = new Swipe(swipe, { callback: function (pos) { // Update the pagination indicators. // // If there are exactly two pages, Swipe has a weird // special case where it duplicates both pages and // can return index 2 and 3 even though those aren't // real pages (see swipe.js, line 47). To deal with // this, we use the expression `pos % page_count`. pos = pos % page_count; nav.querySelectorAll('em').forEach(em => { const page = Number(em.dataset.page); em.setAttribute('class', pos === page ? 'on' : ''); }); }, }); nav.querySelectorAll('em').forEach(em => { em.addEventListener('click', e => { // Go to the page corresponding to the indicator clicked. this.swipe.slide(Number(em.dataset.page)); e.preventDefault(); }); }); // Previous and next buttons. nav.querySelector('.prev').addEventListener('click', e => { this.swipe.prev(); e.preventDefault(); }); nav.querySelector('.next').addEventListener('click', e => { this.swipe.next(); e.preventDefault(); }); }); } /** * Open the overlay and show a loading message. */ showLoadingMessage(link) { this.clear(); this.innerEl.textContent = comment_like_text.loading; this.setPosition(link); } /** * Position the overlay near the current comment. * * @param link element near which to position the overlay */ setPosition(link) { // Prepare a down arrow icon for the bottom of the overlay. const icon = document.createElement('span'); this.el.appendChild(icon); icon.classList.add('icon', 'noticon', 'noticon-downarrow'); icon.style.textShadow = '0px 1px 1px rgb(223, 223, 223)'; const rect = link.getBoundingClientRect(); const win = document.defaultView; const offset = { top: rect.top + win.scrollY, left: rect.left + win.scrollX, }; // Take measurements with the element fully visible. show(this.el); let left = offset.left - (this.el.offsetWidth - link.offsetWidth) / 2; left = left < 5 ? 5 : left; let top = offset.top - this.el.offsetHeight + 5; hide(this.el); const adminBar = document.querySelector('#wpadminbar'); // Check if the overlay would appear off the screen. if (top < win.scrollY + ((adminBar && adminBar.offsetHeight) || 0)) { // We'll display the overlay beneath the link instead. top = offset.top + link.offsetHeight; // Instead of using the down arrow icon, use an up arrow. icon.remove(); this.el.prepend(icon); icon.classList.remove('noticon-downarrow'); icon.classList.add('noticon-uparrow'); icon.style.textShadow = '0px -1px 1px rgb(223, 223, 223)'; icon.style.verticalAlign = 'bottom'; } this.el.style.left = `${left}px`; this.el.style.top = `${top}px`; show(this.el); // The height of the arrow icon differs slightly between browsers, // so we compute the margin here to make sure it isn't disjointed // from the overlay. icon.style.marginTop = `${icon.scrollHeight - 26}px`; icon.style.marginBottom = `${20 - icon.scrollHeight}px`; // Position the arrow to be horizontally centred on the link. icon.style.paddingLeft = `${ offset.left - left + (link.offsetWidth - icon.scrollWidth) / 2 }px`; } /** * Return whether the overlay is visible. */ isVisible() { return this.el.style.getPropertyValue('display') !== 'none'; } /** * Request that the overlay be hidden after a short delay. */ requestHide() { if (this.hideTimeout !== null) { return; } this.hideTimeout = setTimeout(() => { hide(this.el); this.clear(); }, 300); } /** * Cancel a request to hide the overlay. */ cancelHide() { if (this.hideTimeout !== null) { clearTimeout(this.hideTimeout); this.hideTimeout = null; } } } // Overlay used for displaying comment like info. const overlay = new Overlay(); // The most recent comment for which the user has requested to see // who liked it. var relevantComment; // Precache after this timeout. var precacheTimeout = null; /** * Fetch the like data for a particular comment. */ function fetchLikeData(link, commentId) { commentLikeCache[commentId] = null; const container = link && link.parentElement && link.parentElement.parentElement; const star = container.querySelector('a.comment-like-link'); star && handleLinkAction(star, 'view_comment_likes', commentId, data => { // Populate the cache. commentLikeCache[commentId] = data; // Only show the overlay if the user is interested. if (overlay.isVisible() && relevantComment === commentId) { overlay.showLikes(link, data); } }); } function readCookie(c) { const nameEQ = c + '='; const cookieStrings = document.cookie.split(';'); for (let i = 0; i < cookieStrings.length; i++) { let cookieString = cookieStrings[i]; while (cookieString.charAt(0) === ' ') { cookieString = cookieString.substring(1, cookieString.length); } if (cookieString.indexOf(nameEQ) === 0) { const chunk = cookieString.substring(nameEQ.length, cookieString.length); const pairs = chunk.split('&'); const cookieData = {}; for (let num = pairs.length - 1; num >= 0; num--) { const pair = pairs[num].split('='); cookieData[pair[0]] = decodeURIComponent(pair[1]); } return cookieData; } } return null; } function getServiceData() { const data = readCookie('wpc_wpc'); if (data === null || typeof data.access_token === 'undefined' || !data.access_token) { return false; } return data; } function readMessage(msg) { const event = msg.data; if (typeof event.event === 'undefined') { return; } if (event.event === 'login' && event.success) { extWinCheck = setInterval(function () { if (!extWin || extWin.closed) { clearInterval(extWinCheck); if (getServiceData()) { // Load page in an iframe to get the current comment nonce const nonceIframe = document.createElement('iframe'); nonceIframe.id = 'wp-login-comment-nonce-iframe'; nonceIframe.style.display = 'none'; nonceIframe.src = commentLikeEvent + ''; document.body.appendChild(nonceIframe); const commentLikeId = (commentLikeEvent + '') .split('like_comment=')[1] .split('&_wpnonce=')[0]; let c; // Set a 5 second timeout to redirect to the comment page without doing the Like as a fallback const commentLikeTimeout = setTimeout(() => { window.location = commentLikeEvent; }, 5000); // Check for a new nonced redirect and use that if available before timing out const commentLikeCheck = setInterval(() => { const iframe = document.querySelector('#wp-login-comment-nonce-iframe'); if (iframe) { c = iframe.querySelector(`#comment-like-${commentLikeId} .comment-like-link`); } if (c && typeof c.href !== 'undefined') { clearTimeout(commentLikeTimeout); clearInterval(commentLikeCheck); window.location = c.href; } }, 100); } } }, 100); if (extWin) { if (!extWin.closed) { extWin.close(); } extWin = false; } stopPolling(); } } if (typeof window.postMessage !== 'undefined') { window.addEventListener('message', e => { let message = e && e.data; if (typeof message === 'string') { try { message = JSON.parse(message); } catch (err) { return; } } const type = message && message.type; if (type === 'loginMessage') { readMessage(message); } }); } document.body.addEventListener('click', e => { let target = e.target; // Don't do anything when clicking on the "X people" link. if (target.matches('p.comment-likes a.view-likers')) { e.preventDefault(); return; } // Retrieve the surrounding paragraph to the star, if it hasn't been liked. const notLikedPar = target.closest('p.comment-not-liked'); // Return if not clicking on star or surrounding paragraph. if (!target.matches('a.comment-like-link') && !notLikedPar) { return; } // When a comment hasn't been liked, make the text clickable, too. if (notLikedPar) { target = notLikedPar.querySelector('a.comment-like-link'); if (!target) { return; } } if (target.classList.contains('needs-login')) { e.preventDefault(); commentLikeEvent = target; if (extWin) { if (!extWin.closed) { extWin.close(); } extWin = false; } stopPolling(); const url = 'https://wordpress.com/public.api/connect/?action=request&service=wordpress'; extWin = window.open( url, 'likeconn', 'status=0,toolbar=0,location=1,menubar=0,directories=0,resizable=1,scrollbars=1,height=560,width=500' ); startPolling(); return false; } // Record that the user likes or does not like this comment. const commentId = getCommentId(target); target.classList.add('loading'); let commentEl = document.querySelector(`p#comment-like-${commentId}`); // Determine whether to like or unlike based on whether the comment is // currently liked. const action = commentEl && commentEl.dataset.liked === 'comment-liked' ? 'unlike_comment' : 'like_comment'; handleLinkAction(target, action, commentId, data => { // Invalidate the like cache for this comment. delete commentLikeCache[commentId]; const countEl = document.querySelector(`#comment-like-count-${data.context}`); if (countEl) { countEl.innerHTML = data.display; } commentEl = document.querySelector(`p#comment-like-${data.context}`); if (action === 'like_comment') { commentEl.classList.remove('comment-not-liked'); commentEl.classList.add('comment-liked'); commentEl.dataset.liked = 'comment-liked'; } else { commentEl.classList.remove('comment-liked'); commentEl.classList.add('comment-not-liked'); commentEl.dataset.liked = 'comment-not-liked'; } // Prefetch new data for this comment (if there are likers left). const parent = target.closest('.comment-likes'); const link = parent && parent.querySelector('a.view-likers'); if (link) { fetchLikeData(link, commentId); } target.classList.remove('loading'); }); e.preventDefault(); e.stopPropagation(); }); document.body.addEventListener( 'mouseenter', function (e) { if (!e.target.matches('p.comment-likes a.view-likers')) { return; } // Show the user a list of who has liked this comment. const link = e.target; if (Number(link.dataset.likeCount || 0) === 0) { // No one has liked this comment. return; } // Don't hide the overlay. overlay.cancelHide(); // Get the comment ID. const container = link.parentElement && link.parentElement.parentElement; const star = container && container.querySelector('a.comment-like-link'); const commentId = star && getCommentId(star); relevantComment = commentId; // Check if the list of likes for this comment is already in // the cache. if (commentId in commentLikeCache) { const entry = commentLikeCache[commentId]; // Only display the likes if the ajax request is // actually done. if (entry !== null) { overlay.showLikes(link, entry); } else { // Make sure the overlay is visible (in case // the user moved the mouse away while loading // but then came back before it finished // loading). overlay.showLoadingMessage(link); } return; } // Position the "Loading..." overlay. overlay.showLoadingMessage(link); // Fetch the data. fetchLikeData(link, commentId); }, true ); document.body.addEventListener( 'mouseleave', e => { if (!e.target.matches('p.comment-likes a.view-likers')) { return; } // User has moved cursor away - hide the overlay. overlay.requestHide(); }, true ); document.body.addEventListener( 'mouseenter', e => { if (!e.target.matches('.comment') || !e.target.querySelector('a.comment-like-link')) { return; } // User is moving over a comment - precache the comment like data. if (precacheTimeout !== null) { clearTimeout(precacheTimeout); precacheTimeout = null; } const star = e.target.querySelector('a.comment-like-link'); const parent = star.closest('.comment-likes'); const link = parent && parent.querySelector('a.view-likers'); if (!link || Number(link.dataset.likeCount || 0) === 0) { // No likes. return; } const commentId = getCommentId(star); if (commentId in commentLikeCache) { // Already in cache. return; } precacheTimeout = setTimeout(() => { precacheTimeout = null; if (commentId in commentLikeCache) { // Was cached in the interim. return; } fetchLikeData(link, commentId); }, 1000); }, true ); } if (document.readyState !== 'loading') { init(); } else { document.addEventListener('DOMContentLoaded', init); } })(); ; /** * File navigation.js. * * Handles toggling the navigation menu for small screens and enables TAB key * navigation support for dropdown menus. */ ( function() { var container, button, menu, links, subMenus, i, len; container = document.getElementById( 'site-navigation' ); if ( ! container ) { return; } button = container.getElementsByTagName( 'button' )[0]; if ( 'undefined' === typeof button ) { return; } menu = container.getElementsByTagName( 'ul' )[0]; // Hide menu toggle button if menu is empty and return early. if ( 'undefined' === typeof menu ) { button.style.display = 'none'; return; } menu.setAttribute( 'aria-expanded', 'false' ); if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { menu.className += ' nav-menu'; } button.onclick = function() { if ( -1 !== container.className.indexOf( 'toggled' ) ) { container.className = container.className.replace( ' toggled', '' ); button.setAttribute( 'aria-expanded', 'false' ); menu.setAttribute( 'aria-expanded', 'false' ); } else { container.className += ' toggled'; button.setAttribute( 'aria-expanded', 'true' ); menu.setAttribute( 'aria-expanded', 'true' ); } }; // Get all the link elements within the menu. links = menu.getElementsByTagName( 'a' ); subMenus = menu.getElementsByTagName( 'ul' ); // Set menu items with submenus to aria-haspopup="true". for ( i = 0, len = subMenus.length; i < len; i++ ) { subMenus[i].parentNode.setAttribute( 'aria-haspopup', 'true' ); } // Each time a menu link is focused or blurred, toggle focus. for ( i = 0, len = links.length; i < len; i++ ) { links[i].addEventListener( 'focus', toggleFocus, true ); links[i].addEventListener( 'blur', toggleFocus, true ); } /** * Sets or removes .focus class on an element. */ function toggleFocus() { var self = this; // Move up through the ancestors of the current link until we hit .nav-menu. while ( -1 === self.className.indexOf( 'nav-menu' ) ) { // On li elements toggle the class .focus. if ( 'li' === self.tagName.toLowerCase() ) { if ( -1 !== self.className.indexOf( 'focus' ) ) { self.className = self.className.replace( ' focus', '' ); } else { self.className += ' focus'; } } self = self.parentElement; } } // Fix child menus for touch devices. function fixMenuTouchTaps( container ) { var touchStartFn, parentLink = container.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' ); if ( 'ontouchstart' in window ) { touchStartFn = function( e ) { var menuItem = this.parentNode; if ( ! menuItem.classList.contains( 'focus' ) ) { e.preventDefault(); for( var i = 0; i < menuItem.parentNode.children.length; ++i ) { if ( menuItem === menuItem.parentNode.children[i] ) { continue; } menuItem.parentNode.children[i].classList.remove( 'focus' ); } menuItem.classList.add( 'focus' ); } else { menuItem.classList.remove( 'focus' ); } }; for ( var i = 0; i < parentLink.length; ++i ) { parentLink[i].addEventListener( 'touchstart', touchStartFn, false ) } } } fixMenuTouchTaps( container ); } )(); ; (function ($) { var $header = $('.header-top:first'), $headerHeight = $('.header-top').innerHeight(), $headerOffset = $('.custom-header').innerHeight(), $headerHiddenClass = 'site-header-hidden', $headerFixedClass = 'site-header-fixed', $navigation = $('.main-navigation'), $navMenuItem = $navigation.find('.menu-item'), navigationHeight, navigationOuterHeight, navPadding, navMenuItemHeight, idealNavHeight, navIsNotTooTall; // adjust header margin based on height of menu function adjustHeaderMargin() { // check to see if on mobile by checking menu-toggle display if ('none' === $('.menu-toggle').css('display')) { // Don't define this 'til we're using it, it may change $headerHeight = $('.header-top').innerHeight(); // if yes, we want to bump the custom header down a bit, so the menu doesn't cut it off $('.custom-header-image').css('margin-top', $headerHeight); } } // Make sure the nav isn't taller than two rows navigationHeight = $navigation.height(); navMenuItemHeight = $navMenuItem.outerHeight() * 3; idealNavHeight = navMenuItemHeight; navIsNotTooTall = navigationHeight <= idealNavHeight; //we add the scroll class to the navs function adjustScrollClass() { // Make sure we're not on a mobile screen if ('none' === $('.menu-toggle').css('display')) { if ( $(window).scrollTop() <= $headerOffset && $header.hasClass($headerFixedClass) ) { // accounts for empty or removed custom header image if (0 !== $headerOffset) { $header.removeClass($headerFixedClass); $header.addClass($headerHiddenClass); } } else if ($(window).scrollTop() >= $headerOffset) { //If the scroll is more than the custom header // Make sure the menu is not too tall if (navIsNotTooTall || navMenuItemHeight == 0) { // Don't define this 'til we're using it, it may change $headerHeight = $('.header-top').innerHeight(true); // If not, fix that header! $header.addClass($headerFixedClass); $header.removeClass($headerHiddenClass); $('.custom-header').css('margin-top', $headerHeight); } } else { //If not we remove it $header.removeClass($headerFixedClass); $header.removeClass($headerHiddenClass); $('.custom-header').css('margin-top', 'auto'); } } } // Check to see if iOS device // iOS devices make a mess of background-attachment: fixed and background-size: cover function checkiOS() { return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; } /* * Test if background-attachment: fixed is supported. * @link http://stackoverflow.com/questions/14115080/detect-support-for-background-attachment-fixed */ function supportsFixedBackground() { var el = document.createElement('div'), isSupported; try { if (!('backgroundAttachment' in el.style) || checkiOS()) { return false; } el.style.backgroundAttachment = 'fixed'; isSupported = 'fixed' === el.style.backgroundAttachment; return isSupported; } catch (e) { return false; } } // Let's fire some JavaScript! $(document).ready(function () { // On load, we want to adjust the header margin adjustHeaderMargin(); adjustScrollClass(); // We also want to check the device, and add a class if not iOS: if (false === supportsFixedBackground()) { document.documentElement.className += ' no-background-fixed'; } }); // On scroll, we want to stick/unstick the header $(window).on('scroll', function () { adjustScrollClass(); }); // we also want to do the same on window rezize $(window).on('resize', function () { setTimeout(adjustHeaderMargin, 500); }); // Use mutant observer to check when .wf-active is added to theme for Custom Fonts. // That way we can make sure the header is the correct height when it happens. var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; $.fn.attrchange = function (callback) { if (MutationObserver) { var options = { subtree: false, attributes: true, }; var observer = new MutationObserver(function (mutations) { mutations.forEach(function (e) { callback.call(e.target, e.attributeName); }); }); return this.each(function () { observer.observe(this, options); }); } }; $('html').attrchange(function (attrName) { if (attrName == 'class' && $('html').hasClass('wf-active')) { adjustHeaderMargin(); } }); })(jQuery); ; /** * File skip-link-focus-fix.js. * * Helps with accessibility for keyboard only users. * * Learn more: https://git.io/vWdr2 */ ( function() { var isWebkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1, isOpera = navigator.userAgent.toLowerCase().indexOf( 'opera' ) > -1, isIe = navigator.userAgent.toLowerCase().indexOf( 'msie' ) > -1; if ( ( isWebkit || isOpera || isIe ) && document.getElementById && window.addEventListener ) { window.addEventListener( 'hashchange', function() { var id = location.hash.substring( 1 ), element; if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) { return; } element = document.getElementById( id ); if ( element ) { if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) { element.tabIndex = -1; } element.focus(); } }, false ); } })(); ; ( function () { 'use strict'; // Ensure we only load the dependencies once. let loadPromise; // Set up a timeout as a promise. function timeout( duration ) { return new Promise( ( resolve ) => { window.setTimeout( () => resolve(), duration ); } ); } // Load a single stylesheet as a promise. function loadCSS( url ) { const loadPromise = new Promise( ( resolve, reject ) => { const link = document.createElement( 'link' ); link.rel = 'stylesheet'; link.media = 'all'; link.onload = () => resolve(); link.onerror = ( e ) => reject( e ); link.href = url; document.head.appendChild( link ); } ); // Some browsers don't emit a `load` event for dynamically loaded // stylesheets, so we add a 2s timeout as a best-effort workaround. return Promise.race( [ loadPromise, timeout( 2000 ) ] ); } // Load a single script as a promise. function loadJS( url ) { return new Promise( ( resolve, reject ) => { const script = document.createElement( 'script' ); script.onload = () => resolve(); script.onerror = ( e ) => reject( e ); script.src = url; document.body.appendChild( script ); } ); } // Ensure all the necessary scripts are loaded. function loadDependencies() { if ( ! loadPromise ) { // Disable jQuery usage logging, since we'll be lazily pulling in jQuery. window.wpcomJQueryUsageLoggerDisabled = true; const config = window.wpcom_tos_report_form; const css = [ loadCSS( config.thickbox_css ), loadCSS( config.tos_report_form_css ) ]; // `jQuery` and `spin` don't have any dependencies. const jQuery = window.jQuery ? Promise.resolve() : loadJS( config.jquery_js ); const spin = window.Spinner ? Promise.resolve() : loadJS( config.spin_js ); // `thickbox` depends on `jQuery`. const thickbox = jQuery.then( () => window.tb_show ? Promise.resolve() : loadJS( config.thickbox_js ) ); // `jQuery.spin` depends on `jQuery` and `spin`. const jQuerySpin = Promise.all( [ jQuery, spin ] ).then( () => window.jQuery.fn.spin ? Promise.resolve() : loadJS( config.jquery_spin_js ) ); // The TOS report form JS depends on `jQuery`, `thickbox`, and `jQuery.spin`. const tosReportFormJS = Promise.all( [ jQuery, thickbox, jQuerySpin ] ).then( () => loadJS( config.tos_report_form_js ) ); loadPromise = Promise.all( [ ...css, tosReportFormJS ] ); } return loadPromise; } function init() { document.body.addEventListener( 'click', ( e ) => { if ( ! e.target || ! e.target.closest ) { return; } if ( e.target.closest( '#wp-admin-bar-wpcom_report_url' ) || e.target.closest( '.flb-report a' ) ) { if ( ! window.wpcom_tos_report_form ) { return; } // When clicking on a report link, instead of following the link, // load the TOS report form script and dependencies and open the dialog. e.preventDefault(); loadDependencies() .then( () => setTimeout( () => window.jQuery( document ).trigger( 'open_tos_report_form' ) ) ) // If something goes wrong, redirect to standalone page. .catch( () => ( window.location = e.target.href || 'http://en.wordpress.com/abuse/' ) ); } } ); } if ( document.readyState !== 'loading' ) { init(); } else { document.addEventListener( 'DOMContentLoaded', init ); } } )(); ; /* globals JSON */ ( function () { var eventName = 'wpcom_masterbar_click'; var linksTracksEvents = { // top level items 'wp-admin-bar-blog' : 'my_sites', 'wp-admin-bar-newdash' : 'reader', 'wp-admin-bar-ab-new-post' : 'write_button', 'wp-admin-bar-my-account' : 'my_account', 'wp-admin-bar-notes' : 'notifications', // my sites - top items 'wp-admin-bar-switch-site' : 'my_sites_switch_site', 'wp-admin-bar-blog-info' : 'my_sites_site_info', 'wp-admin-bar-site-view' : 'my_sites_view_site', 'wp-admin-bar-blog-stats' : 'my_sites_site_stats', 'wp-admin-bar-plan' : 'my_sites_plan', 'wp-admin-bar-plan-badge' : 'my_sites_plan_badge', // my sites - manage 'wp-admin-bar-edit-page' : 'my_sites_manage_site_pages', 'wp-admin-bar-new-page-badge' : 'my_sites_manage_add_page', 'wp-admin-bar-edit-post' : 'my_sites_manage_blog_posts', 'wp-admin-bar-new-post-badge' : 'my_sites_manage_add_post', 'wp-admin-bar-edit-attachment' : 'my_sites_manage_media', 'wp-admin-bar-new-attachment-badge' : 'my_sites_manage_add_media', 'wp-admin-bar-comments' : 'my_sites_manage_comments', 'wp-admin-bar-edit-jetpack-testimonial' : 'my_sites_manage_testimonials', 'wp-admin-bar-new-jetpack-testimonial' : 'my_sites_manage_add_testimonial', 'wp-admin-bar-edit-jetpack-portfolio' : 'my_sites_manage_portfolio', 'wp-admin-bar-new-jetpack-portfolio' : 'my_sites_manage_add_portfolio', // my sites - personalize 'wp-admin-bar-themes' : 'my_sites_personalize_themes', 'wp-admin-bar-cmz' : 'my_sites_personalize_themes_customize', // my sites - configure 'wp-admin-bar-sharing' : 'my_sites_configure_sharing', 'wp-admin-bar-people' : 'my_sites_configure_people', 'wp-admin-bar-people-add' : 'my_sites_configure_people_add_button', 'wp-admin-bar-plugins' : 'my_sites_configure_plugins', 'wp-admin-bar-domains' : 'my_sites_configure_domains', 'wp-admin-bar-domains-add' : 'my_sites_configure_add_domain', 'wp-admin-bar-blog-settings' : 'my_sites_configure_settings', 'wp-admin-bar-legacy-dashboard' : 'my_sites_configure_wp_admin', // reader 'wp-admin-bar-followed-sites' : 'reader_followed_sites', 'wp-admin-bar-reader-followed-sites-manage': 'reader_manage_followed_sites', 'wp-admin-bar-discover-discover' : 'reader_discover', 'wp-admin-bar-discover-search' : 'reader_search', 'wp-admin-bar-my-activity-my-likes' : 'reader_my_likes', // account 'wp-admin-bar-user-info' : 'my_account_user_name', // account - profile 'wp-admin-bar-my-profile' : 'my_account_profile_my_profile', 'wp-admin-bar-account-settings' : 'my_account_profile_account_settings', 'wp-admin-bar-billing' : 'my_account_profile_manage_purchases', 'wp-admin-bar-security' : 'my_account_profile_security', 'wp-admin-bar-notifications' : 'my_account_profile_notifications', // account - special 'wp-admin-bar-get-apps' : 'my_account_special_get_apps', 'wp-admin-bar-next-steps' : 'my_account_special_next_steps', 'wp-admin-bar-help' : 'my_account_special_help', }; var notesTracksEvents = { openSite: function ( data ) { return { clicked: 'masterbar_notifications_panel_site', site_id: data.siteId }; }, openPost: function ( data ) { return { clicked: 'masterbar_notifications_panel_post', site_id: data.siteId, post_id: data.postId }; }, openComment: function ( data ) { return { clicked: 'masterbar_notifications_panel_comment', site_id: data.siteId, post_id: data.postId, comment_id: data.commentId }; } }; // Element.prototype.matches as a standalone function, with old browser fallback function matches( node, selector ) { if ( ! node ) { return undefined; } if ( ! Element.prototype.matches && ! Element.prototype.msMatchesSelector ) { throw new Error( 'Unsupported browser' ); } return Element.prototype.matches ? node.matches( selector ) : node.msMatchesSelector( selector ); } // Element.prototype.closest as a standalone function, with old browser fallback function closest( node, selector ) { if ( ! node ) { return undefined; } if ( Element.prototype.closest ) { return node.closest( selector ); } do { if ( matches( node, selector ) ) { return node; } node = node.parentElement || node.parentNode; } while ( node !== null && node.nodeType === 1 ); return null; } function recordTracksEvent( eventProps ) { eventProps = eventProps || {}; window._tkq = window._tkq || []; window._tkq.push( [ 'recordEvent', eventName, eventProps ] ); } function parseJson( s, defaultValue ) { try { return JSON.parse( s ); } catch ( e ) { return defaultValue; } } function createTrackableLinkEventHandler( link ) { return function () { var parent = closest( link, 'li' ); if ( ! parent ) { return; } var trackingId = link.getAttribute( 'ID' ) || parent.getAttribute( 'ID' ); if ( ! linksTracksEvents.hasOwnProperty( trackingId ) ) { return; } var eventProps = { 'clicked': linksTracksEvents[ trackingId ] }; recordTracksEvent( eventProps ); } } function init() { var trackableLinkSelector = '.mb-trackable .ab-item:not(div),' + '#wp-admin-bar-notes .ab-item,' + '#wp-admin-bar-user-info .ab-item,' + '.mb-trackable .ab-secondary'; var trackableLinks = document.querySelectorAll( trackableLinkSelector ); for ( var i = 0; i < trackableLinks.length; i++ ) { var link = trackableLinks[ i ]; var handler = createTrackableLinkEventHandler( link ); link.addEventListener( 'click', handler ); link.addEventListener( 'touchstart', handler ); } } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', init ); } else { init(); } // listen for postMessage events from the notifications iframe window.addEventListener( 'message', function ( event ) { if ( event.origin !== 'https://widgets.wp.com' ) { return; } var data = ( typeof event.data === 'string' ) ? parseJson( event.data, {} ) : event.data; if ( data.type !== 'notesIframeMessage' ) { return; } var eventData = notesTracksEvents[ data.action ]; if ( ! eventData ) { return; } recordTracksEvent( eventData( data ) ); }, false ); } )(); ; (function () { var el = document.querySelector(".launch-banner"); if (!el) { return; } // Minimize the banner contents jumping around by only un-hiding once the page is loaded document.addEventListener("DOMContentLoaded", function () { var el = document.querySelector(".launch-banner"); if (el) { el.style.display = null; } }); // Dismiss the banner. var dismissButton = document.querySelector( ".launch-banner .dismiss-button button" ); dismissButton.addEventListener("click", function () { el.style.display = "none"; }); function initTracksEvents() { var myHomeButton = document.querySelector( ".launch-banner .my-home-button a" ); registerTracksEvent(myHomeButton, "wpcom_launchbar_button_click", "my-home-button"); var editItemButton = document.querySelector( ".launch-banner .edit-item-button" ); registerTracksEvent(editItemButton, "wpcom_launchbar_button_click", "edit-item-button"); var changeThemeButton = document.querySelector( ".launch-banner .change-theme-button" ); registerTracksEvent(changeThemeButton, "wpcom_launchbar_button_click", "change-theme-button"); var launchSiteButton = document.querySelector( ".launch-banner .launch-site-button" ); registerTracksEvent(launchSiteButton, "wpcom_launchbar_button_click", "launch-site-button"); var dismissButton = document.querySelector( ".launch-banner .dismiss-button" ); registerTracksEvent(dismissButton, "wpcom_launchbar_button_click", "dismiss-button"); } function registerTracksEvent( element, eventName, buttonId ) { window._tkq = window._tkq || []; element?.addEventListener( 'click', function () { window._tkq.push( [ 'recordEvent', eventName, { button: buttonId, blog_id: launchBarUserData.blogId }, ] ); } ); } initTracksEvents(); })(); ;