{"version":3,"sources":["webpack:///./node_modules/lodash/_root.js","webpack:///./node_modules/lodash/isObject.js","webpack:///(webpack)/buildin/global.js","webpack:///./node_modules/lodash/isSymbol.js","webpack:///./node_modules/lodash/_baseGetTag.js","webpack:///./node_modules/lodash/isObjectLike.js","webpack:///./app/javascript/utilities/locale.js","webpack:///./node_modules/lodash/_Symbol.js","webpack:///(webpack)/buildin/module.js","webpack:///./node_modules/lodash/_freeGlobal.js","webpack:///./app/javascript/utilities/waitOnBaseData.js","webpack:///./node_modules/lodash/toNumber.js","webpack:///./app/javascript/topNavigation/utilities.js","webpack:///./node_modules/lodash/_baseTrim.js","webpack:///./node_modules/lodash/_trimmedEndIndex.js","webpack:///./node_modules/lodash/_getRawTag.js","webpack:///./node_modules/lodash/_objectToString.js","webpack:///./app/javascript/packs/followButtons.js"],"names":["freeGlobal","freeSelf","self","Object","root","Function","module","exports","value","type","g","this","e","window","baseGetTag","isObjectLike","Symbol","getRawTag","objectToString","symToStringTag","toStringTag","undefined","i18n","I18n","translationsDiv","document","getElementById","translations","JSON","parse","dataset","store","defaultLocale","userLocale","body","locale","term","params","arguments","length","t","webpackPolyfill","deprecate","paths","children","defineProperty","enumerable","get","l","i","global","waitOnBaseData","Promise","resolve","waitingForDataLoad","setInterval","getAttribute","clearInterval","baseTrim","isObject","isSymbol","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","parseInt","other","valueOf","isBinary","test","slice","closeHeaderMenu","memberMenu","menuNavButton","setAttribute","classList","remove","clicked","firstItem","initializeMemberMenu","memberTopMenu","navigator","userAgent","add","addEventListener","_event","contains","focus","focusFirstItem","activeElement","requestAnimationFrame","openHeaderMenu","isTouchDevice","key","querySelector","event","stopPropagation","target","closest","secondToLastNavLink","setTimeout","toggleBurgerMenu","_document$body$datase","leftNavState","getInstantClick","_getInstantClick","apply","_asyncToGenerator","waitTime","reject","failTimer","timer","Error","InstantClick","clearTimeout","initializeMobileMenu","menuTriggers","forEach","trigger","onclick","setCurrentPageIconLink","currentPage","pageEntries","filter","_ref","_slicedToArray","_ref3","_ref4","page","iconLink","blur","removeAttribute","trimmedEndIndex","reTrimStart","string","replace","reWhitespace","index","charAt","objectProto","prototype","hasOwnProperty","nativeObjectToString","toString","isOwn","call","tag","unmasked","result","addButtonFollowText","button","style","_JSON$parse","info","name","className","addAriaLabelToButton","followName","followType","textContent","_ref$style","label","pressed","concat","toLowerCase","updateFollowingButton","_JSON$parse3","verb","addButtonFollowingText","updateUserOwnFollowButton","updateFollowButton","newState","buttonInfo","followStyle","handleFollowButtonClick","_ref2","matchingFollowButtons","trackingData","determineSecondarySource","referring_source","showLoginModal","Array","from","getElementsByClassName","id","matchingButton","browserStoreCache","location","href","_JSON$parse4","formData","FormData","append","getCsrfToken","then","sendFetch","response","status","showModalAfterError","element","action_ing","action_past","timeframe","updateInitialButtonUI","followStatus","initializeAllUserFollowButtons","buttons","querySelectorAll","idButtonHash","url","searchParams","userIds","fetched","userStatus","userId","push","keys","URL","URLSearchParams","search","fetch","method","headers","Accept","csrfToken","credentials","json","idStatuses","initializeNonUserFollowButtons","nonUserFollowButtons","user","userData","followedTags","followed_tags","map","followedTagIds","Set","has","text","fetchFollowButtonStatus","followClicksInitialized","observer","MutationObserver","mutationsList","mutation","followButtonContainer","observe","childList","subtree","ic","on","disconnect"],"mappings":"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","file":"js/followButtons-fbadadc01e4f1ccc8167.chunk.js","sourcesContent":["var freeGlobal = require('./_freeGlobal');\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\nmodule.exports = root;\n","/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n}\n\nmodule.exports = isObject;\n","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","var baseGetTag = require('./_baseGetTag'),\n isObjectLike = require('./isObjectLike');\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && baseGetTag(value) == symbolTag);\n}\n\nmodule.exports = isSymbol;\n","var Symbol = require('./_Symbol'),\n getRawTag = require('./_getRawTag'),\n objectToString = require('./_objectToString');\n\n/** `Object#toString` result references. */\nvar nullTag = '[object Null]',\n undefinedTag = '[object Undefined]';\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n}\n\nmodule.exports = baseGetTag;\n","/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return value != null && typeof value == 'object';\n}\n\nmodule.exports = isObjectLike;\n","import { I18n } from 'i18n-js';\n\nconst i18n = new I18n();\n\nconst translationsDiv = document.getElementById('i18n-translations');\nif (translationsDiv) {\n const translations = JSON.parse(translationsDiv.dataset.translations);\n i18n.store(translations);\n}\ni18n.defaultLocale = 'en';\nconst { locale: userLocale } = document.body.dataset;\nif (userLocale) {\n i18n.locale = userLocale;\n}\nexport function locale(term, params = {}) {\n return i18n.t(term, params);\n}\n","var root = require('./_root');\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\n","module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\tmodule.deprecate = function() {};\n\t\tmodule.paths = [];\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n","/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\nmodule.exports = freeGlobal;\n","/**\n * A util function to wrap any code that needs to wait until the page has\n * initialized correctly before executing. This is generally the case for\n * packs/components that require `/app/assets/initializers` to execute first,\n * this way you're ensured that global functions/namespaces will be available\n * (i.e. the Runtime class).\n *\n * @returns {Promise} A chainable promise that will fulfill when the page has\n * loaded correctly and all initializers have run.\n */\nexport function waitOnBaseData() {\n return new Promise((resolve) => {\n const waitingForDataLoad = setInterval(() => {\n if (document.body.getAttribute('data-loaded') === 'true') {\n clearInterval(waitingForDataLoad);\n resolve();\n }\n }, 100);\n });\n}\n","var baseTrim = require('./_baseTrim'),\n isObject = require('./isObject'),\n isSymbol = require('./isSymbol');\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = baseTrim(value);\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = toNumber;\n","/* global isTouchDevice */\n\nfunction closeHeaderMenu(memberMenu, menuNavButton) {\n menuNavButton.setAttribute('aria-expanded', 'false');\n memberMenu.classList.remove('desktop', 'showing');\n delete memberMenu.dataset.clicked;\n}\n\nconst firstItem = document.getElementById('first-nav-link');\n\nfunction openHeaderMenu(memberMenu, menuNavButton) {\n menuNavButton.setAttribute('aria-expanded', 'true');\n memberMenu.classList.add('showing');\n\n if (!firstItem) {\n return;\n }\n\n // Focus on the first item in the menu\n (function focusFirstItem() {\n if (document.activeElement === firstItem) {\n // The first element has focus\n return;\n }\n\n firstItem.focus();\n // requestAnimationFrame is faster and more reliable than setTimeout\n // https://swizec.com/blog/how-to-wait-for-dom-elements-to-show-up-in-modern-browsers\n window.requestAnimationFrame(focusFirstItem);\n })();\n}\n\n/**\n * Initializes the member navigation menu events.\n *\n * @param {HTMLElement} memberTopMenu The member menu in the top right navigation.\n * @param {HTMLElement} menuNavButton The button to activate the member navigation menu.\n */\nexport function initializeMemberMenu(memberTopMenu, menuNavButton) {\n // Typically using CSS for hovering for the menu is the way to go. But... since we use InstantClick for\n // loading pages, the top header navigation never changes in terms of the DOM references.\n // Because of this, we're using mouse events to mouseover/mouseout on the member's avatar\n // to attach styles to get it to show the menu so that this works on desktop and mobile.\n if (navigator.userAgent === 'DEV-Native-ios') {\n document.body.classList.add('dev-ios-native-body');\n }\n const { classList } = memberTopMenu;\n menuNavButton.addEventListener('click', (_event) => {\n if (classList.contains('showing') && memberTopMenu.dataset.clicked) {\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n } else {\n openHeaderMenu(memberTopMenu, menuNavButton);\n memberTopMenu.dataset.clicked = 'clicked';\n }\n });\n\n if (isTouchDevice()) {\n memberTopMenu.addEventListener('focus', (_event) => {\n menuNavButton.setAttribute('aria-expanded', 'true');\n });\n } else {\n memberTopMenu.addEventListener('keyup', (e) => {\n if (e.key === 'Escape' && classList.contains('showing')) {\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n }\n });\n }\n\n memberTopMenu\n .querySelector('.crayons-header__menu__dropdown')\n .addEventListener('click', (event) => {\n // There is a click event listener on the body and we do not want\n // this click to be caught by it\n event.stopPropagation();\n\n // Close the menu if the user clicked or touched on mobile a link in the menu.\n closeHeaderMenu(memberTopMenu, menuNavButton);\n menuNavButton.focus();\n });\n\n document.addEventListener('click', (event) => {\n if (event.target.closest('#member-menu-button') === menuNavButton) {\n // The menu navigation button manages it's own click event.\n return;\n }\n\n // Close the menu if the user clicked or touched on mobile a link in the menu.\n closeHeaderMenu(memberTopMenu, menuNavButton);\n });\n\n const secondToLastNavLink = document.getElementById('second-last-nav-link');\n\n document\n .getElementById('last-nav-link')\n .addEventListener('blur', (_event) => {\n // When we tab out of the last link in the member menu, close\n // the menu.\n setTimeout(() => {\n if (document.activeElement === secondToLastNavLink) {\n return;\n }\n\n closeHeaderMenu(memberTopMenu, menuNavButton);\n }, 10);\n });\n}\n\nfunction toggleBurgerMenu() {\n const { leftNavState = 'closed' } = document.body.dataset;\n document.body.dataset.leftNavState =\n leftNavState === 'open' ? 'closed' : 'open';\n}\n\n/**\n * Gets a reference to InstantClick\n *\n * @param {number} [waitTime=2000] The amount of time to wait\n * until giving up waiting for InstantClick to exist\n *\n * @returns {Promise} The global instance of InstantClick.\n */\nexport async function getInstantClick(waitTime = 2000) {\n return new Promise((resolve, reject) => {\n const failTimer = setTimeout(() => {\n clearInterval(timer);\n reject(new Error('Unable to resolve InstantClick'));\n }, waitTime);\n\n const timer = setInterval(() => {\n if (typeof InstantClick !== 'undefined') {\n clearTimeout(failTimer);\n clearInterval(timer);\n resolve(InstantClick);\n }\n });\n });\n}\n\n/**\n * Initializes the hamburger menu for mobile navigation\n *\n * @param {HTMLElement[]} menuTriggers The menuTriggers include the hamburger to open the menu,\n * the close icon to close the menu and the overlay to close the menu.\n */\nexport function initializeMobileMenu(menuTriggers) {\n menuTriggers.forEach((trigger) => {\n trigger.onclick = toggleBurgerMenu;\n });\n}\n\n/**\n * Sets the icon link visually for the current page if the current page\n * is one of the main icon links of the top navigation.\n *\n * @param {string} currentPage\n * @param {[string, HTMLElement][]} pageEntries\n */\nexport function setCurrentPageIconLink(currentPage, pageEntries) {\n pageEntries\n // Filter out nulls (means the user is logged out so most icons are not in the logged out view)\n .filter(([, iconLink]) => iconLink)\n .forEach(([page, iconLink]) => {\n if (currentPage === page) {\n iconLink.blur();\n iconLink.setAttribute('aria-current', 'page');\n } else {\n iconLink.removeAttribute('aria-current');\n }\n });\n}\n","var trimmedEndIndex = require('./_trimmedEndIndex');\n\n/** Used to match leading whitespace. */\nvar reTrimStart = /^\\s+/;\n\n/**\n * The base implementation of `_.trim`.\n *\n * @private\n * @param {string} string The string to trim.\n * @returns {string} Returns the trimmed string.\n */\nfunction baseTrim(string) {\n return string\n ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')\n : string;\n}\n\nmodule.exports = baseTrim;\n","/** Used to match a single whitespace character. */\nvar reWhitespace = /\\s/;\n\n/**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace\n * character of `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the index of the last non-whitespace character.\n */\nfunction trimmedEndIndex(string) {\n var index = string.length;\n\n while (index-- && reWhitespace.test(string.charAt(index))) {}\n return index;\n}\n\nmodule.exports = trimmedEndIndex;\n","var Symbol = require('./_Symbol');\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\nfunction getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n}\n\nmodule.exports = getRawTag;\n","/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\nfunction objectToString(value) {\n return nativeObjectToString.call(value);\n}\n\nmodule.exports = objectToString;\n","import { getInstantClick } from '../topNavigation/utilities';\nimport { waitOnBaseData } from '../utilities/waitOnBaseData';\nimport { locale } from '@utilities/locale';\n\n/* global showLoginModal userData showModalAfterError browserStoreCache */\n\n/**\n * Sets the text content of the button to the correct 'Follow' state\n *\n * @param {HTMLElement} button The Follow button to update\n * @param {string} style The style of the button from its \"info\" data attribute\n */\n\nfunction addButtonFollowText(button, style) {\n const { name, className } = JSON.parse(button.dataset.info);\n\n switch (style) {\n case 'small':\n addAriaLabelToButton({\n button,\n followName: name,\n followType: className,\n style: 'follow',\n });\n button.textContent = '+';\n break;\n case 'follow-back':\n addAriaLabelToButton({\n button,\n followName: name,\n followType: className,\n style: 'follow-back',\n });\n button.textContent = locale('core.follow_back');\n break;\n default:\n addAriaLabelToButton({\n button,\n followName: name,\n followType: className,\n style: 'follow',\n });\n button.textContent = locale('core.follow');\n }\n}\n\n/**\n * Sets the aria-label and aria-pressed value of the button\n *\n * @param {HTMLElement} button The Follow button to update.\n * @param {string} followType The followableType of the button.\n * @param {string} followName The name of the followable to be followed.\n * @param {string} style The style of the button from its \"info\" data attribute\n */\nfunction addAriaLabelToButton({ button, followType, followName, style = '' }) {\n let label = '';\n let pressed = '';\n switch (style) {\n case 'follow':\n label = `Follow ${followType.toLowerCase()}: ${followName}`;\n pressed = 'false';\n break;\n case 'follow-back':\n label = `Follow ${followType.toLowerCase()} back: ${followName}`;\n pressed = 'false';\n break;\n case 'following':\n label = `Follow ${followType.toLowerCase()}: ${followName}`;\n pressed = 'true';\n break;\n case 'self':\n label = `Edit profile`;\n break;\n default:\n label = `Follow ${followType.toLowerCase()}: ${followName}`;\n pressed = 'false';\n }\n button.setAttribute('aria-label', label);\n pressed.length === 0\n ? button.removeAttribute('aria-pressed')\n : button.setAttribute('aria-pressed', pressed);\n}\n\n/**\n * Sets the text content of the button to the correct 'Following' state\n *\n * @param {HTMLElement} button The Follow button to update\n * @param {string} style The style of the button from its \"info\" data attribute\n */\nfunction addButtonFollowingText(button, style) {\n button.textContent = style === 'small' ? '✓' : locale('core.following');\n}\n\n/**\n * Changes the visual appearance and 'verb' of the button to match the new state\n *\n * @param {HTMLElement} button The Follow button to be updated\n */\nfunction optimisticallyUpdateButtonUI(button) {\n const { verb: newState } = button.dataset;\n const buttonInfo = JSON.parse(button.dataset.info);\n const { style } = buttonInfo;\n\n // Often there are multiple follow buttons for the same followable item on the page\n // We collect all buttons which match the click, and update them all\n const matchingFollowButtons = Array.from(\n document.getElementsByClassName('follow-action-button'),\n ).filter((button) => {\n const { info } = button.dataset;\n if (info) {\n const { id } = JSON.parse(info);\n return id === buttonInfo.id;\n }\n return false;\n });\n\n matchingFollowButtons.forEach((matchingButton) => {\n matchingButton.classList.add('showing');\n\n switch (newState) {\n case 'follow':\n case 'follow-back':\n updateFollowButton(matchingButton, newState, buttonInfo);\n break;\n case 'login':\n addButtonFollowText(matchingButton, style);\n break;\n case 'self':\n updateUserOwnFollowButton(matchingButton);\n break;\n default:\n updateFollowingButton(matchingButton, style);\n }\n });\n}\n\n/**\n * Set the Follow button's UI to the 'following' state\n *\n * @param {HTMLElement} button The Follow button to be updated\n * @param {string} style Style of the follow button (e.g. 'small')\n */\nfunction updateFollowingButton(button, style) {\n const { name, className } = JSON.parse(button.dataset.info);\n button.dataset.verb = 'follow';\n addButtonFollowingText(button, style);\n button.classList.remove('crayons-btn--primary');\n button.classList.remove('crayons-btn--secondary');\n button.classList.add('crayons-btn--outlined');\n addAriaLabelToButton({\n button,\n followName: name,\n followType: className,\n style: 'following',\n });\n}\n\n/**\n * Update the UI of the given button to the user's own button - i.e. 'Edit profile'\n *\n * @param {HTMLElement} button The Follow button to be updated\n */\nfunction updateUserOwnFollowButton(button) {\n button.dataset.verb = 'self';\n button.textContent = locale('core.edit_profile');\n addAriaLabelToButton({\n button,\n followName: '',\n followType: '',\n style: 'self',\n });\n}\n\n/**\n * Update the UI of the given button to the 'follow' or 'follow-back' state\n *\n * @param {HTMLElement} button The Follow button to be updated\n * @param {string} newState The new follow state of the button\n * @param {Object} buttonInfo The parsed info object obtained from the button's dataset\n * @param {string} buttonInfo.style The style of the follow button (e.g 'small')\n * @param {string} buttonInfo.followStyle The crayons button variant (e.g 'primary')\n */\nfunction updateFollowButton(button, newState, buttonInfo) {\n const { style, followStyle } = buttonInfo;\n\n button.dataset.verb = 'unfollow';\n button.classList.remove('crayons-btn--outlined');\n\n if (followStyle === 'primary') {\n button.classList.add('crayons-btn--primary');\n } else if (followStyle === 'secondary') {\n button.classList.add('crayons-btn--secondary');\n }\n\n const nextButtonStyle = newState === 'follow-back' ? newState : style;\n addButtonFollowText(button, nextButtonStyle);\n}\n\n/**\n * Checks a click event's target, and if it is a follow button, triggers the appropriate follow action\n *\n * @param {HTMLElement} target The target of the click event\n */\nfunction handleFollowButtonClick({ target }) {\n if (\n target.classList.contains('follow-action-button') ||\n target.classList.contains('follow-user')\n ) {\n const userStatus = document.body.getAttribute('data-user-status');\n if (userStatus === 'logged-out') {\n let trackingData = {};\n if (determineSecondarySource(target)) {\n trackingData = {\n referring_source: determineSecondarySource(target),\n trigger: 'follow_button',\n };\n }\n showLoginModal(trackingData);\n return;\n }\n\n optimisticallyUpdateButtonUI(target);\n browserStoreCache('remove');\n\n const { verb } = target.dataset;\n\n if (verb === 'self') {\n window.location.href = '/settings';\n return;\n }\n\n const { className, id } = JSON.parse(target.dataset.info);\n const formData = new FormData();\n formData.append('followable_type', className);\n formData.append('followable_id', id);\n formData.append('verb', verb);\n getCsrfToken()\n .then(sendFetch('follow-creation', formData))\n .then((response) => {\n if (response.status !== 200) {\n showModalAfterError({\n response,\n element: 'user',\n action_ing: 'following',\n action_past: 'followed',\n timeframe: 'for a day',\n });\n }\n });\n }\n}\n\n/**\n * Determines where the click came from for event tracking\n */\nfunction determineSecondarySource(target) {\n // The follow user buttons have both follow-action-button and follow-user\n // classnames on them. For now we only want to\n // implement tracking for follow-user.\n if (target.classList.contains('follow-user')) {\n return 'user';\n }\n}\n\n/**\n * Adds an event listener to the inner page content, to handle any and all follow button clicks with a single handler\n */\nfunction listenForFollowButtonClicks() {\n document\n .getElementById('page-content-inner')\n .addEventListener('click', handleFollowButtonClick);\n\n document.getElementById(\n 'page-content-inner',\n ).dataset.followClicksInitialized = true;\n}\n\n/**\n * Sets the UI of the button based on the current following status\n *\n * @param {string} followStatus The current following status for the button\n * @param {HTMLElement} button The button to update\n */\nfunction updateInitialButtonUI(followStatus, button) {\n const buttonInfo = JSON.parse(button.dataset.info);\n const { style } = buttonInfo;\n button.classList.add('showing');\n\n switch (followStatus) {\n case 'true':\n case 'mutual':\n updateFollowingButton(button, style);\n break;\n case 'follow-back':\n addButtonFollowText(button, followStatus);\n break;\n case 'false':\n updateFollowButton(button, 'follow', buttonInfo);\n break;\n case 'self':\n updateUserOwnFollowButton(button);\n break;\n default:\n addButtonFollowText(button, style);\n }\n}\n\n/**\n * Fetches all user 'follow statuses' for the given userIds, and then updates the UI for all buttons related to each user\n *\n * @param {Object} idButtonHash A hash of user IDs and the array buttons which relate to them\n */\nfunction fetchUserFollowStatuses(idButtonHash) {\n const url = new URL('/follows/bulk_show', document.location);\n const searchParams = new URLSearchParams();\n Object.keys(idButtonHash).forEach((id) => {\n searchParams.append('ids[]', id);\n });\n searchParams.append('followable_type', 'User');\n url.search = searchParams;\n\n fetch(url, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'X-CSRF-Token': window.csrfToken,\n 'Content-Type': 'application/json',\n },\n credentials: 'same-origin',\n })\n .then((response) => response.json())\n .then((idStatuses) => {\n Object.keys(idStatuses).forEach((id) => {\n idButtonHash[id].forEach((button) => {\n updateInitialButtonUI(idStatuses[id], button);\n });\n });\n });\n}\n\n/**\n * Sets up the initial state of all user follow buttons on the page,\n * by obtaining the 'follow status' of each user and updating the associated buttons' UI.\n */\nfunction initializeAllUserFollowButtons() {\n const buttons = document.querySelectorAll(\n '.follow-action-button.follow-user:not([data-fetched])',\n );\n\n if (buttons.length === 0) {\n return;\n }\n\n const userIds = {};\n\n Array.from(buttons, (button) => {\n button.dataset.fetched = 'fetched';\n const { userStatus } = document.body.dataset;\n const buttonInfo = JSON.parse(button.dataset.info);\n const { name, className } = buttonInfo;\n\n if (userStatus === 'logged-out') {\n const { style } = buttonInfo;\n addButtonFollowText(button, style);\n } else {\n addAriaLabelToButton({ button, followType: className, followName: name });\n const { id: userId } = buttonInfo;\n if (userIds[userId]) {\n userIds[userId].push(button);\n } else {\n userIds[userId] = [button];\n }\n }\n });\n\n if (Object.keys(userIds).length > 0) {\n fetchUserFollowStatuses(userIds);\n }\n}\n\n/**\n * Individually fetches the current status of a follow button and updates the UI to match\n *\n * @param {HTMLElement} button\n * @param {Object} buttonInfo The parsed buttonInfo object obtained from the button's data-attribute\n */\nfunction fetchFollowButtonStatus(button, buttonInfo) {\n button.dataset.fetched = 'fetched';\n\n fetch(`/follows/${buttonInfo.id}?followable_type=${buttonInfo.className}`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'X-CSRF-Token': window.csrfToken,\n 'Content-Type': 'application/json',\n },\n credentials: 'same-origin',\n })\n .then((response) => response.text())\n .then((followStatus) => {\n updateInitialButtonUI(followStatus, button);\n });\n}\n\n/**\n * Makes sure the initial state of follow buttons is fetched and presented in the UI.\n * User follow buttons are initialized separately via bulk request\n */\nfunction initializeNonUserFollowButtons() {\n const nonUserFollowButtons = document.querySelectorAll(\n '.follow-action-button:not(.follow-user):not([data-fetched])',\n );\n\n waitOnBaseData().then(() => {\n const userLoggedIn =\n document.body.getAttribute('data-user-status') === 'logged-in';\n\n const user = userLoggedIn ? userData() : null;\n\n const followedTags = user\n ? JSON.parse(user.followed_tags).map((tag) => tag.id)\n : [];\n\n const followedTagIds = new Set(followedTags);\n\n nonUserFollowButtons.forEach((button) => {\n const { info } = button.dataset;\n const buttonInfo = JSON.parse(info);\n const { className, name } = buttonInfo;\n addAriaLabelToButton({ button, followType: className, followName: name });\n if (className === 'Tag' && user) {\n // We don't need to make a network request to 'fetch' the status of tag buttons\n button.dataset.fetched = true;\n const initialButtonFollowState = followedTagIds.has(buttonInfo.id)\n ? 'true'\n : 'false';\n updateInitialButtonUI(initialButtonFollowState, button);\n } else {\n fetchFollowButtonStatus(button, buttonInfo);\n }\n });\n });\n}\n\ninitializeAllUserFollowButtons();\ninitializeNonUserFollowButtons();\nlistenForFollowButtonClicks();\n\n// Some follow buttons are added to the DOM dynamically, e.g. search results,\n// So we listen for any new additions to be fetched\nconst observer = new MutationObserver((mutationsList) => {\n mutationsList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n initializeAllUserFollowButtons();\n initializeNonUserFollowButtons();\n }\n });\n});\n\n// Any element containing the given data-attribute will be monitored for new follow buttons\ndocument\n .querySelectorAll('[data-follow-button-container]')\n .forEach((followButtonContainer) => {\n observer.observe(followButtonContainer, {\n childList: true,\n subtree: true,\n });\n });\n\ngetInstantClick().then((ic) => {\n ic.on('change', () => {\n observer.disconnect();\n });\n});\n\nwindow.addEventListener('beforeunload', () => {\n observer.disconnect();\n});\n"],"sourceRoot":""}