{"version":3,"sources":["webpack:///./node_modules/lodash/isSymbol.js","webpack:///./node_modules/lodash/_baseGetTag.js","webpack:///./node_modules/lodash/isObjectLike.js","webpack:///./node_modules/lodash/_Symbol.js","webpack:///(webpack)/buildin/global.js","webpack:///./app/javascript/utilities/locale.js","webpack:///(webpack)/buildin/module.js","webpack:///./node_modules/lodash/_root.js","webpack:///./node_modules/lodash/_freeGlobal.js","webpack:///./node_modules/lodash/toNumber.js","webpack:///./app/javascript/topNavigation/utilities.js","webpack:///./node_modules/lodash/isObject.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":["baseGetTag","isObjectLike","module","exports","value","Symbol","getRawTag","objectToString","symToStringTag","toStringTag","undefined","Object","g","this","Function","e","window","i18n","I18n","translationsDiv","document","getElementById","translations","JSON","parse","dataset","store","defaultLocale","userLocale","body","locale","term","t","localeArray","match","webpackPolyfill","deprecate","paths","children","defineProperty","enumerable","get","l","i","freeGlobal","freeSelf","self","root","global","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","leftNavState","getInstantClick","waitTime","Promise","resolve","reject","failTimer","clearInterval","timer","Error","setInterval","InstantClick","clearTimeout","initializeMobileMenu","menuTriggers","forEach","trigger","onclick","setCurrentPageIconLink","currentPage","pageEntries","filter","page","iconLink","blur","removeAttribute","type","trimmedEndIndex","reTrimStart","string","replace","reWhitespace","index","length","charAt","objectProto","prototype","hasOwnProperty","nativeObjectToString","toString","isOwn","call","tag","unmasked","result","addButtonFollowText","button","style","info","name","className","addAriaLabelToButton","followName","followType","textContent","label","pressed","toLowerCase","updateFollowingButton","verb","addButtonFollowingText","updateUserOwnFollowButton","updateFollowButton","newState","buttonInfo","followStyle","handleFollowButtonClick","matchingFollowButtons","getAttribute","trackingData","determineSecondarySource","referring_source","showLoginModal","Array","from","getElementsByClassName","id","matchingButton","location","href","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-0cd076e7674b95c00292.chunk.js","sourcesContent":["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","var root = require('./_root');\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\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","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) {\n return i18n.t(term);\n}\n\nexport function localeArray(term) {\n return `${i18n.t(term)}`.match(/^\\[missing.*translation\\]$/)\n ? []\n : i18n.t(term);\n}\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","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","/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\nmodule.exports = freeGlobal;\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","/**\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 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 { locale } from '@utilities/locale';\n\n/* global showLoginModal userData showModalAfterError*/\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\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 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\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":""}