{ "version": 3, "sources": ["../../../../node_modules/events/events.js", "../../../../node_modules/intersection-observer/intersection-observer.js", "../../../../node_modules/@rails/ujs/app/assets/javascripts/rails-ujs.esm.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/elm_google_translate.js", "../../../../node_modules/@ungap/custom-elements/index.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/header.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/event_bus.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/remote_content.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/lazy_svg_sprite.js", "../../../../node_modules/body-scroll-lock/lib/bodyScrollLock.esm.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/modal.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/theme_common.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/doc_cookies.js", "../../../javascript/shared/referrer_tracking.js", "../../../javascript/shared/lazy_srcset.js", "../../../javascript/shared/common.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/media_slider/slide_zoom_controls.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/media_slider/slide.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/media_slider/video_slide.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/media_slider/slide_controller.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/helpers/attributes.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/media_slider.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/video.js", "../../../../node_modules/@carwow/carwow_theme/app/javascript/components/gallery.js", "../../../javascript/shared/gallery.js", "../../../../node_modules/lite-youtube-embed/src/lite-yt-embed.js", "../../../javascript/pages/partner_site_blog_posts/show.js"], "sourcesContent": ["// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function errorListener(err) {\n emitter.removeListener(name, resolver);\n reject(err);\n }\n\n function resolver() {\n if (typeof emitter.removeListener === 'function') {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n\n eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });\n if (name !== 'error') {\n addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });\n }\n });\n}\n\nfunction addErrorHandlerIfEventEmitter(emitter, handler, flags) {\n if (typeof emitter.on === 'function') {\n eventTargetAgnosticAddListener(emitter, 'error', handler, flags);\n }\n}\n\nfunction eventTargetAgnosticAddListener(emitter, name, listener, flags) {\n if (typeof emitter.on === 'function') {\n if (flags.once) {\n emitter.once(name, listener);\n } else {\n emitter.on(name, listener);\n }\n } else if (typeof emitter.addEventListener === 'function') {\n // EventTarget does not have `error` event semantics like Node\n // EventEmitters, we do not listen for `error` events here.\n emitter.addEventListener(name, function wrapListener(arg) {\n // IE does not have builtin `{ once: true }` support so we\n // have to do it manually.\n if (flags.once) {\n emitter.removeEventListener(name, wrapListener);\n }\n listener(arg);\n });\n } else {\n throw new TypeError('The \"emitter\" argument must be of type EventEmitter. Received type ' + typeof emitter);\n }\n}\n", "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.\n *\n * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document\n *\n */\n(function() {\n'use strict';\n\n// Exit early if we're not running in a browser.\nif (typeof window !== 'object') {\n return;\n}\n\n// Exit early if all IntersectionObserver and IntersectionObserverEntry\n// features are natively supported.\nif ('IntersectionObserver' in window &&\n 'IntersectionObserverEntry' in window &&\n 'intersectionRatio' in window.IntersectionObserverEntry.prototype) {\n\n // Minimal polyfill for Edge 15's lack of `isIntersecting`\n // See: https://github.com/w3c/IntersectionObserver/issues/211\n if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {\n Object.defineProperty(window.IntersectionObserverEntry.prototype,\n 'isIntersecting', {\n get: function () {\n return this.intersectionRatio > 0;\n }\n });\n }\n return;\n}\n\n/**\n * Returns the embedding frame element, if any.\n * @param {!Document} doc\n * @return {!Element}\n */\nfunction getFrameElement(doc) {\n try {\n return doc.defaultView && doc.defaultView.frameElement || null;\n } catch (e) {\n // Ignore the error.\n return null;\n }\n}\n\n/**\n * A local reference to the root document.\n */\nvar document = (function(startDoc) {\n var doc = startDoc;\n var frame = getFrameElement(doc);\n while (frame) {\n doc = frame.ownerDocument;\n frame = getFrameElement(doc);\n }\n return doc;\n})(window.document);\n\n/**\n * An IntersectionObserver registry. This registry exists to hold a strong\n * reference to IntersectionObserver instances currently observing a target\n * element. Without this registry, instances without another reference may be\n * garbage collected.\n */\nvar registry = [];\n\n/**\n * The signal updater for cross-origin intersection. When not null, it means\n * that the polyfill is configured to work in a cross-origin mode.\n * @type {function(DOMRect|ClientRect, DOMRect|ClientRect)}\n */\nvar crossOriginUpdater = null;\n\n/**\n * The current cross-origin intersection. Only used in the cross-origin mode.\n * @type {DOMRect|ClientRect}\n */\nvar crossOriginRect = null;\n\n\n/**\n * Creates the global IntersectionObserverEntry constructor.\n * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry\n * @param {Object} entry A dictionary of instance properties.\n * @constructor\n */\nfunction IntersectionObserverEntry(entry) {\n this.time = entry.time;\n this.target = entry.target;\n this.rootBounds = ensureDOMRect(entry.rootBounds);\n this.boundingClientRect = ensureDOMRect(entry.boundingClientRect);\n this.intersectionRect = ensureDOMRect(entry.intersectionRect || getEmptyRect());\n this.isIntersecting = !!entry.intersectionRect;\n\n // Calculates the intersection ratio.\n var targetRect = this.boundingClientRect;\n var targetArea = targetRect.width * targetRect.height;\n var intersectionRect = this.intersectionRect;\n var intersectionArea = intersectionRect.width * intersectionRect.height;\n\n // Sets intersection ratio.\n if (targetArea) {\n // Round the intersection ratio to avoid floating point math issues:\n // https://github.com/w3c/IntersectionObserver/issues/324\n this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4));\n } else {\n // If area is zero and is intersecting, sets to 1, otherwise to 0\n this.intersectionRatio = this.isIntersecting ? 1 : 0;\n }\n}\n\n\n/**\n * Creates the global IntersectionObserver constructor.\n * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface\n * @param {Function} callback The function to be invoked after intersection\n * changes have queued. The function is not invoked if the queue has\n * been emptied by calling the `takeRecords` method.\n * @param {Object=} opt_options Optional configuration options.\n * @constructor\n */\nfunction IntersectionObserver(callback, opt_options) {\n\n var options = opt_options || {};\n\n if (typeof callback != 'function') {\n throw new Error('callback must be a function');\n }\n\n if (\n options.root &&\n options.root.nodeType != 1 &&\n options.root.nodeType != 9\n ) {\n throw new Error('root must be a Document or Element');\n }\n\n // Binds and throttles `this._checkForIntersections`.\n this._checkForIntersections = throttle(\n this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT);\n\n // Private properties.\n this._callback = callback;\n this._observationTargets = [];\n this._queuedEntries = [];\n this._rootMarginValues = this._parseRootMargin(options.rootMargin);\n\n // Public properties.\n this.thresholds = this._initThresholds(options.threshold);\n this.root = options.root || null;\n this.rootMargin = this._rootMarginValues.map(function(margin) {\n return margin.value + margin.unit;\n }).join(' ');\n\n /** @private @const {!Array} */\n this._monitoringDocuments = [];\n /** @private @const {!Array} */\n this._monitoringUnsubscribes = [];\n}\n\n\n/**\n * The minimum interval within which the document will be checked for\n * intersection changes.\n */\nIntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;\n\n\n/**\n * The frequency in which the polyfill polls for intersection changes.\n * this can be updated on a per instance basis and must be set prior to\n * calling `observe` on the first target.\n */\nIntersectionObserver.prototype.POLL_INTERVAL = null;\n\n/**\n * Use a mutation observer on the root element\n * to detect intersection changes.\n */\nIntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;\n\n\n/**\n * Sets up the polyfill in the cross-origin mode. The result is the\n * updater function that accepts two arguments: `boundingClientRect` and\n * `intersectionRect` - just as these fields would be available to the\n * parent via `IntersectionObserverEntry`. This function should be called\n * each time the iframe receives intersection information from the parent\n * window, e.g. via messaging.\n * @return {function(DOMRect|ClientRect, DOMRect|ClientRect)}\n */\nIntersectionObserver._setupCrossOriginUpdater = function() {\n if (!crossOriginUpdater) {\n /**\n * @param {DOMRect|ClientRect} boundingClientRect\n * @param {DOMRect|ClientRect} intersectionRect\n */\n crossOriginUpdater = function(boundingClientRect, intersectionRect) {\n if (!boundingClientRect || !intersectionRect) {\n crossOriginRect = getEmptyRect();\n } else {\n crossOriginRect = convertFromParentRect(boundingClientRect, intersectionRect);\n }\n registry.forEach(function(observer) {\n observer._checkForIntersections();\n });\n };\n }\n return crossOriginUpdater;\n};\n\n\n/**\n * Resets the cross-origin mode.\n */\nIntersectionObserver._resetCrossOriginUpdater = function() {\n crossOriginUpdater = null;\n crossOriginRect = null;\n};\n\n\n/**\n * Starts observing a target element for intersection changes based on\n * the thresholds values.\n * @param {Element} target The DOM element to observe.\n */\nIntersectionObserver.prototype.observe = function(target) {\n var isTargetAlreadyObserved = this._observationTargets.some(function(item) {\n return item.element == target;\n });\n\n if (isTargetAlreadyObserved) {\n return;\n }\n\n if (!(target && target.nodeType == 1)) {\n throw new Error('target must be an Element');\n }\n\n this._registerInstance();\n this._observationTargets.push({element: target, entry: null});\n this._monitorIntersections(target.ownerDocument);\n this._checkForIntersections();\n};\n\n\n/**\n * Stops observing a target element for intersection changes.\n * @param {Element} target The DOM element to observe.\n */\nIntersectionObserver.prototype.unobserve = function(target) {\n this._observationTargets =\n this._observationTargets.filter(function(item) {\n return item.element != target;\n });\n this._unmonitorIntersections(target.ownerDocument);\n if (this._observationTargets.length == 0) {\n this._unregisterInstance();\n }\n};\n\n\n/**\n * Stops observing all target elements for intersection changes.\n */\nIntersectionObserver.prototype.disconnect = function() {\n this._observationTargets = [];\n this._unmonitorAllIntersections();\n this._unregisterInstance();\n};\n\n\n/**\n * Returns any queue entries that have not yet been reported to the\n * callback and clears the queue. This can be used in conjunction with the\n * callback to obtain the absolute most up-to-date intersection information.\n * @return {Array} The currently queued entries.\n */\nIntersectionObserver.prototype.takeRecords = function() {\n var records = this._queuedEntries.slice();\n this._queuedEntries = [];\n return records;\n};\n\n\n/**\n * Accepts the threshold value from the user configuration object and\n * returns a sorted array of unique threshold values. If a value is not\n * between 0 and 1 and error is thrown.\n * @private\n * @param {Array|number=} opt_threshold An optional threshold value or\n * a list of threshold values, defaulting to [0].\n * @return {Array} A sorted list of unique and valid threshold values.\n */\nIntersectionObserver.prototype._initThresholds = function(opt_threshold) {\n var threshold = opt_threshold || [0];\n if (!Array.isArray(threshold)) threshold = [threshold];\n\n return threshold.sort().filter(function(t, i, a) {\n if (typeof t != 'number' || isNaN(t) || t < 0 || t > 1) {\n throw new Error('threshold must be a number between 0 and 1 inclusively');\n }\n return t !== a[i - 1];\n });\n};\n\n\n/**\n * Accepts the rootMargin value from the user configuration object\n * and returns an array of the four margin values as an object containing\n * the value and unit properties. If any of the values are not properly\n * formatted or use a unit other than px or %, and error is thrown.\n * @private\n * @param {string=} opt_rootMargin An optional rootMargin value,\n * defaulting to '0px'.\n * @return {Array} An array of margin objects with the keys\n * value and unit.\n */\nIntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) {\n var marginString = opt_rootMargin || '0px';\n var margins = marginString.split(/\\s+/).map(function(margin) {\n var parts = /^(-?\\d*\\.?\\d+)(px|%)$/.exec(margin);\n if (!parts) {\n throw new Error('rootMargin must be specified in pixels or percent');\n }\n return {value: parseFloat(parts[1]), unit: parts[2]};\n });\n\n // Handles shorthand.\n margins[1] = margins[1] || margins[0];\n margins[2] = margins[2] || margins[0];\n margins[3] = margins[3] || margins[1];\n\n return margins;\n};\n\n\n/**\n * Starts polling for intersection changes if the polling is not already\n * happening, and if the page's visibility state is visible.\n * @param {!Document} doc\n * @private\n */\nIntersectionObserver.prototype._monitorIntersections = function(doc) {\n var win = doc.defaultView;\n if (!win) {\n // Already destroyed.\n return;\n }\n if (this._monitoringDocuments.indexOf(doc) != -1) {\n // Already monitoring.\n return;\n }\n\n // Private state for monitoring.\n var callback = this._checkForIntersections;\n var monitoringInterval = null;\n var domObserver = null;\n\n // If a poll interval is set, use polling instead of listening to\n // resize and scroll events or DOM mutations.\n if (this.POLL_INTERVAL) {\n monitoringInterval = win.setInterval(callback, this.POLL_INTERVAL);\n } else {\n addEvent(win, 'resize', callback, true);\n addEvent(doc, 'scroll', callback, true);\n if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in win) {\n domObserver = new win.MutationObserver(callback);\n domObserver.observe(doc, {\n attributes: true,\n childList: true,\n characterData: true,\n subtree: true\n });\n }\n }\n\n this._monitoringDocuments.push(doc);\n this._monitoringUnsubscribes.push(function() {\n // Get the window object again. When a friendly iframe is destroyed, it\n // will be null.\n var win = doc.defaultView;\n\n if (win) {\n if (monitoringInterval) {\n win.clearInterval(monitoringInterval);\n }\n removeEvent(win, 'resize', callback, true);\n }\n\n removeEvent(doc, 'scroll', callback, true);\n if (domObserver) {\n domObserver.disconnect();\n }\n });\n\n // Also monitor the parent.\n var rootDoc =\n (this.root && (this.root.ownerDocument || this.root)) || document;\n if (doc != rootDoc) {\n var frame = getFrameElement(doc);\n if (frame) {\n this._monitorIntersections(frame.ownerDocument);\n }\n }\n};\n\n\n/**\n * Stops polling for intersection changes.\n * @param {!Document} doc\n * @private\n */\nIntersectionObserver.prototype._unmonitorIntersections = function(doc) {\n var index = this._monitoringDocuments.indexOf(doc);\n if (index == -1) {\n return;\n }\n\n var rootDoc =\n (this.root && (this.root.ownerDocument || this.root)) || document;\n\n // Check if any dependent targets are still remaining.\n var hasDependentTargets =\n this._observationTargets.some(function(item) {\n var itemDoc = item.element.ownerDocument;\n // Target is in this context.\n if (itemDoc == doc) {\n return true;\n }\n // Target is nested in this context.\n while (itemDoc && itemDoc != rootDoc) {\n var frame = getFrameElement(itemDoc);\n itemDoc = frame && frame.ownerDocument;\n if (itemDoc == doc) {\n return true;\n }\n }\n return false;\n });\n if (hasDependentTargets) {\n return;\n }\n\n // Unsubscribe.\n var unsubscribe = this._monitoringUnsubscribes[index];\n this._monitoringDocuments.splice(index, 1);\n this._monitoringUnsubscribes.splice(index, 1);\n unsubscribe();\n\n // Also unmonitor the parent.\n if (doc != rootDoc) {\n var frame = getFrameElement(doc);\n if (frame) {\n this._unmonitorIntersections(frame.ownerDocument);\n }\n }\n};\n\n\n/**\n * Stops polling for intersection changes.\n * @param {!Document} doc\n * @private\n */\nIntersectionObserver.prototype._unmonitorAllIntersections = function() {\n var unsubscribes = this._monitoringUnsubscribes.slice(0);\n this._monitoringDocuments.length = 0;\n this._monitoringUnsubscribes.length = 0;\n for (var i = 0; i < unsubscribes.length; i++) {\n unsubscribes[i]();\n }\n};\n\n\n/**\n * Scans each observation target for intersection changes and adds them\n * to the internal entries queue. If new entries are found, it\n * schedules the callback to be invoked.\n * @private\n */\nIntersectionObserver.prototype._checkForIntersections = function() {\n if (!this.root && crossOriginUpdater && !crossOriginRect) {\n // Cross origin monitoring, but no initial data available yet.\n return;\n }\n\n var rootIsInDom = this._rootIsInDom();\n var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect();\n\n this._observationTargets.forEach(function(item) {\n var target = item.element;\n var targetRect = getBoundingClientRect(target);\n var rootContainsTarget = this._rootContainsTarget(target);\n var oldEntry = item.entry;\n var intersectionRect = rootIsInDom && rootContainsTarget &&\n this._computeTargetAndRootIntersection(target, targetRect, rootRect);\n\n var rootBounds = null;\n if (!this._rootContainsTarget(target)) {\n rootBounds = getEmptyRect();\n } else if (!crossOriginUpdater || this.root) {\n rootBounds = rootRect;\n }\n\n var newEntry = item.entry = new IntersectionObserverEntry({\n time: now(),\n target: target,\n boundingClientRect: targetRect,\n rootBounds: rootBounds,\n intersectionRect: intersectionRect\n });\n\n if (!oldEntry) {\n this._queuedEntries.push(newEntry);\n } else if (rootIsInDom && rootContainsTarget) {\n // If the new entry intersection ratio has crossed any of the\n // thresholds, add a new entry.\n if (this._hasCrossedThreshold(oldEntry, newEntry)) {\n this._queuedEntries.push(newEntry);\n }\n } else {\n // If the root is not in the DOM or target is not contained within\n // root but the previous entry for this target had an intersection,\n // add a new record indicating removal.\n if (oldEntry && oldEntry.isIntersecting) {\n this._queuedEntries.push(newEntry);\n }\n }\n }, this);\n\n if (this._queuedEntries.length) {\n this._callback(this.takeRecords(), this);\n }\n};\n\n\n/**\n * Accepts a target and root rect computes the intersection between then\n * following the algorithm in the spec.\n * TODO(philipwalton): at this time clip-path is not considered.\n * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo\n * @param {Element} target The target DOM element\n * @param {Object} targetRect The bounding rect of the target.\n * @param {Object} rootRect The bounding rect of the root after being\n * expanded by the rootMargin value.\n * @return {?Object} The final intersection rect object or undefined if no\n * intersection is found.\n * @private\n */\nIntersectionObserver.prototype._computeTargetAndRootIntersection =\n function(target, targetRect, rootRect) {\n // If the element isn't displayed, an intersection can't happen.\n if (window.getComputedStyle(target).display == 'none') return;\n\n var intersectionRect = targetRect;\n var parent = getParentNode(target);\n var atRoot = false;\n\n while (!atRoot && parent) {\n var parentRect = null;\n var parentComputedStyle = parent.nodeType == 1 ?\n window.getComputedStyle(parent) : {};\n\n // If the parent isn't displayed, an intersection can't happen.\n if (parentComputedStyle.display == 'none') return null;\n\n if (parent == this.root || parent.nodeType == /* DOCUMENT */ 9) {\n atRoot = true;\n if (parent == this.root || parent == document) {\n if (crossOriginUpdater && !this.root) {\n if (!crossOriginRect ||\n crossOriginRect.width == 0 && crossOriginRect.height == 0) {\n // A 0-size cross-origin intersection means no-intersection.\n parent = null;\n parentRect = null;\n intersectionRect = null;\n } else {\n parentRect = crossOriginRect;\n }\n } else {\n parentRect = rootRect;\n }\n } else {\n // Check if there's a frame that can be navigated to.\n var frame = getParentNode(parent);\n var frameRect = frame && getBoundingClientRect(frame);\n var frameIntersect =\n frame &&\n this._computeTargetAndRootIntersection(frame, frameRect, rootRect);\n if (frameRect && frameIntersect) {\n parent = frame;\n parentRect = convertFromParentRect(frameRect, frameIntersect);\n } else {\n parent = null;\n intersectionRect = null;\n }\n }\n } else {\n // If the element has a non-visible overflow, and it's not the \n // or element, update the intersection rect.\n // Note: and cannot be clipped to a rect that's not also\n // the document rect, so no need to compute a new intersection.\n var doc = parent.ownerDocument;\n if (parent != doc.body &&\n parent != doc.documentElement &&\n parentComputedStyle.overflow != 'visible') {\n parentRect = getBoundingClientRect(parent);\n }\n }\n\n // If either of the above conditionals set a new parentRect,\n // calculate new intersection data.\n if (parentRect) {\n intersectionRect = computeRectIntersection(parentRect, intersectionRect);\n }\n if (!intersectionRect) break;\n parent = parent && getParentNode(parent);\n }\n return intersectionRect;\n};\n\n\n/**\n * Returns the root rect after being expanded by the rootMargin value.\n * @return {ClientRect} The expanded root rect.\n * @private\n */\nIntersectionObserver.prototype._getRootRect = function() {\n var rootRect;\n if (this.root && !isDoc(this.root)) {\n rootRect = getBoundingClientRect(this.root);\n } else {\n // Use / instead of window since scroll bars affect size.\n var doc = isDoc(this.root) ? this.root : document;\n var html = doc.documentElement;\n var body = doc.body;\n rootRect = {\n top: 0,\n left: 0,\n right: html.clientWidth || body.clientWidth,\n width: html.clientWidth || body.clientWidth,\n bottom: html.clientHeight || body.clientHeight,\n height: html.clientHeight || body.clientHeight\n };\n }\n return this._expandRectByRootMargin(rootRect);\n};\n\n\n/**\n * Accepts a rect and expands it by the rootMargin value.\n * @param {DOMRect|ClientRect} rect The rect object to expand.\n * @return {ClientRect} The expanded rect.\n * @private\n */\nIntersectionObserver.prototype._expandRectByRootMargin = function(rect) {\n var margins = this._rootMarginValues.map(function(margin, i) {\n return margin.unit == 'px' ? margin.value :\n margin.value * (i % 2 ? rect.width : rect.height) / 100;\n });\n var newRect = {\n top: rect.top - margins[0],\n right: rect.right + margins[1],\n bottom: rect.bottom + margins[2],\n left: rect.left - margins[3]\n };\n newRect.width = newRect.right - newRect.left;\n newRect.height = newRect.bottom - newRect.top;\n\n return newRect;\n};\n\n\n/**\n * Accepts an old and new entry and returns true if at least one of the\n * threshold values has been crossed.\n * @param {?IntersectionObserverEntry} oldEntry The previous entry for a\n * particular target element or null if no previous entry exists.\n * @param {IntersectionObserverEntry} newEntry The current entry for a\n * particular target element.\n * @return {boolean} Returns true if a any threshold has been crossed.\n * @private\n */\nIntersectionObserver.prototype._hasCrossedThreshold =\n function(oldEntry, newEntry) {\n\n // To make comparing easier, an entry that has a ratio of 0\n // but does not actually intersect is given a value of -1\n var oldRatio = oldEntry && oldEntry.isIntersecting ?\n oldEntry.intersectionRatio || 0 : -1;\n var newRatio = newEntry.isIntersecting ?\n newEntry.intersectionRatio || 0 : -1;\n\n // Ignore unchanged ratios\n if (oldRatio === newRatio) return;\n\n for (var i = 0; i < this.thresholds.length; i++) {\n var threshold = this.thresholds[i];\n\n // Return true if an entry matches a threshold or if the new ratio\n // and the old ratio are on the opposite sides of a threshold.\n if (threshold == oldRatio || threshold == newRatio ||\n threshold < oldRatio !== threshold < newRatio) {\n return true;\n }\n }\n};\n\n\n/**\n * Returns whether or not the root element is an element and is in the DOM.\n * @return {boolean} True if the root element is an element and is in the DOM.\n * @private\n */\nIntersectionObserver.prototype._rootIsInDom = function() {\n return !this.root || containsDeep(document, this.root);\n};\n\n\n/**\n * Returns whether or not the target element is a child of root.\n * @param {Element} target The target element to check.\n * @return {boolean} True if the target element is a child of root.\n * @private\n */\nIntersectionObserver.prototype._rootContainsTarget = function(target) {\n var rootDoc =\n (this.root && (this.root.ownerDocument || this.root)) || document;\n return (\n containsDeep(rootDoc, target) &&\n (!this.root || rootDoc == target.ownerDocument)\n );\n};\n\n\n/**\n * Adds the instance to the global IntersectionObserver registry if it isn't\n * already present.\n * @private\n */\nIntersectionObserver.prototype._registerInstance = function() {\n if (registry.indexOf(this) < 0) {\n registry.push(this);\n }\n};\n\n\n/**\n * Removes the instance from the global IntersectionObserver registry.\n * @private\n */\nIntersectionObserver.prototype._unregisterInstance = function() {\n var index = registry.indexOf(this);\n if (index != -1) registry.splice(index, 1);\n};\n\n\n/**\n * Returns the result of the performance.now() method or null in browsers\n * that don't support the API.\n * @return {number} The elapsed time since the page was requested.\n */\nfunction now() {\n return window.performance && performance.now && performance.now();\n}\n\n\n/**\n * Throttles a function and delays its execution, so it's only called at most\n * once within a given time period.\n * @param {Function} fn The function to throttle.\n * @param {number} timeout The amount of time that must pass before the\n * function can be called again.\n * @return {Function} The throttled function.\n */\nfunction throttle(fn, timeout) {\n var timer = null;\n return function () {\n if (!timer) {\n timer = setTimeout(function() {\n fn();\n timer = null;\n }, timeout);\n }\n };\n}\n\n\n/**\n * Adds an event handler to a DOM node ensuring cross-browser compatibility.\n * @param {Node} node The DOM node to add the event handler to.\n * @param {string} event The event name.\n * @param {Function} fn The event handler to add.\n * @param {boolean} opt_useCapture Optionally adds the even to the capture\n * phase. Note: this only works in modern browsers.\n */\nfunction addEvent(node, event, fn, opt_useCapture) {\n if (typeof node.addEventListener == 'function') {\n node.addEventListener(event, fn, opt_useCapture || false);\n }\n else if (typeof node.attachEvent == 'function') {\n node.attachEvent('on' + event, fn);\n }\n}\n\n\n/**\n * Removes a previously added event handler from a DOM node.\n * @param {Node} node The DOM node to remove the event handler from.\n * @param {string} event The event name.\n * @param {Function} fn The event handler to remove.\n * @param {boolean} opt_useCapture If the event handler was added with this\n * flag set to true, it should be set to true here in order to remove it.\n */\nfunction removeEvent(node, event, fn, opt_useCapture) {\n if (typeof node.removeEventListener == 'function') {\n node.removeEventListener(event, fn, opt_useCapture || false);\n }\n else if (typeof node.detachEvent == 'function') {\n node.detachEvent('on' + event, fn);\n }\n}\n\n\n/**\n * Returns the intersection between two rect objects.\n * @param {Object} rect1 The first rect.\n * @param {Object} rect2 The second rect.\n * @return {?Object|?ClientRect} The intersection rect or undefined if no\n * intersection is found.\n */\nfunction computeRectIntersection(rect1, rect2) {\n var top = Math.max(rect1.top, rect2.top);\n var bottom = Math.min(rect1.bottom, rect2.bottom);\n var left = Math.max(rect1.left, rect2.left);\n var right = Math.min(rect1.right, rect2.right);\n var width = right - left;\n var height = bottom - top;\n\n return (width >= 0 && height >= 0) && {\n top: top,\n bottom: bottom,\n left: left,\n right: right,\n width: width,\n height: height\n } || null;\n}\n\n\n/**\n * Shims the native getBoundingClientRect for compatibility with older IE.\n * @param {Element} el The element whose bounding rect to get.\n * @return {DOMRect|ClientRect} The (possibly shimmed) rect of the element.\n */\nfunction getBoundingClientRect(el) {\n var rect;\n\n try {\n rect = el.getBoundingClientRect();\n } catch (err) {\n // Ignore Windows 7 IE11 \"Unspecified error\"\n // https://github.com/w3c/IntersectionObserver/pull/205\n }\n\n if (!rect) return getEmptyRect();\n\n // Older IE\n if (!(rect.width && rect.height)) {\n rect = {\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.right - rect.left,\n height: rect.bottom - rect.top\n };\n }\n return rect;\n}\n\n\n/**\n * Returns an empty rect object. An empty rect is returned when an element\n * is not in the DOM.\n * @return {ClientRect} The empty rect.\n */\nfunction getEmptyRect() {\n return {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0,\n width: 0,\n height: 0\n };\n}\n\n\n/**\n * Ensure that the result has all of the necessary fields of the DOMRect.\n * Specifically this ensures that `x` and `y` fields are set.\n *\n * @param {?DOMRect|?ClientRect} rect\n * @return {?DOMRect}\n */\nfunction ensureDOMRect(rect) {\n // A `DOMRect` object has `x` and `y` fields.\n if (!rect || 'x' in rect) {\n return rect;\n }\n // A IE's `ClientRect` type does not have `x` and `y`. The same is the case\n // for internally calculated Rect objects. For the purposes of\n // `IntersectionObserver`, it's sufficient to simply mirror `left` and `top`\n // for these fields.\n return {\n top: rect.top,\n y: rect.top,\n bottom: rect.bottom,\n left: rect.left,\n x: rect.left,\n right: rect.right,\n width: rect.width,\n height: rect.height\n };\n}\n\n\n/**\n * Inverts the intersection and bounding rect from the parent (frame) BCR to\n * the local BCR space.\n * @param {DOMRect|ClientRect} parentBoundingRect The parent's bound client rect.\n * @param {DOMRect|ClientRect} parentIntersectionRect The parent's own intersection rect.\n * @return {ClientRect} The local root bounding rect for the parent's children.\n */\nfunction convertFromParentRect(parentBoundingRect, parentIntersectionRect) {\n var top = parentIntersectionRect.top - parentBoundingRect.top;\n var left = parentIntersectionRect.left - parentBoundingRect.left;\n return {\n top: top,\n left: left,\n height: parentIntersectionRect.height,\n width: parentIntersectionRect.width,\n bottom: top + parentIntersectionRect.height,\n right: left + parentIntersectionRect.width\n };\n}\n\n\n/**\n * Checks to see if a parent element contains a child element (including inside\n * shadow DOM).\n * @param {Node} parent The parent element.\n * @param {Node} child The child element.\n * @return {boolean} True if the parent node contains the child node.\n */\nfunction containsDeep(parent, child) {\n var node = child;\n while (node) {\n if (node == parent) return true;\n\n node = getParentNode(node);\n }\n return false;\n}\n\n\n/**\n * Gets the parent node of an element or its host element if the parent node\n * is a shadow root.\n * @param {Node} node The node whose parent to get.\n * @return {Node|null} The parent node or null if no parent exists.\n */\nfunction getParentNode(node) {\n var parent = node.parentNode;\n\n if (node.nodeType == /* DOCUMENT */ 9 && node != document) {\n // If this node is a document node, look for the embedding frame.\n return getFrameElement(node);\n }\n\n // If the parent has element that is assigned through shadow root slot\n if (parent && parent.assignedSlot) {\n parent = parent.assignedSlot.parentNode\n }\n\n if (parent && parent.nodeType == 11 && parent.host) {\n // If the parent is a shadow root, return the host element.\n return parent.host;\n }\n\n return parent;\n}\n\n/**\n * Returns true if `node` is a Document.\n * @param {!Node} node\n * @returns {boolean}\n */\nfunction isDoc(node) {\n return node && node.nodeType === 9;\n}\n\n\n// Exposes the constructors globally.\nwindow.IntersectionObserver = IntersectionObserver;\nwindow.IntersectionObserverEntry = IntersectionObserverEntry;\n\n}());\n", "/*\nUnobtrusive JavaScript\nhttps://github.com/rails/rails/blob/main/actionview/app/javascript\nReleased under the MIT license\n */\nconst linkClickSelector = \"a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]\";\n\nconst buttonClickSelector = {\n selector: \"button[data-remote]:not([form]), button[data-confirm]:not([form])\",\n exclude: \"form button\"\n};\n\nconst inputChangeSelector = \"select[data-remote], input[data-remote], textarea[data-remote]\";\n\nconst formSubmitSelector = \"form:not([data-turbo=true])\";\n\nconst formInputClickSelector = \"form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])\";\n\nconst formDisableSelector = \"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled\";\n\nconst formEnableSelector = \"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled\";\n\nconst fileInputSelector = \"input[name][type=file]:not([disabled])\";\n\nconst linkDisableSelector = \"a[data-disable-with], a[data-disable]\";\n\nconst buttonDisableSelector = \"button[data-remote][data-disable-with], button[data-remote][data-disable]\";\n\nlet nonce = null;\n\nconst loadCSPNonce = () => {\n const metaTag = document.querySelector(\"meta[name=csp-nonce]\");\n return nonce = metaTag && metaTag.content;\n};\n\nconst cspNonce = () => nonce || loadCSPNonce();\n\nconst m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector;\n\nconst matches = function(element, selector) {\n if (selector.exclude) {\n return m.call(element, selector.selector) && !m.call(element, selector.exclude);\n } else {\n return m.call(element, selector);\n }\n};\n\nconst EXPANDO = \"_ujsData\";\n\nconst getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined;\n\nconst setData = function(element, key, value) {\n if (!element[EXPANDO]) {\n element[EXPANDO] = {};\n }\n return element[EXPANDO][key] = value;\n};\n\nconst $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector));\n\nconst isContentEditable = function(element) {\n var isEditable = false;\n do {\n if (element.isContentEditable) {\n isEditable = true;\n break;\n }\n element = element.parentElement;\n } while (element);\n return isEditable;\n};\n\nconst csrfToken = () => {\n const meta = document.querySelector(\"meta[name=csrf-token]\");\n return meta && meta.content;\n};\n\nconst csrfParam = () => {\n const meta = document.querySelector(\"meta[name=csrf-param]\");\n return meta && meta.content;\n};\n\nconst CSRFProtection = xhr => {\n const token = csrfToken();\n if (token) {\n return xhr.setRequestHeader(\"X-CSRF-Token\", token);\n }\n};\n\nconst refreshCSRFTokens = () => {\n const token = csrfToken();\n const param = csrfParam();\n if (token && param) {\n return $('form input[name=\"' + param + '\"]').forEach((input => input.value = token));\n }\n};\n\nconst AcceptHeaders = {\n \"*\": \"*/*\",\n text: \"text/plain\",\n html: \"text/html\",\n xml: \"application/xml, text/xml\",\n json: \"application/json, text/javascript\",\n script: \"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"\n};\n\nconst ajax = options => {\n options = prepareOptions(options);\n var xhr = createXHR(options, (function() {\n const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader(\"Content-Type\"));\n if (Math.floor(xhr.status / 100) === 2) {\n if (typeof options.success === \"function\") {\n options.success(response, xhr.statusText, xhr);\n }\n } else {\n if (typeof options.error === \"function\") {\n options.error(response, xhr.statusText, xhr);\n }\n }\n return typeof options.complete === \"function\" ? options.complete(xhr, xhr.statusText) : undefined;\n }));\n if (options.beforeSend && !options.beforeSend(xhr, options)) {\n return false;\n }\n if (xhr.readyState === XMLHttpRequest.OPENED) {\n return xhr.send(options.data);\n }\n};\n\nvar prepareOptions = function(options) {\n options.url = options.url || location.href;\n options.type = options.type.toUpperCase();\n if (options.type === \"GET\" && options.data) {\n if (options.url.indexOf(\"?\") < 0) {\n options.url += \"?\" + options.data;\n } else {\n options.url += \"&\" + options.data;\n }\n }\n if (!(options.dataType in AcceptHeaders)) {\n options.dataType = \"*\";\n }\n options.accept = AcceptHeaders[options.dataType];\n if (options.dataType !== \"*\") {\n options.accept += \", */*; q=0.01\";\n }\n return options;\n};\n\nvar createXHR = function(options, done) {\n const xhr = new XMLHttpRequest;\n xhr.open(options.type, options.url, true);\n xhr.setRequestHeader(\"Accept\", options.accept);\n if (typeof options.data === \"string\") {\n xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded; charset=UTF-8\");\n }\n if (!options.crossDomain) {\n xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n CSRFProtection(xhr);\n }\n xhr.withCredentials = !!options.withCredentials;\n xhr.onreadystatechange = function() {\n if (xhr.readyState === XMLHttpRequest.DONE) {\n return done(xhr);\n }\n };\n return xhr;\n};\n\nvar processResponse = function(response, type) {\n if (typeof response === \"string\" && typeof type === \"string\") {\n if (type.match(/\\bjson\\b/)) {\n try {\n response = JSON.parse(response);\n } catch (error) {}\n } else if (type.match(/\\b(?:java|ecma)script\\b/)) {\n const script = document.createElement(\"script\");\n script.setAttribute(\"nonce\", cspNonce());\n script.text = response;\n document.head.appendChild(script).parentNode.removeChild(script);\n } else if (type.match(/\\b(xml|html|svg)\\b/)) {\n const parser = new DOMParser;\n type = type.replace(/;.+/, \"\");\n try {\n response = parser.parseFromString(response, type);\n } catch (error1) {}\n }\n }\n return response;\n};\n\nconst href = element => element.href;\n\nconst isCrossDomain = function(url) {\n const originAnchor = document.createElement(\"a\");\n originAnchor.href = location.href;\n const urlAnchor = document.createElement(\"a\");\n try {\n urlAnchor.href = url;\n return !((!urlAnchor.protocol || urlAnchor.protocol === \":\") && !urlAnchor.host || originAnchor.protocol + \"//\" + originAnchor.host === urlAnchor.protocol + \"//\" + urlAnchor.host);\n } catch (e) {\n return true;\n }\n};\n\nlet preventDefault;\n\nlet {CustomEvent: CustomEvent} = window;\n\nif (typeof CustomEvent !== \"function\") {\n CustomEvent = function(event, params) {\n const evt = document.createEvent(\"CustomEvent\");\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n };\n CustomEvent.prototype = window.Event.prototype;\n ({preventDefault: preventDefault} = CustomEvent.prototype);\n CustomEvent.prototype.preventDefault = function() {\n const result = preventDefault.call(this);\n if (this.cancelable && !this.defaultPrevented) {\n Object.defineProperty(this, \"defaultPrevented\", {\n get() {\n return true;\n }\n });\n }\n return result;\n };\n}\n\nconst fire = (obj, name, data) => {\n const event = new CustomEvent(name, {\n bubbles: true,\n cancelable: true,\n detail: data\n });\n obj.dispatchEvent(event);\n return !event.defaultPrevented;\n};\n\nconst stopEverything = e => {\n fire(e.target, \"ujs:everythingStopped\");\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n};\n\nconst delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, (function(e) {\n let {target: target} = e;\n while (!!(target instanceof Element) && !matches(target, selector)) {\n target = target.parentNode;\n }\n if (target instanceof Element && handler.call(target, e) === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n}));\n\nconst toArray = e => Array.prototype.slice.call(e);\n\nconst serializeElement = (element, additionalParam) => {\n let inputs = [ element ];\n if (matches(element, \"form\")) {\n inputs = toArray(element.elements);\n }\n const params = [];\n inputs.forEach((function(input) {\n if (!input.name || input.disabled) {\n return;\n }\n if (matches(input, \"fieldset[disabled] *\")) {\n return;\n }\n if (matches(input, \"select\")) {\n toArray(input.options).forEach((function(option) {\n if (option.selected) {\n params.push({\n name: input.name,\n value: option.value\n });\n }\n }));\n } else if (input.checked || [ \"radio\", \"checkbox\", \"submit\" ].indexOf(input.type) === -1) {\n params.push({\n name: input.name,\n value: input.value\n });\n }\n }));\n if (additionalParam) {\n params.push(additionalParam);\n }\n return params.map((function(param) {\n if (param.name) {\n return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`;\n } else {\n return param;\n }\n })).join(\"&\");\n};\n\nconst formElements = (form, selector) => {\n if (matches(form, \"form\")) {\n return toArray(form.elements).filter((el => matches(el, selector)));\n } else {\n return toArray(form.querySelectorAll(selector));\n }\n};\n\nconst handleConfirmWithRails = rails => function(e) {\n if (!allowAction(this, rails)) {\n stopEverything(e);\n }\n};\n\nconst confirm = (message, element) => window.confirm(message);\n\nvar allowAction = function(element, rails) {\n let callback;\n const message = element.getAttribute(\"data-confirm\");\n if (!message) {\n return true;\n }\n let answer = false;\n if (fire(element, \"confirm\")) {\n try {\n answer = rails.confirm(message, element);\n } catch (error) {}\n callback = fire(element, \"confirm:complete\", [ answer ]);\n }\n return answer && callback;\n};\n\nconst handleDisabledElement = function(e) {\n const element = this;\n if (element.disabled) {\n stopEverything(e);\n }\n};\n\nconst enableElement = e => {\n let element;\n if (e instanceof Event) {\n if (isXhrRedirect(e)) {\n return;\n }\n element = e.target;\n } else {\n element = e;\n }\n if (isContentEditable(element)) {\n return;\n }\n if (matches(element, linkDisableSelector)) {\n return enableLinkElement(element);\n } else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {\n return enableFormElement(element);\n } else if (matches(element, formSubmitSelector)) {\n return enableFormElements(element);\n }\n};\n\nconst disableElement = e => {\n const element = e instanceof Event ? e.target : e;\n if (isContentEditable(element)) {\n return;\n }\n if (matches(element, linkDisableSelector)) {\n return disableLinkElement(element);\n } else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {\n return disableFormElement(element);\n } else if (matches(element, formSubmitSelector)) {\n return disableFormElements(element);\n }\n};\n\nvar disableLinkElement = function(element) {\n if (getData(element, \"ujs:disabled\")) {\n return;\n }\n const replacement = element.getAttribute(\"data-disable-with\");\n if (replacement != null) {\n setData(element, \"ujs:enable-with\", element.innerHTML);\n element.innerHTML = replacement;\n }\n element.addEventListener(\"click\", stopEverything);\n return setData(element, \"ujs:disabled\", true);\n};\n\nvar enableLinkElement = function(element) {\n const originalText = getData(element, \"ujs:enable-with\");\n if (originalText != null) {\n element.innerHTML = originalText;\n setData(element, \"ujs:enable-with\", null);\n }\n element.removeEventListener(\"click\", stopEverything);\n return setData(element, \"ujs:disabled\", null);\n};\n\nvar disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement);\n\nvar disableFormElement = function(element) {\n if (getData(element, \"ujs:disabled\")) {\n return;\n }\n const replacement = element.getAttribute(\"data-disable-with\");\n if (replacement != null) {\n if (matches(element, \"button\")) {\n setData(element, \"ujs:enable-with\", element.innerHTML);\n element.innerHTML = replacement;\n } else {\n setData(element, \"ujs:enable-with\", element.value);\n element.value = replacement;\n }\n }\n element.disabled = true;\n return setData(element, \"ujs:disabled\", true);\n};\n\nvar enableFormElements = form => formElements(form, formEnableSelector).forEach((element => enableFormElement(element)));\n\nvar enableFormElement = function(element) {\n const originalText = getData(element, \"ujs:enable-with\");\n if (originalText != null) {\n if (matches(element, \"button\")) {\n element.innerHTML = originalText;\n } else {\n element.value = originalText;\n }\n setData(element, \"ujs:enable-with\", null);\n }\n element.disabled = false;\n return setData(element, \"ujs:disabled\", null);\n};\n\nvar isXhrRedirect = function(event) {\n const xhr = event.detail ? event.detail[0] : undefined;\n return xhr && xhr.getResponseHeader(\"X-Xhr-Redirect\");\n};\n\nconst handleMethodWithRails = rails => function(e) {\n const link = this;\n const method = link.getAttribute(\"data-method\");\n if (!method) {\n return;\n }\n if (isContentEditable(this)) {\n return;\n }\n const href = rails.href(link);\n const csrfToken$1 = csrfToken();\n const csrfParam$1 = csrfParam();\n const form = document.createElement(\"form\");\n let formContent = ``;\n if (csrfParam$1 && csrfToken$1 && !isCrossDomain(href)) {\n formContent += ``;\n }\n formContent += '';\n form.method = \"post\";\n form.action = href;\n form.target = link.target;\n form.innerHTML = formContent;\n form.style.display = \"none\";\n document.body.appendChild(form);\n form.querySelector('[type=\"submit\"]').click();\n stopEverything(e);\n};\n\nconst isRemote = function(element) {\n const value = element.getAttribute(\"data-remote\");\n return value != null && value !== \"false\";\n};\n\nconst handleRemoteWithRails = rails => function(e) {\n let data, method, url;\n const element = this;\n if (!isRemote(element)) {\n return true;\n }\n if (!fire(element, \"ajax:before\")) {\n fire(element, \"ajax:stopped\");\n return false;\n }\n if (isContentEditable(element)) {\n fire(element, \"ajax:stopped\");\n return false;\n }\n const withCredentials = element.getAttribute(\"data-with-credentials\");\n const dataType = element.getAttribute(\"data-type\") || \"script\";\n if (matches(element, formSubmitSelector)) {\n const button = getData(element, \"ujs:submit-button\");\n method = getData(element, \"ujs:submit-button-formmethod\") || element.getAttribute(\"method\") || \"get\";\n url = getData(element, \"ujs:submit-button-formaction\") || element.getAttribute(\"action\") || location.href;\n if (method.toUpperCase() === \"GET\") {\n url = url.replace(/\\?.*$/, \"\");\n }\n if (element.enctype === \"multipart/form-data\") {\n data = new FormData(element);\n if (button != null) {\n data.append(button.name, button.value);\n }\n } else {\n data = serializeElement(element, button);\n }\n setData(element, \"ujs:submit-button\", null);\n setData(element, \"ujs:submit-button-formmethod\", null);\n setData(element, \"ujs:submit-button-formaction\", null);\n } else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {\n method = element.getAttribute(\"data-method\");\n url = element.getAttribute(\"data-url\");\n data = serializeElement(element, element.getAttribute(\"data-params\"));\n } else {\n method = element.getAttribute(\"data-method\");\n url = rails.href(element);\n data = element.getAttribute(\"data-params\");\n }\n ajax({\n type: method || \"GET\",\n url: url,\n data: data,\n dataType: dataType,\n beforeSend(xhr, options) {\n if (fire(element, \"ajax:beforeSend\", [ xhr, options ])) {\n return fire(element, \"ajax:send\", [ xhr ]);\n } else {\n fire(element, \"ajax:stopped\");\n return false;\n }\n },\n success(...args) {\n return fire(element, \"ajax:success\", args);\n },\n error(...args) {\n return fire(element, \"ajax:error\", args);\n },\n complete(...args) {\n return fire(element, \"ajax:complete\", args);\n },\n crossDomain: isCrossDomain(url),\n withCredentials: withCredentials != null && withCredentials !== \"false\"\n });\n stopEverything(e);\n};\n\nconst formSubmitButtonClick = function(e) {\n const button = this;\n const {form: form} = button;\n if (!form) {\n return;\n }\n if (button.name) {\n setData(form, \"ujs:submit-button\", {\n name: button.name,\n value: button.value\n });\n }\n setData(form, \"ujs:formnovalidate-button\", button.formNoValidate);\n setData(form, \"ujs:submit-button-formaction\", button.getAttribute(\"formaction\"));\n return setData(form, \"ujs:submit-button-formmethod\", button.getAttribute(\"formmethod\"));\n};\n\nconst preventInsignificantClick = function(e) {\n const link = this;\n const method = (link.getAttribute(\"data-method\") || \"GET\").toUpperCase();\n const data = link.getAttribute(\"data-params\");\n const metaClick = e.metaKey || e.ctrlKey;\n const insignificantMetaClick = metaClick && method === \"GET\" && !data;\n const nonPrimaryMouseClick = e.button != null && e.button !== 0;\n if (nonPrimaryMouseClick || insignificantMetaClick) {\n e.stopImmediatePropagation();\n }\n};\n\nconst Rails = {\n $: $,\n ajax: ajax,\n buttonClickSelector: buttonClickSelector,\n buttonDisableSelector: buttonDisableSelector,\n confirm: confirm,\n cspNonce: cspNonce,\n csrfToken: csrfToken,\n csrfParam: csrfParam,\n CSRFProtection: CSRFProtection,\n delegate: delegate,\n disableElement: disableElement,\n enableElement: enableElement,\n fileInputSelector: fileInputSelector,\n fire: fire,\n formElements: formElements,\n formEnableSelector: formEnableSelector,\n formDisableSelector: formDisableSelector,\n formInputClickSelector: formInputClickSelector,\n formSubmitButtonClick: formSubmitButtonClick,\n formSubmitSelector: formSubmitSelector,\n getData: getData,\n handleDisabledElement: handleDisabledElement,\n href: href,\n inputChangeSelector: inputChangeSelector,\n isCrossDomain: isCrossDomain,\n linkClickSelector: linkClickSelector,\n linkDisableSelector: linkDisableSelector,\n loadCSPNonce: loadCSPNonce,\n matches: matches,\n preventInsignificantClick: preventInsignificantClick,\n refreshCSRFTokens: refreshCSRFTokens,\n serializeElement: serializeElement,\n setData: setData,\n stopEverything: stopEverything\n};\n\nconst handleConfirm = handleConfirmWithRails(Rails);\n\nRails.handleConfirm = handleConfirm;\n\nconst handleMethod = handleMethodWithRails(Rails);\n\nRails.handleMethod = handleMethod;\n\nconst handleRemote = handleRemoteWithRails(Rails);\n\nRails.handleRemote = handleRemote;\n\nconst start = function() {\n if (window._rails_loaded) {\n throw new Error(\"rails-ujs has already been loaded!\");\n }\n window.addEventListener(\"pageshow\", (function() {\n $(formEnableSelector).forEach((function(el) {\n if (getData(el, \"ujs:disabled\")) {\n enableElement(el);\n }\n }));\n $(linkDisableSelector).forEach((function(el) {\n if (getData(el, \"ujs:disabled\")) {\n enableElement(el);\n }\n }));\n }));\n delegate(document, linkDisableSelector, \"ajax:complete\", enableElement);\n delegate(document, linkDisableSelector, \"ajax:stopped\", enableElement);\n delegate(document, buttonDisableSelector, \"ajax:complete\", enableElement);\n delegate(document, buttonDisableSelector, \"ajax:stopped\", enableElement);\n delegate(document, linkClickSelector, \"click\", preventInsignificantClick);\n delegate(document, linkClickSelector, \"click\", handleDisabledElement);\n delegate(document, linkClickSelector, \"click\", handleConfirm);\n delegate(document, linkClickSelector, \"click\", disableElement);\n delegate(document, linkClickSelector, \"click\", handleRemote);\n delegate(document, linkClickSelector, \"click\", handleMethod);\n delegate(document, buttonClickSelector, \"click\", preventInsignificantClick);\n delegate(document, buttonClickSelector, \"click\", handleDisabledElement);\n delegate(document, buttonClickSelector, \"click\", handleConfirm);\n delegate(document, buttonClickSelector, \"click\", disableElement);\n delegate(document, buttonClickSelector, \"click\", handleRemote);\n delegate(document, inputChangeSelector, \"change\", handleDisabledElement);\n delegate(document, inputChangeSelector, \"change\", handleConfirm);\n delegate(document, inputChangeSelector, \"change\", handleRemote);\n delegate(document, formSubmitSelector, \"submit\", handleDisabledElement);\n delegate(document, formSubmitSelector, \"submit\", handleConfirm);\n delegate(document, formSubmitSelector, \"submit\", handleRemote);\n delegate(document, formSubmitSelector, \"submit\", (e => setTimeout((() => disableElement(e)), 13)));\n delegate(document, formSubmitSelector, \"ajax:send\", disableElement);\n delegate(document, formSubmitSelector, \"ajax:complete\", enableElement);\n delegate(document, formInputClickSelector, \"click\", preventInsignificantClick);\n delegate(document, formInputClickSelector, \"click\", handleDisabledElement);\n delegate(document, formInputClickSelector, \"click\", handleConfirm);\n delegate(document, formInputClickSelector, \"click\", formSubmitButtonClick);\n document.addEventListener(\"DOMContentLoaded\", refreshCSRFTokens);\n document.addEventListener(\"DOMContentLoaded\", loadCSPNonce);\n return window._rails_loaded = true;\n};\n\nRails.start = start;\n\nif (typeof jQuery !== \"undefined\" && jQuery && jQuery.ajax) {\n if (jQuery.rails) {\n throw new Error(\"If you load both jquery_ujs and rails-ujs, use rails-ujs only.\");\n }\n jQuery.rails = Rails;\n jQuery.ajaxPrefilter((function(options, originalOptions, xhr) {\n if (!options.crossDomain) {\n return CSRFProtection(xhr);\n }\n }));\n}\n\nexport { Rails as default };\n", "window.HTMLFontElement.prototype.replaceData = function replaceData (_0, _1, string) {\n this.parentNode.replaceChild(document.createTextNode(string), this)\n}\n", "/*! (c) Andrea Giammarchi @webreflection ISC */\n(function () {\n 'use strict';\n\n var attributesObserver = (function (whenDefined, MutationObserver) {\n var attributeChanged = function attributeChanged(records) {\n for (var i = 0, length = records.length; i < length; i++) dispatch(records[i]);\n };\n var dispatch = function dispatch(_ref) {\n var target = _ref.target,\n attributeName = _ref.attributeName,\n oldValue = _ref.oldValue;\n target.attributeChangedCallback(attributeName, oldValue, target.getAttribute(attributeName));\n };\n return function (target, is) {\n var attributeFilter = target.constructor.observedAttributes;\n if (attributeFilter) {\n whenDefined(is).then(function () {\n new MutationObserver(attributeChanged).observe(target, {\n attributes: true,\n attributeOldValue: true,\n attributeFilter: attributeFilter\n });\n for (var i = 0, length = attributeFilter.length; i < length; i++) {\n if (target.hasAttribute(attributeFilter[i])) dispatch({\n target: target,\n attributeName: attributeFilter[i],\n oldValue: null\n });\n }\n });\n }\n return target;\n };\n });\n\n function _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return _arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);\n }\n function _arrayLikeToArray(arr, len) {\n if (len == null || len > arr.length) len = arr.length;\n for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n return arr2;\n }\n function _createForOfIteratorHelper(o, allowArrayLike) {\n var it = typeof Symbol !== \"undefined\" && o[Symbol.iterator] || o[\"@@iterator\"];\n if (!it) {\n if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === \"number\") {\n if (it) o = it;\n var i = 0;\n var F = function () {};\n return {\n s: F,\n n: function () {\n if (i >= o.length) return {\n done: true\n };\n return {\n done: false,\n value: o[i++]\n };\n },\n e: function (e) {\n throw e;\n },\n f: F\n };\n }\n throw new TypeError(\"Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n }\n var normalCompletion = true,\n didErr = false,\n err;\n return {\n s: function () {\n it = it.call(o);\n },\n n: function () {\n var step = it.next();\n normalCompletion = step.done;\n return step;\n },\n e: function (e) {\n didErr = true;\n err = e;\n },\n f: function () {\n try {\n if (!normalCompletion && it.return != null) it.return();\n } finally {\n if (didErr) throw err;\n }\n }\n };\n }\n\n /*! (c) Andrea Giammarchi - ISC */\n var TRUE = true,\n FALSE = false,\n QSA$1 = 'querySelectorAll';\n\n /**\n * Start observing a generic document or root element.\n * @param {(node:Element, connected:boolean) => void} callback triggered per each dis/connected element\n * @param {Document|Element} [root=document] by default, the global document to observe\n * @param {Function} [MO=MutationObserver] by default, the global MutationObserver\n * @param {string[]} [query=['*']] the selectors to use within nodes\n * @returns {MutationObserver}\n */\n var notify = function notify(callback) {\n var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;\n var MO = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : MutationObserver;\n var query = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ['*'];\n var loop = function loop(nodes, selectors, added, removed, connected, pass) {\n var _iterator = _createForOfIteratorHelper(nodes),\n _step;\n try {\n for (_iterator.s(); !(_step = _iterator.n()).done;) {\n var node = _step.value;\n if (pass || QSA$1 in node) {\n if (connected) {\n if (!added.has(node)) {\n added.add(node);\n removed[\"delete\"](node);\n callback(node, connected);\n }\n } else if (!removed.has(node)) {\n removed.add(node);\n added[\"delete\"](node);\n callback(node, connected);\n }\n if (!pass) loop(node[QSA$1](selectors), selectors, added, removed, connected, TRUE);\n }\n }\n } catch (err) {\n _iterator.e(err);\n } finally {\n _iterator.f();\n }\n };\n var mo = new MO(function (records) {\n if (query.length) {\n var selectors = query.join(',');\n var added = new Set(),\n removed = new Set();\n var _iterator2 = _createForOfIteratorHelper(records),\n _step2;\n try {\n for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {\n var _step2$value = _step2.value,\n addedNodes = _step2$value.addedNodes,\n removedNodes = _step2$value.removedNodes;\n loop(removedNodes, selectors, added, removed, FALSE, FALSE);\n loop(addedNodes, selectors, added, removed, TRUE, FALSE);\n }\n } catch (err) {\n _iterator2.e(err);\n } finally {\n _iterator2.f();\n }\n }\n });\n var observe = mo.observe;\n (mo.observe = function (node) {\n return observe.call(mo, node, {\n subtree: TRUE,\n childList: TRUE\n });\n })(root);\n return mo;\n };\n\n var QSA = 'querySelectorAll';\n var _self$1 = self,\n document$2 = _self$1.document,\n Element$1 = _self$1.Element,\n MutationObserver$2 = _self$1.MutationObserver,\n Set$2 = _self$1.Set,\n WeakMap$1 = _self$1.WeakMap;\n var elements = function elements(element) {\n return QSA in element;\n };\n var filter = [].filter;\n var qsaObserver = (function (options) {\n var live = new WeakMap$1();\n var drop = function drop(elements) {\n for (var i = 0, length = elements.length; i < length; i++) live[\"delete\"](elements[i]);\n };\n var flush = function flush() {\n var records = observer.takeRecords();\n for (var i = 0, length = records.length; i < length; i++) {\n parse(filter.call(records[i].removedNodes, elements), false);\n parse(filter.call(records[i].addedNodes, elements), true);\n }\n };\n var matches = function matches(element) {\n return element.matches || element.webkitMatchesSelector || element.msMatchesSelector;\n };\n var notifier = function notifier(element, connected) {\n var selectors;\n if (connected) {\n for (var q, m = matches(element), i = 0, length = query.length; i < length; i++) {\n if (m.call(element, q = query[i])) {\n if (!live.has(element)) live.set(element, new Set$2());\n selectors = live.get(element);\n if (!selectors.has(q)) {\n selectors.add(q);\n options.handle(element, connected, q);\n }\n }\n }\n } else if (live.has(element)) {\n selectors = live.get(element);\n live[\"delete\"](element);\n selectors.forEach(function (q) {\n options.handle(element, connected, q);\n });\n }\n };\n var parse = function parse(elements) {\n var connected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n for (var i = 0, length = elements.length; i < length; i++) notifier(elements[i], connected);\n };\n var query = options.query;\n var root = options.root || document$2;\n var observer = notify(notifier, root, MutationObserver$2, query);\n var attachShadow = Element$1.prototype.attachShadow;\n if (attachShadow) Element$1.prototype.attachShadow = function (init) {\n var shadowRoot = attachShadow.call(this, init);\n observer.observe(shadowRoot);\n return shadowRoot;\n };\n if (query.length) parse(root[QSA](query));\n return {\n drop: drop,\n flush: flush,\n observer: observer,\n parse: parse\n };\n });\n\n var _self = self,\n document$1 = _self.document,\n Map = _self.Map,\n MutationObserver$1 = _self.MutationObserver,\n Object$1 = _self.Object,\n Set$1 = _self.Set,\n WeakMap = _self.WeakMap,\n Element = _self.Element,\n HTMLElement = _self.HTMLElement,\n Node = _self.Node,\n Error = _self.Error,\n TypeError$1 = _self.TypeError,\n Reflect = _self.Reflect;\n var defineProperty = Object$1.defineProperty,\n keys = Object$1.keys,\n getOwnPropertyNames = Object$1.getOwnPropertyNames,\n setPrototypeOf = Object$1.setPrototypeOf;\n var legacy = !self.customElements;\n var expando = function expando(element) {\n var key = keys(element);\n var value = [];\n var ignore = new Set$1();\n var length = key.length;\n for (var i = 0; i < length; i++) {\n value[i] = element[key[i]];\n try {\n delete element[key[i]];\n } catch (SafariTP) {\n ignore.add(i);\n }\n }\n return function () {\n for (var _i = 0; _i < length; _i++) ignore.has(_i) || (element[key[_i]] = value[_i]);\n };\n };\n if (legacy) {\n var HTMLBuiltIn = function HTMLBuiltIn() {\n var constructor = this.constructor;\n if (!classes.has(constructor)) throw new TypeError$1('Illegal constructor');\n var is = classes.get(constructor);\n if (override) return augment(override, is);\n var element = createElement.call(document$1, is);\n return augment(setPrototypeOf(element, constructor.prototype), is);\n };\n var createElement = document$1.createElement;\n var classes = new Map();\n var defined = new Map();\n var prototypes = new Map();\n var registry = new Map();\n var query = [];\n var handle = function handle(element, connected, selector) {\n var proto = prototypes.get(selector);\n if (connected && !proto.isPrototypeOf(element)) {\n var redefine = expando(element);\n override = setPrototypeOf(element, proto);\n try {\n new proto.constructor();\n } finally {\n override = null;\n redefine();\n }\n }\n var method = \"\".concat(connected ? '' : 'dis', \"connectedCallback\");\n if (method in proto) element[method]();\n };\n var _qsaObserver = qsaObserver({\n query: query,\n handle: handle\n }),\n parse = _qsaObserver.parse;\n var override = null;\n var whenDefined = function whenDefined(name) {\n if (!defined.has(name)) {\n var _,\n $ = new Promise(function ($) {\n _ = $;\n });\n defined.set(name, {\n $: $,\n _: _\n });\n }\n return defined.get(name).$;\n };\n var augment = attributesObserver(whenDefined, MutationObserver$1);\n self.customElements = {\n define: function define(is, Class) {\n if (registry.has(is)) throw new Error(\"the name \\\"\".concat(is, \"\\\" has already been used with this registry\"));\n classes.set(Class, is);\n prototypes.set(is, Class.prototype);\n registry.set(is, Class);\n query.push(is);\n whenDefined(is).then(function () {\n parse(document$1.querySelectorAll(is));\n });\n defined.get(is)._(Class);\n },\n get: function get(is) {\n return registry.get(is);\n },\n whenDefined: whenDefined\n };\n defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {\n value: HTMLBuiltIn\n });\n self.HTMLElement = HTMLBuiltIn;\n document$1.createElement = function (name, options) {\n var is = options && options.is;\n var Class = is ? registry.get(is) : registry.get(name);\n return Class ? new Class() : createElement.call(document$1, name);\n };\n // in case ShadowDOM is used through a polyfill, to avoid issues\n // with builtin extends within shadow roots\n if (!('isConnected' in Node.prototype)) defineProperty(Node.prototype, 'isConnected', {\n configurable: true,\n get: function get() {\n return !(this.ownerDocument.compareDocumentPosition(this) & this.DOCUMENT_POSITION_DISCONNECTED);\n }\n });\n } else {\n legacy = !self.customElements.get('extends-br');\n if (legacy) {\n try {\n var BR = function BR() {\n return self.Reflect.construct(HTMLBRElement, [], BR);\n };\n BR.prototype = HTMLLIElement.prototype;\n var is = 'extends-br';\n self.customElements.define('extends-br', BR, {\n 'extends': 'br'\n });\n legacy = document$1.createElement('br', {\n is: is\n }).outerHTML.indexOf(is) < 0;\n var _self$customElements = self.customElements,\n get = _self$customElements.get,\n _whenDefined = _self$customElements.whenDefined;\n self.customElements.whenDefined = function (is) {\n var _this = this;\n return _whenDefined.call(this, is).then(function (Class) {\n return Class || get.call(_this, is);\n });\n };\n } catch (o_O) {}\n }\n }\n if (legacy) {\n var _parseShadow = function _parseShadow(element) {\n var root = shadowRoots.get(element);\n _parse(root.querySelectorAll(this), element.isConnected);\n };\n var customElements = self.customElements;\n var _createElement = document$1.createElement;\n var define = customElements.define,\n _get = customElements.get,\n upgrade = customElements.upgrade;\n var _ref = Reflect || {\n construct: function construct(HTMLElement) {\n return HTMLElement.call(this);\n }\n },\n construct = _ref.construct;\n var shadowRoots = new WeakMap();\n var shadows = new Set$1();\n var _classes = new Map();\n var _defined = new Map();\n var _prototypes = new Map();\n var _registry = new Map();\n var shadowed = [];\n var _query = [];\n var getCE = function getCE(is) {\n return _registry.get(is) || _get.call(customElements, is);\n };\n var _handle = function _handle(element, connected, selector) {\n var proto = _prototypes.get(selector);\n if (connected && !proto.isPrototypeOf(element)) {\n var redefine = expando(element);\n _override = setPrototypeOf(element, proto);\n try {\n new proto.constructor();\n } finally {\n _override = null;\n redefine();\n }\n }\n var method = \"\".concat(connected ? '' : 'dis', \"connectedCallback\");\n if (method in proto) element[method]();\n };\n var _qsaObserver2 = qsaObserver({\n query: _query,\n handle: _handle\n }),\n _parse = _qsaObserver2.parse;\n var _qsaObserver3 = qsaObserver({\n query: shadowed,\n handle: function handle(element, connected) {\n if (shadowRoots.has(element)) {\n if (connected) shadows.add(element);else shadows[\"delete\"](element);\n if (_query.length) _parseShadow.call(_query, element);\n }\n }\n }),\n parseShadowed = _qsaObserver3.parse;\n // qsaObserver also patches attachShadow\n // be sure this runs *after* that\n var attachShadow = Element.prototype.attachShadow;\n if (attachShadow) Element.prototype.attachShadow = function (init) {\n var root = attachShadow.call(this, init);\n shadowRoots.set(this, root);\n return root;\n };\n var _whenDefined2 = function _whenDefined2(name) {\n if (!_defined.has(name)) {\n var _,\n $ = new Promise(function ($) {\n _ = $;\n });\n _defined.set(name, {\n $: $,\n _: _\n });\n }\n return _defined.get(name).$;\n };\n var _augment = attributesObserver(_whenDefined2, MutationObserver$1);\n var _override = null;\n getOwnPropertyNames(self).filter(function (k) {\n return /^HTML.*Element$/.test(k);\n }).forEach(function (k) {\n var HTMLElement = self[k];\n function HTMLBuiltIn() {\n var constructor = this.constructor;\n if (!_classes.has(constructor)) throw new TypeError$1('Illegal constructor');\n var _classes$get = _classes.get(constructor),\n is = _classes$get.is,\n tag = _classes$get.tag;\n if (is) {\n if (_override) return _augment(_override, is);\n var element = _createElement.call(document$1, tag);\n element.setAttribute('is', is);\n return _augment(setPrototypeOf(element, constructor.prototype), is);\n } else return construct.call(this, HTMLElement, [], constructor);\n }\n\n defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {\n value: HTMLBuiltIn\n });\n defineProperty(self, k, {\n value: HTMLBuiltIn\n });\n });\n document$1.createElement = function (name, options) {\n var is = options && options.is;\n if (is) {\n var Class = _registry.get(is);\n if (Class && _classes.get(Class).tag === name) return new Class();\n }\n var element = _createElement.call(document$1, name);\n if (is) element.setAttribute('is', is);\n return element;\n };\n customElements.get = getCE;\n customElements.whenDefined = _whenDefined2;\n customElements.upgrade = function (element) {\n var is = element.getAttribute('is');\n if (is) {\n var _constructor = _registry.get(is);\n if (_constructor) {\n _augment(setPrototypeOf(element, _constructor.prototype), is);\n // apparently unnecessary because this is handled by qsa observer\n // if (element.isConnected && element.connectedCallback)\n // element.connectedCallback();\n return;\n }\n }\n upgrade.call(customElements, element);\n };\n customElements.define = function (is, Class, options) {\n if (getCE(is)) throw new Error(\"'\".concat(is, \"' has already been defined as a custom element\"));\n var selector;\n var tag = options && options[\"extends\"];\n _classes.set(Class, tag ? {\n is: is,\n tag: tag\n } : {\n is: '',\n tag: is\n });\n if (tag) {\n selector = \"\".concat(tag, \"[is=\\\"\").concat(is, \"\\\"]\");\n _prototypes.set(selector, Class.prototype);\n _registry.set(is, Class);\n _query.push(selector);\n } else {\n define.apply(customElements, arguments);\n shadowed.push(selector = is);\n }\n _whenDefined2(is).then(function () {\n if (tag) {\n _parse(document$1.querySelectorAll(selector));\n shadows.forEach(_parseShadow, [selector]);\n } else parseShadowed(document$1.querySelectorAll(selector));\n });\n _defined.get(is)._(Class);\n };\n }\n\n})();\n", "function resetMenuState (sidemenuContent) {\n const primaryListItems = document.querySelectorAll('[data-main-nav-sidemenu-list-item]')\n const nestedListItems = document.querySelectorAll('[data-main-nav-sidemenu-nested-list-item]')\n\n primaryListItems.forEach(primaryListItem => {\n primaryListItem.classList.remove('main-nav__sidemenu-list-item--open')\n })\n\n nestedListItems.forEach(nestedListItem => {\n nestedListItem.classList.remove('main-nav__sidemenu-list-item-nested--open')\n })\n\n sidemenuContent.classList.remove('main-nav__sidemenu--nested-menu-open')\n}\n\nfunction headerSideMenuClickEvents (mainNav) {\n const backButton = mainNav.querySelector('[data-main-nav-sidemenu-back]')\n const sidemenuContent = mainNav.querySelector('[data-main-nav-sidemenu]')\n const primaryListItems = mainNav.querySelectorAll('[data-main-nav-sidemenu-list] [data-main-nav-sidemenu-list-item]')\n\n primaryListItems.forEach(primaryListItem => {\n const nestedList = primaryListItem.querySelector('[data-main-nav-sidemenu-nested-list]')\n\n if (nestedList) {\n primaryListItem.addEventListener('click', function handlePrimaryListItemClick () {\n this.classList.toggle('main-nav__sidemenu-list-item--open')\n })\n\n const nestedListItems = nestedList.querySelectorAll('[data-main-nav-sidemenu-nested-list-item]')\n nestedListItems.forEach(nestedListItem => {\n nestedListItem.addEventListener('click', function handleNestedListItemClick (e) {\n e.stopPropagation()\n\n if (this.firstElementChild.tagName === 'A') return\n\n mainNav.querySelector('[data-main-nav-sidemenu-back]').classList.add('main-nav__sidemenu-header-back--show')\n this.classList.toggle('main-nav__sidemenu-list-item-nested--open')\n sidemenuContent.classList.add('main-nav__sidemenu--nested-menu-open')\n })\n })\n\n const level2Lists = nestedList.querySelectorAll('[data-main-nav-sidemenu-nested-list-item-list]')\n level2Lists.forEach(level2List => {\n const level2ListItems = level2List.querySelectorAll('[data-main-nav-sidemenu-nested-list-item-list] li')\n level2ListItems.forEach(level2ListItem => {\n level2ListItem.addEventListener('click', (e) => {\n e.stopPropagation()\n })\n })\n })\n }\n })\n\n mainNav.querySelector('#main-menu-open').addEventListener('change', function handleSidemenuCloseButtonClick () {\n backButton.classList.remove('main-nav__sidemenu-header-back--show')\n resetMenuState(sidemenuContent)\n })\n\n backButton.addEventListener('click', function handleSidemenuBackButtonClick () {\n const openedNestedLists = mainNav.querySelectorAll('.main-nav__sidemenu-list-item-nested--open')\n const lastOpenedNestedList = openedNestedLists[openedNestedLists.length - 1]\n\n lastOpenedNestedList.classList.remove('main-nav__sidemenu-list-item-nested--open')\n if (openedNestedLists.length === 1) {\n backButton.classList.remove('main-nav__sidemenu-header-back--show')\n }\n sidemenuContent.classList.remove('main-nav__sidemenu--nested-menu-open')\n })\n}\n\nexport function setActiveMenuItem () {\n const mainMenuSectionEl = document.querySelector('#js-main-menu-section')\n if (mainMenuSectionEl) {\n const params = new URLSearchParams(window.location.search)\n if (params.has('tab')) {\n if (params.get('tab') === 'my-cars-tab-buying') {\n mainMenuSectionEl.setAttribute('data-section', 'buy')\n }\n if (params.get('tab') === 'my-cars-tab-selling') {\n mainMenuSectionEl.setAttribute('data-section', 'sell-my-car')\n }\n }\n\n const mainMenuSectionId = mainMenuSectionEl.getAttribute('data-section')\n const activeMenuItems = document.querySelectorAll(`[data-main-menu-section=\"${mainMenuSectionId}\"]`)\n activeMenuItems.forEach((item) => {\n item.classList.add('is-active')\n })\n }\n}\n\nexport function initHeader () {\n const mainNav = document.querySelector('[data-main-nav]')\n if (!mainNav) return\n\n headerSideMenuClickEvents(mainNav)\n setActiveMenuItem()\n}\n", "import { EventEmitter } from 'events'\n\nwindow.Carwow = window.Carwow || {}\nwindow.Carwow.eventBus = window.Carwow.eventBus || new EventEmitter()\nexport default window.Carwow.eventBus\n", "import eventBus from '../event_bus'\n\nconst observer = new window.IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n entry.target.intersected = entry.isIntersecting\n entry.target.tryFetchContent()\n })\n})\n\nclass CarwowRemoteContent extends window.HTMLElement {\n static get RETRYABLE_STATUS_CODES () {\n return [408, 503, 504, 599]\n }\n\n static get DELAYS () {\n return [250, 500, 1000, 2000, 4000]\n }\n\n static get MAX_RETRIES () {\n return this.DELAYS.length\n }\n\n static get observedAttributes () {\n return ['href', 'fetch']\n }\n\n get fetch () {\n return this.hasAttribute('fetch')\n }\n\n set fetch (val) {\n if (val) {\n this.setAttribute('fetch', '')\n } else {\n this.removeAttribute('fetch')\n }\n }\n\n get failed () {\n return this.hasAttribute('failed')\n }\n\n set failed (val) {\n if (val) {\n this.setAttribute('failed', '')\n } else {\n this.removeAttribute('failed')\n }\n }\n\n get link () {\n return this.getAttribute('href')\n }\n\n get id () {\n return this.getAttribute('data-remote-content-event-id')\n }\n\n tryFetchContent () {\n if (this.intersected && (this.fetch || this.isVisible())) {\n this.failed = false\n this.fetchContent()\n }\n }\n\n isVisible () {\n return window.getComputedStyle(this).visibility !== 'hidden'\n }\n\n observeRemoteContentViewed (elInView) {\n const viewContentEventEmitterName = this.id ? `remote_content:viewedContent:${this.id}` : 'remote_content:viewedContent'\n\n const remoteContentViewedObserver = new window.IntersectionObserver(entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n remoteContentViewedObserver.disconnect()\n eventBus.emit(viewContentEventEmitterName, this)\n }\n })\n }, { threshold: 0.5 })\n\n remoteContentViewedObserver.observe(elInView)\n }\n\n fetchContent (attempts = 0) {\n if (!this.fetched && this.link !== null) {\n this.fetched = true\n fetch(this.link, { credentials: 'same-origin' })\n .then(response => {\n if (response.ok) {\n return response.text()\n }\n const error = new Error(response.statusText)\n error.httpStatus = response.status\n throw error\n })\n .then(body => {\n const contentDisplayedEventEmitterName = this.id ? `remote_content:contentLoaded:${this.id}` : 'remote_content:contentLoaded'\n\n this.innerHTML = body\n eventBus.emit(contentDisplayedEventEmitterName, this)\n this.observeRemoteContentViewed(this)\n })\n .catch(error => {\n if (this.isRetryable(error.httpStatus)) {\n this.retry(attempts)\n }\n })\n }\n }\n\n isRetryable (status) {\n return this.constructor.RETRYABLE_STATUS_CODES.includes(status)\n }\n\n retry (attempts) {\n const currentRetry = attempts + 1\n if (currentRetry > this.constructor.MAX_RETRIES) {\n this.failed = true\n } else {\n const delay = this.constructor.DELAYS[attempts]\n setTimeout(() => {\n this.fetched = false\n this.fetchContent(currentRetry)\n }, delay)\n }\n }\n\n connectedCallback () {\n observer.observe(this)\n }\n\n disconnectedCallback () {\n observer.unobserve(this)\n this.intersected = false\n }\n\n attributeChangedCallback (name, oldValue, newValue) {\n if (name === 'href' && oldValue !== newValue) {\n this.fetched = false\n }\n\n this.tryFetchContent()\n }\n}\n\nfunction initRemoteContent () {\n if (window.customElements.get('carwow-remote-content')) { return }\n window.customElements.define('carwow-remote-content', CarwowRemoteContent)\n}\n\ninitRemoteContent()\n", "const LAZY_SVG_SPRITE_SELECTOR = '.lazy-svg-sprite'\n\nfunction loadLazySprite (sprite) {\n fetch(sprite.dataset.url)\n .then(response => response.text())\n .then(data => { sprite.innerHTML = data })\n}\n\nfunction loadLazySprites () {\n document.querySelectorAll(LAZY_SVG_SPRITE_SELECTOR).forEach((lazySprite) => {\n loadLazySprite(lazySprite)\n })\n}\n\nwindow.addEventListener('DOMContentLoaded', loadLazySprites)\n", "function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\n// Older browsers don't support event options, feature detect it.\n\n// Adopted and modified solution from Bohdan Didukh (2017)\n// https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi\n\nvar hasPassiveEvents = false;\nif (typeof window !== 'undefined') {\n var passiveTestOptions = {\n get passive() {\n hasPassiveEvents = true;\n return undefined;\n }\n };\n window.addEventListener('testPassive', null, passiveTestOptions);\n window.removeEventListener('testPassive', null, passiveTestOptions);\n}\n\nvar isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1);\n\n\nvar locks = [];\nvar documentListenerAdded = false;\nvar initialClientY = -1;\nvar previousBodyOverflowSetting = void 0;\nvar previousBodyPosition = void 0;\nvar previousBodyPaddingRight = void 0;\n\n// returns true if `el` should be allowed to receive touchmove events.\nvar allowTouchMove = function allowTouchMove(el) {\n return locks.some(function (lock) {\n if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) {\n return true;\n }\n\n return false;\n });\n};\n\nvar preventDefault = function preventDefault(rawEvent) {\n var e = rawEvent || window.event;\n\n // For the case whereby consumers adds a touchmove event listener to document.\n // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false })\n // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then\n // the touchmove event on document will break.\n if (allowTouchMove(e.target)) {\n return true;\n }\n\n // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom).\n if (e.touches.length > 1) return true;\n\n if (e.preventDefault) e.preventDefault();\n\n return false;\n};\n\nvar setOverflowHidden = function setOverflowHidden(options) {\n // If previousBodyPaddingRight is already set, don't set it again.\n if (previousBodyPaddingRight === undefined) {\n var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true;\n var scrollBarGap = window.innerWidth - document.documentElement.clientWidth;\n\n if (_reserveScrollBarGap && scrollBarGap > 0) {\n var computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10);\n previousBodyPaddingRight = document.body.style.paddingRight;\n document.body.style.paddingRight = computedBodyPaddingRight + scrollBarGap + 'px';\n }\n }\n\n // If previousBodyOverflowSetting is already set, don't set it again.\n if (previousBodyOverflowSetting === undefined) {\n previousBodyOverflowSetting = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n};\n\nvar restoreOverflowSetting = function restoreOverflowSetting() {\n if (previousBodyPaddingRight !== undefined) {\n document.body.style.paddingRight = previousBodyPaddingRight;\n\n // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it\n // can be set again.\n previousBodyPaddingRight = undefined;\n }\n\n if (previousBodyOverflowSetting !== undefined) {\n document.body.style.overflow = previousBodyOverflowSetting;\n\n // Restore previousBodyOverflowSetting to undefined\n // so setOverflowHidden knows it can be set again.\n previousBodyOverflowSetting = undefined;\n }\n};\n\nvar setPositionFixed = function setPositionFixed() {\n return window.requestAnimationFrame(function () {\n // If previousBodyPosition is already set, don't set it again.\n if (previousBodyPosition === undefined) {\n previousBodyPosition = {\n position: document.body.style.position,\n top: document.body.style.top,\n left: document.body.style.left\n };\n\n // Update the dom inside an animation frame \n var _window = window,\n scrollY = _window.scrollY,\n scrollX = _window.scrollX,\n innerHeight = _window.innerHeight;\n\n document.body.style.position = 'fixed';\n document.body.style.top = -scrollY;\n document.body.style.left = -scrollX;\n\n setTimeout(function () {\n return window.requestAnimationFrame(function () {\n // Attempt to check if the bottom bar appeared due to the position change\n var bottomBarHeight = innerHeight - window.innerHeight;\n if (bottomBarHeight && scrollY >= innerHeight) {\n // Move the content further up so that the bottom bar doesn't hide it\n document.body.style.top = -(scrollY + bottomBarHeight);\n }\n });\n }, 300);\n }\n });\n};\n\nvar restorePositionSetting = function restorePositionSetting() {\n if (previousBodyPosition !== undefined) {\n // Convert the position from \"px\" to Int\n var y = -parseInt(document.body.style.top, 10);\n var x = -parseInt(document.body.style.left, 10);\n\n // Restore styles\n document.body.style.position = previousBodyPosition.position;\n document.body.style.top = previousBodyPosition.top;\n document.body.style.left = previousBodyPosition.left;\n\n // Restore scroll\n window.scrollTo(x, y);\n\n previousBodyPosition = undefined;\n }\n};\n\n// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions\nvar isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) {\n return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false;\n};\n\nvar handleScroll = function handleScroll(event, targetElement) {\n var clientY = event.targetTouches[0].clientY - initialClientY;\n\n if (allowTouchMove(event.target)) {\n return false;\n }\n\n if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {\n // element is at the top of its scroll.\n return preventDefault(event);\n }\n\n if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) {\n // element is at the bottom of its scroll.\n return preventDefault(event);\n }\n\n event.stopPropagation();\n return true;\n};\n\nexport var disableBodyScroll = function disableBodyScroll(targetElement, options) {\n // targetElement must be provided\n if (!targetElement) {\n // eslint-disable-next-line no-console\n console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.');\n return;\n }\n\n // disableBodyScroll must not have been called on this targetElement before\n if (locks.some(function (lock) {\n return lock.targetElement === targetElement;\n })) {\n return;\n }\n\n var lock = {\n targetElement: targetElement,\n options: options || {}\n };\n\n locks = [].concat(_toConsumableArray(locks), [lock]);\n\n if (isIosDevice) {\n setPositionFixed();\n } else {\n setOverflowHidden(options);\n }\n\n if (isIosDevice) {\n targetElement.ontouchstart = function (event) {\n if (event.targetTouches.length === 1) {\n // detect single touch.\n initialClientY = event.targetTouches[0].clientY;\n }\n };\n targetElement.ontouchmove = function (event) {\n if (event.targetTouches.length === 1) {\n // detect single touch.\n handleScroll(event, targetElement);\n }\n };\n\n if (!documentListenerAdded) {\n document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined);\n documentListenerAdded = true;\n }\n }\n};\n\nexport var clearAllBodyScrollLocks = function clearAllBodyScrollLocks() {\n if (isIosDevice) {\n // Clear all locks ontouchstart/ontouchmove handlers, and the references.\n locks.forEach(function (lock) {\n lock.targetElement.ontouchstart = null;\n lock.targetElement.ontouchmove = null;\n });\n\n if (documentListenerAdded) {\n document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined);\n documentListenerAdded = false;\n }\n\n // Reset initial clientY.\n initialClientY = -1;\n }\n\n if (isIosDevice) {\n restorePositionSetting();\n } else {\n restoreOverflowSetting();\n }\n\n locks = [];\n};\n\nexport var enableBodyScroll = function enableBodyScroll(targetElement) {\n if (!targetElement) {\n // eslint-disable-next-line no-console\n console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.');\n return;\n }\n\n locks = locks.filter(function (lock) {\n return lock.targetElement !== targetElement;\n });\n\n if (isIosDevice) {\n targetElement.ontouchstart = null;\n targetElement.ontouchmove = null;\n\n if (documentListenerAdded && locks.length === 0) {\n document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined);\n documentListenerAdded = false;\n }\n }\n\n if (isIosDevice) {\n restorePositionSetting();\n } else {\n restoreOverflowSetting();\n }\n};\n\n", "import { disableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'\n\nfunction closeModalOnEscape (e, elementsToClose) {\n if (e.keyCode === 27) {\n elementsToClose.forEach((element) => {\n if (element.checked) {\n const thisModalRadioClose = element.parentNode.querySelector('[data-close-modal-input]')\n if (!thisModalRadioClose) return\n thisModalRadioClose.checked = true\n clearAllBodyScrollLocks()\n }\n })\n }\n}\n\nfunction handleCloseModalOnEscape () {\n const elementsToCloseOnEscape = document.querySelectorAll('[data-close-on-esc=\"true\"]')\n\n if (elementsToCloseOnEscape.length) {\n document.addEventListener('keyup', (e) => {\n closeModalOnEscape(e, elementsToCloseOnEscape)\n })\n }\n}\n\nfunction updateBodyScroll (e) {\n if (e.target && e.target.hasAttribute('data-modal-input')) {\n // select element which needs to be kept scrollable\n const thisModalBody = document.querySelector(`#${e.target.getAttribute('name')} [data-modal-content-body]`)\n\n if (e.target.hasAttribute('data-modal-body-no-scroll') && e.target.checked) {\n if (thisModalBody) {\n disableBodyScroll(thisModalBody)\n }\n }\n\n if (e.target.hasAttribute('data-close-modal-input') && e.target.checked) {\n clearAllBodyScrollLocks()\n }\n }\n}\n\nfunction resetModalScroll (e) {\n if (e.target && e.target.hasAttribute('data-modal-reset-scroll')) {\n const thisModalBody = document.querySelector(`#${e.target.getAttribute('name')} [data-modal-content-body]`)\n if (thisModalBody) {\n thisModalBody.scrollTop = 0\n }\n }\n}\n\nfunction closeAllModals (e) {\n if (e.target && e.target.hasAttribute('data-modal-close-all')) {\n document.querySelectorAll('[data-close-modal-input]').forEach((element) => {\n if (element.getAttribute('name') !== e.target.getAttribute('name')) {\n element.checked = true\n }\n })\n }\n}\n\n// selector of a wrapping element containing both radio inputs and modal\nexport function openModal (selector) {\n const modal = document.querySelector(selector)\n if (!modal) return\n\n const modalInput = modal.querySelector('[data-open-modal-input]')\n if (modalInput) {\n modalInput.checked = true\n }\n\n const modalBody = modal.querySelector('[data-modal-content-body]')\n if (modalInput.hasAttribute('data-modal-body-no-scroll') && modalInput.checked) {\n if (modalBody) {\n disableBodyScroll(modalBody, { reserveScrollBarGap: true })\n }\n }\n}\n\nexport function closeModal (selector) {\n const modal = document.querySelector(selector)\n if (!modal) return\n const modalInput = modal.querySelector('[data-close-modal-input]')\n if (modalInput) {\n modalInput.checked = true\n }\n clearAllBodyScrollLocks()\n}\n\nfunction openModalOnLoad () {\n openModal('[data-open-on-load]')\n}\n\nfunction handleChangeEvent (e) {\n updateBodyScroll(e)\n resetModalScroll(e)\n closeAllModals(e)\n}\n\nexport function initModal () {\n handleCloseModalOnEscape()\n openModalOnLoad()\n\n document.addEventListener('change', handleChangeEvent)\n}\n", "import './elm_google_translate'\n\n// polyfill\nimport '@ungap/custom-elements'\n\n// Shared components for all pages\nimport { initHeader } from './components/header'\nimport './components/remote_content'\nimport './components/lazy_svg_sprite'\nimport { initModal as initOldModal } from './components/modal'\n\ndocument.addEventListener('DOMContentLoaded', () => {\n initOldModal()\n initHeader()\n})\n", "export function setItem (sKey, sValue, vEnd, sPath, sDomain, bSecure) {\n if (!sKey || /^(?:expires|max-age|path|domain|secure)$/i.test(sKey)) { return false }\n let sExpires = ''\n if (vEnd) {\n switch (vEnd.constructor) {\n case Number:\n sExpires = vEnd === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + vEnd\n break\n case String:\n sExpires = '; expires=' + vEnd\n break\n case Date:\n sExpires = '; expires=' + vEnd.toUTCString()\n break\n default:\n break\n }\n }\n document.cookie = encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue) + sExpires + (sDomain ? '; domain=' + sDomain : '') + (sPath ? '; path=' + sPath : '') + (bSecure ? '; secure' : '')\n return true\n}\n\nexport function hasItem (sKey) {\n return (new RegExp('(?:^|;\\\\s*)' + encodeURIComponent(sKey).replace(/[-.+*]/g, '\\\\$&') + '\\\\s*\\\\=')).test(document.cookie)\n}\n", "import { setItem } from '@carwow/carwow_theme/app/javascript/doc_cookies'\n\nfunction getParameterByName (name) {\n const searchName = name.replace(/[[]/, '\\\\[').replace(/[\\]]/, '\\\\]')\n const regex = new RegExp('[\\\\?&]' + searchName + '=([^&#]*)')\n const results = regex.exec(window.location.search)\n return results === null ? '' : decodeURIComponent(results[1].replace(/\\+/g, ' '))\n}\n\nconst domain = document.domain.replace(/^[a-zA-Z]+?\\./, '')\nconst utmMedium = getParameterByName('utm_medium')\nconst utmSource = getParameterByName('utm_source')\nconst utmCampaign = getParameterByName('utm_campaign')\nconst utmKeyword = getParameterByName('utm_keyword')\nconst utmGroup = getParameterByName('utm_group')\nconst gclid = getParameterByName('gclid')\nconst type = getParameterByName('Type')\nconst makeModel = getParameterByName('MakeModel').split('|')\nconst make = makeModel[0]\nconst model = makeModel[1]\nconst network = getParameterByName('network')\n\nconst threeHours = 3 * 60 * 60 * 1000\n\nif (utmMedium !== '') {\n setItem('user_utm_medium', utmMedium, threeHours, '/', domain)\n}\n\nif (utmSource !== '') {\n setItem('user_utm_source', utmSource, threeHours, '/', domain)\n}\n\nif (utmCampaign !== '') {\n setItem('user_utm_campaign', utmCampaign, threeHours, '/', domain)\n}\n\nif (utmKeyword !== '') {\n setItem('user_utm_keyword', utmKeyword, threeHours, '/', domain)\n}\n\nif (utmGroup !== '') {\n setItem('user_utm_group', utmGroup, threeHours, '/', domain)\n}\n\nif (gclid !== '') {\n setItem('user_gclid', gclid, threeHours, '/', domain)\n}\n\nif (type !== '') {\n setItem('car_type', type, threeHours, '/', domain)\n}\n\nif (network !== '') {\n setItem('user_search_partner', network, threeHours, '/', domain)\n}\n\nif (make !== undefined && make !== '') {\n setItem('car_make', make, threeHours, '/', domain)\n}\n\nif (model !== undefined && model !== '') {\n setItem('car_model', model, threeHours, '/', domain)\n}\n", "const LAZY_SRCSET_ATTRIBUTE_NAME = 'data-lazy-srcset'\n\nfunction getElementWidth (element) {\n return element.getBoundingClientRect().width\n}\n\nfunction suitableSrc (element, srcset) {\n const srcsWithSize = srcset.split(',')\n const width = getElementWidth(element)\n let srcAndWidth = []\n\n for (let i = 0; i < srcsWithSize.length; i += 1) {\n srcAndWidth = srcsWithSize[i].split(' ')\n\n if (parseFloat(srcAndWidth[1]) >= width) {\n break\n }\n }\n\n return srcAndWidth[0]\n}\n\nfunction loadImage (element) {\n const srcSet = element.getAttribute(LAZY_SRCSET_ATTRIBUTE_NAME)\n if (!srcSet) { return }\n\n let img = element\n if (element.tagName !== 'IMG') {\n img = document.createElement('img')\n img.sizes = getElementWidth(element) + 'px'\n img.addEventListener('load', () => {\n const imgSrc = img.currentSrc || suitableSrc(element, img.srcset) || img.src\n element.style.backgroundImage = `url(${imgSrc})`\n })\n }\n\n const fallbackUrl = srcSet.split(' ')[0]\n img.srcset = srcSet\n img.src = fallbackUrl\n}\n\nfunction handleMutation (observe, mutations) {\n mutations.forEach(mutation => {\n if (mutation.type === 'attributes') {\n loadImage(mutation.target)\n }\n if (mutation.type === 'childList') {\n observe(mutation.target)\n }\n })\n}\n\nfunction makeIntersectionObserver () {\n const opts = { rootMargin: '20%' }\n const observer = new window.IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n loadImage(entry.target)\n observer.unobserve(entry.target)\n }\n })\n }, opts)\n observer.POLL_INTERVAL = 100\n\n return observer\n}\n\nfunction observeChildren ({ intersection, mutation }, rootElement) {\n const nodes = rootElement.querySelectorAll(`[${LAZY_SRCSET_ATTRIBUTE_NAME}]`)\n nodes.forEach((e) => mutation.observe(e, {\n attributeFilter: [LAZY_SRCSET_ATTRIBUTE_NAME],\n attributes: true\n }))\n\n nodes.forEach(intersection.observe.bind(intersection))\n\n const remoteContentNodes = rootElement.querySelectorAll('carwow-remote-content')\n remoteContentNodes.forEach((e) => mutation.observe(e, { childList: true }))\n}\n\nfunction bootstrap () {\n const ctx = {}\n const observe = observeChildren.bind(null, ctx)\n\n ctx.intersection = makeIntersectionObserver()\n ctx.mutation = new window.MutationObserver(handleMutation.bind(null, observe))\n\n observe(document.body)\n}\n\ndocument.addEventListener('DOMContentLoaded', bootstrap)\n", "// polyfills\nimport 'intersection-observer'\n\nimport Rails from '@rails/ujs'\nimport '@carwow/carwow_theme/app/javascript/theme_common'\nimport 'shared/referrer_tracking'\n\n// JS-based image lazy loading is the legacy solution. We have moved all our code to\n// use native lazy loading intead. However, I/O agency pages still use these data atrributes\n// We need to load this to make sure the images are loaded on these pages.\nimport 'shared/lazy_srcset'\n\nRails.start()\n", "export class SlideZoomControls {\n constructor (image) {\n this.image = image\n this.activateZoom = this.activateZoom.bind(this)\n this.deactivateZoom = this.deactivateZoom.bind(this)\n this.moveZoom = this.moveZoom.bind(this)\n this.enable()\n }\n\n enable () {\n this.image.addEventListener('click', this.activateZoom)\n this.image.classList.add('media-slider__image--with-zoom')\n }\n\n activateZoom () {\n this.image.removeEventListener('click', this.activateZoom)\n this.image.parentElement.addEventListener('mousemove', this.moveZoom)\n document.addEventListener('click', this.deactivateZoom, true)\n this.image.classList.add('media-slider__image--zooming')\n }\n\n moveZoom (event) {\n const imageContainerRect = this.image.parentElement.getBoundingClientRect()\n const [mouseX, mouseY] = [\n event.clientX - imageContainerRect.left,\n event.clientY - imageContainerRect.top\n ]\n const percentX = (mouseX / this.image.width) * 100\n const percentY = (mouseY / this.image.height) * 100\n this.image.style.transformOrigin = `${percentX}% ${percentY}%`\n }\n\n deactivateZoom (event) {\n event.stopImmediatePropagation()\n this.image.classList.remove('media-slider__image--zooming')\n this.image.style.transformOrigin = 'center'\n document.removeEventListener('click', this.deactivateZoom, true)\n this.image.removeEventListener('mousemove', this.moveZoom)\n this.image.addEventListener('click', this.activateZoom)\n }\n}\n", "import { SlideZoomControls } from './slide_zoom_controls'\n\nlet lastSlideID = 0\n\nexport default class Slide {\n get defaultSrc () {\n if (!this.srcset) {\n return null\n }\n\n const srcs = this.srcset.split(',')\n const src = srcs[1] || srcs[0]\n return src.split(' ')[0]\n }\n\n constructor (data, number, options, errorCallback) {\n lastSlideID += 1\n\n this.id = lastSlideID\n this.srcset = data.img\n this.video = data.video\n this.title = data.title\n this.alt = data.alt\n this.subtitle = data.subtitle\n this.caption = data.caption\n this.number = number\n this.onErrorCallback = errorCallback\n this.options = options\n }\n\n copy () {\n return new Slide({\n img: this.srcset,\n video: this.video,\n title: this.title,\n alt: this.alt,\n subtitle: this.subtitle\n }, this.number, this.options, this.onErrorCallback)\n }\n\n assignPosition (position) {\n this.container.style.transform = `translateX(${position * 100}%)`\n }\n\n hide () {\n if (this.isBuilt) {\n this.container.style.transform = 'translateY(100%)'\n }\n }\n\n build (slidesContainer) {\n this.isBuilt = true\n this.container = document.createElement('div')\n this.container.className = 'media-slider__slide'\n this.container.dataset.number = this.number.toString()\n\n this.image = document.createElement('img')\n this.image.className = 'media-slider__image'\n\n if (this.options.linkTo) {\n const link = document.createElement('a')\n link.href = this.options.linkTo\n if (this.options.linkToTracking) {\n const tracking = JSON.parse(this.options.linkToTracking)\n\n link.dataset.interactionElement = tracking.interaction_element\n link.dataset.interactionSection = tracking.interaction_section\n }\n link.target = this.options.linkToTarget\n link.appendChild(this.image)\n this.container.appendChild(link)\n } else {\n this.container.appendChild(this.image)\n }\n\n this.load()\n\n this.buildImageSrc()\n\n if (this.title) {\n this.buildTitleOverlay()\n }\n\n if (this.video) {\n this.buildPlayButton()\n }\n\n if (this.caption) {\n this.buildCaption()\n }\n\n slidesContainer.appendChild(this.container)\n\n if (!this.options.cropImages) {\n this.displayFullImages()\n }\n\n if (this.options.enableZoomControls) {\n this.zoomControls = new SlideZoomControls(this.image)\n }\n }\n\n displayFullImages () {\n const ghostImage = this.image.cloneNode()\n ghostImage.classList.add('media-slider__image--ghost')\n this.image.classList.add('media-slider__image--contained')\n this.image.before(ghostImage)\n }\n\n buildPlayButton () {\n this.playButton = document.createElement('div')\n this.playButton.className = 'media-slider__play-button'\n\n this.container.appendChild(this.playButton)\n }\n\n buildTitleOverlay () {\n const overlayNode = document.createElement('div')\n const titleNode = document.createElement('div')\n const subtitleNode = document.createElement('div')\n\n overlayNode.className = 'media-slider__title-overlay'\n\n titleNode.className = 'media-slider__title-overlay-title'\n titleNode.innerHTML = this.title\n overlayNode.appendChild(titleNode)\n\n subtitleNode.className = 'media-slider__title-overlay-subtitle'\n subtitleNode.innerHTML = this.subtitle\n overlayNode.appendChild(subtitleNode)\n\n this.container.appendChild(overlayNode)\n }\n\n buildCaption () {\n const captionNode = document.createElement('div')\n const innerCaptionNode = document.createElement('div')\n captionNode.className = 'media-slider__caption'\n innerCaptionNode.className = 'media-slider__caption-inner'\n innerCaptionNode.textContent = this.caption\n captionNode.appendChild(innerCaptionNode)\n this.container.appendChild(captionNode)\n }\n\n buildImageSrc () {\n if (this.options.lazyLoadFirstImages && (this.number <= 2)) {\n this.image.loading = 'lazy'\n }\n\n this.image.srcset = this.srcset\n this.image.src = this.defaultSrc\n this.image.alt = this.alt || ''\n }\n\n load () {\n this.image.addEventListener('error', () => {\n this.errored = true\n\n if (this.onErrorCallback) this.onErrorCallback(this)\n })\n }\n\n // eslint-disable-next-line class-methods-use-this\n blur () { }\n}\n", "/* global YT */\n\nimport Slide from './slide'\n\nlet youtubeApiLoaded = false\nlet youtubeApiLoadInitiated = false\nconst youtubeApiLoadCallbacks = []\n\n// We need to remember playback state for each video since sometimes slides\n// could be copied\nconst playbackStates = {}\n\nfunction loadYoutubeApi () {\n youtubeApiLoadInitiated = true\n const tag = document.createElement('script')\n tag.src = 'https://www.youtube.com/iframe_api'\n document.head.appendChild(tag)\n}\n\nfunction afterYoutubeApiLoad (callback) {\n if (!youtubeApiLoadInitiated) loadYoutubeApi()\n\n if (youtubeApiLoaded) {\n callback()\n } else {\n youtubeApiLoadCallbacks.push(callback)\n }\n}\n\nwindow.onYouTubeIframeAPIReady = () => {\n youtubeApiLoaded = true\n\n youtubeApiLoadCallbacks.forEach((callback) => callback())\n}\n\nexport default class VideoSlide extends Slide {\n constructor (data, number, options, errorCallback) {\n super(data, number, { ...options, linkTo: undefined, cropImages: true }, errorCallback)\n }\n\n copy () {\n return new VideoSlide({\n img: this.srcset,\n video: this.video,\n title: this.title,\n subtitle: this.subtitle\n }, this.index, this.options, this.onErrorCallback)\n }\n\n blur () {\n if (this.player) this.pause()\n }\n\n stop () {\n if (this.player) this.player.stopVideo()\n }\n\n build (slidesContainer) {\n super.build(slidesContainer)\n\n this.videoContainer = document.createElement('div')\n this.videoContainer.className = 'media-slider__video'\n this.videoContainer.id = `slide_${this.id}`\n this.container.dataset.number = this.number.toString()\n\n this.container.appendChild(this.videoContainer)\n this.playButton.addEventListener('click', () => this.play(), false)\n\n afterYoutubeApiLoad(() => {\n this.player = new YT.Player(this.videoContainer.id, {\n videoId: this.video,\n events: {\n onReady: () => {\n this.playerInitiated = true\n if (this.playScheduled) this.play()\n }\n }\n })\n })\n }\n\n play () {\n if (this.playerInitiated) {\n this.player.seekTo(playbackStates[this.video] || 0)\n this.player.playVideo()\n this.container.classList.add('media-slider__slide--video-active')\n } else {\n this.playScheduled = true\n }\n }\n\n pause () {\n if (this.playerInitiated) {\n playbackStates[this.video] = this.player.getCurrentTime()\n\n this.player.pauseVideo()\n this.container.classList.remove('media-slider__slide--video-active')\n }\n }\n}\n", "import Slide from './slide'\nimport VideoSlide from './video_slide'\n\nexport default class SlideController {\n get currentSlide () {\n return this.slides[this.currentSlideIndex]\n }\n\n constructor (slidesData, options, currentSlideIndex, errorCallback) {\n this.currentSlideIndex = currentSlideIndex\n this.errorCallback = errorCallback\n this.numberOfSlides = 0\n this.data = slidesData\n this.options = options\n\n this.setup()\n }\n\n setup () {\n this.slides = this.data.map((item, index) => {\n if (typeof item.video === 'string') {\n return new VideoSlide(item, index + 1, this.options, this.slideError.bind(this))\n }\n\n return new Slide(item, index + 1, this.options, this.slideError.bind(this))\n })\n\n this.numberOfSlides = this.slides.length\n\n this.copySlidesIfNeeded()\n }\n\n copySlidesIfNeeded () {\n if (this.slides.length && this.slides.length < 3) {\n const slideCopies = this.slides.map((s) => s.copy())\n this.slides = this.slides.concat(slideCopies)\n\n this.copySlidesIfNeeded()\n }\n }\n\n slideError () {\n let dataModified = false\n\n this.slides.forEach(slide => {\n if (slide.errored && this.data[slide.number - 1]) {\n this.data.splice((slide.number - 1), 1)\n dataModified = true\n }\n })\n\n if (dataModified) {\n this.setup()\n this.errorCallback()\n }\n }\n\n nextSlideIndex (slideIndex) {\n return slideIndex === (this.slides.length - 1) ? 0 : (slideIndex + 1)\n }\n\n prevSlideIndex (slideIndex) {\n return slideIndex === 0 ? (this.slides.length - 1) : (slideIndex - 1)\n }\n\n nextSlide () {\n this.currentSlide.blur()\n this.currentSlideIndex = this.nextSlideIndex(this.currentSlideIndex)\n }\n\n prevSlide () {\n this.currentSlide.blur()\n this.currentSlideIndex = this.prevSlideIndex(this.currentSlideIndex)\n }\n\n jumpToSlide (slideIndex) {\n this.currentSlide.blur()\n this.currentSlideIndex = slideIndex\n }\n\n stop () {\n this.currentSlide.stop()\n }\n\n activeSlidesIndexes () {\n const indexes = [this.currentSlideIndex]\n\n // growLeft\n const slideToLeftIndex = this.prevSlideIndex(indexes[0])\n indexes.unshift(slideToLeftIndex)\n\n // growRight\n const slideToRightIndex = this.nextSlideIndex(indexes[indexes.length - 1])\n indexes.push(slideToRightIndex)\n\n return indexes\n }\n}\n", "// useful for when using logic dependant on if an attribute exists, and that\n// it's not falsey.\n// further: https://github.com/carwow/carwow-theme/pull/5209\nexport function hasTruthyAttribute (attributes, attributeName) {\n if (!attributes.hasAttribute(attributeName)) return false\n\n const value = attributes.getAttribute(attributeName)\n\n if (typeof value === 'boolean') return value\n if (value === 'false') return false\n\n return true\n}\n", "import SlideController from './media_slider/slide_controller'\nimport eventBus from '../event_bus'\nimport { hasTruthyAttribute } from '../helpers/attributes'\n\nconst SWIPE_PX_THRESHOLD = 50\nconst SWIPE_MS_THRESHOLD = 500\nconst SWIPE_ANIMATION_THRESHOLD = 20\n\nfunction isSwipeLeft (pxDiff, msDiff) {\n return msDiff < SWIPE_MS_THRESHOLD && pxDiff > SWIPE_PX_THRESHOLD\n}\n\nfunction isSwipeRight (pxDiff, msDiff) {\n return msDiff < SWIPE_MS_THRESHOLD && pxDiff < -SWIPE_PX_THRESHOLD\n}\n\nclass CarwowMediaSlider extends window.HTMLElement {\n static get observedAttributes () { return ['items', 'link_to'] }\n\n get nextSlideTrigger () {\n return this.querySelector('[data-role=\"media-slider__next\"]')\n }\n\n get previousSlideTrigger () {\n return this.querySelector('[data-role=\"media-slider__prev\"]')\n }\n\n get currentSlideNumber () {\n const currentSlide = this.controller.currentSlide\n\n return currentSlide ? currentSlide.number : 1\n }\n\n connectedCallback () {\n this.isInGalleryOverlay = hasTruthyAttribute(this, 'is_in_gallery_overlay')\n this.lazyLoadFirstImages = hasTruthyAttribute(this, 'lazy_load_first_images')\n this.connected = true\n\n // defer slider initialisation when it's in the gallery\n if (!this.isInGalleryOverlay) {\n this.render()\n }\n\n this.listenToGalleryEvents()\n }\n\n attributeChangedCallback (name) {\n if (name !== 'items' && name !== 'link_to') return\n\n this.render()\n }\n\n render () {\n if (!this.connected) return\n const data = this.sanitizeItems(JSON.parse(this.getAttribute('items')))\n\n if (data.length === 0) { this.handleNoSlides(); return }\n\n this.setup(data)\n this.buildDOMStructure()\n\n if (data.length === 1) { return }\n\n this.resetPosition()\n this.updateCurrentSlideIndicator()\n\n this.addSwipeGestures()\n this.addClickCallback()\n this.addTransitionCallback()\n }\n\n setup (data) {\n this.id = this.hasAttribute('id') ? this.getAttribute('id') : ''\n\n const options = {\n lazyLoadFirstImages: hasTruthyAttribute(this, 'lazy_load_first_images'),\n linkTo: this.getAttribute('link_to'),\n linkToTarget: this.getAttribute('link_to_target'),\n linkToTracking: this.getAttribute('link_to_tracking'),\n cropImages: this.getAttribute('crop_images') !== 'false',\n enableZoomControls: hasTruthyAttribute(this, 'enable_zoom_controls')\n }\n\n if (options.linkTo && options.enableZoomControls) {\n throw new Error('The following flags are not compatible: \"linkTo\" and \"enableZoomControls\"')\n }\n\n this.controller = new SlideController(\n data,\n options,\n 0,\n () => {\n // remove existing slides from DOM\n this.row.textContent = ''\n\n this.assignSlides()\n this.updateCurrentSlideIndicator()\n }\n )\n }\n\n handleNoSlides () {\n this.classList.add('media-slider--empty')\n }\n\n clearDOM () {\n this.classList.remove('media-slider--empty')\n this.querySelectorAll('.media-slider__pages').forEach(e => e.remove())\n this.querySelectorAll('.media-slider__frame').forEach(e => e.remove())\n }\n\n buildDOMStructure () {\n this.clearDOM()\n\n this.frame = document.createElement('div')\n this.frame.className = 'media-slider__frame'\n this.appendChild(this.frame)\n\n this.row = document.createElement('div')\n this.row.className = 'media-slider__images'\n this.frame.appendChild(this.row)\n this.nav = this.querySelector('[data-role=\"media-slider__nav\"]')\n\n if (this.controller.numberOfSlides > 1) {\n this.assignSlides()\n this.nav.classList.remove('media-slider__nav--hidden')\n this.currentSlideIndicator = document.createElement('div')\n this.currentSlideIndicator.className = 'media-slider__pages'\n this.nav.appendChild(this.currentSlideIndicator)\n } else {\n const slide = this.controller.slides[0]\n slide.build(this.row)\n }\n }\n\n assignSlides () {\n // this check is needed for when there are more than one item passed into component\n // but slides cannot be created from them because of broken image urls\n if (this.controller.numberOfSlides === 0) { this.handleNoSlides(); return }\n\n this.controller.slides.forEach((slide) => slide.hide())\n\n const numberOfSlides = 3\n const activeSlidesIndexes = this.controller.activeSlidesIndexes();\n\n [...Array(numberOfSlides)].forEach((el, ind) => {\n const slide = this.controller.slides[activeSlidesIndexes[ind]]\n\n if (!slide.isBuilt) {\n slide.build(this.row)\n }\n\n slide.assignPosition(ind)\n })\n }\n\n addSwipeGestures () {\n this.addEventListener(\n 'touchstart',\n this.touchStart.bind(this),\n { passive: true }\n )\n\n this.addEventListener(\n 'touchend',\n this.touchStop.bind(this),\n false\n )\n\n this.addEventListener(\n 'touchmove',\n this.touchMove.bind(this),\n { passive: false }\n )\n }\n\n addClickCallback () {\n this.addEventListener(\n 'click',\n this.handleClick.bind(this),\n false\n )\n }\n\n addTransitionCallback () {\n this.addEventListener(\n 'transitionend',\n this.handleTransitionEnd.bind(this),\n false\n )\n }\n\n touchStart (e) {\n if (this.transitioning) return\n\n this.touchstartX = e.changedTouches[0].screenX\n this.touchstartY = e.changedTouches[0].screenY\n this.touchstartTime = new Date()\n }\n\n touchMove (e) {\n if (this.transitioning) return\n\n const frameWidth = this.frame.offsetWidth\n\n const diffX = e.changedTouches[0].screenX - this.touchstartX\n const diffY = e.changedTouches[0].screenY - this.touchstartY\n\n // Prevent scroll if needed\n if (Math.abs(diffX) > Math.abs(diffY)) {\n e.preventDefault()\n }\n\n if (!this.slowSwipeStarted && Math.abs(diffX) < SWIPE_ANIMATION_THRESHOLD) return\n this.slowSwipeStarted = true\n\n const offset = Math.max(\n -frameWidth,\n Math.min(diffX, frameWidth)\n )\n\n this.row.style.transform = `translateX(${-frameWidth + offset}px)`\n }\n\n touchStop (e) {\n this.slowSwipeStarted = false\n if (this.transitioning) return\n\n const pxDiff = e.changedTouches[0].screenX - this.touchstartX\n const msDiff = new Date() - this.touchstartTime\n\n if (isSwipeLeft(pxDiff, msDiff)) {\n this.changeSlide(-1)\n } else if (isSwipeRight(pxDiff, msDiff)) {\n this.changeSlide(1)\n } else {\n this.closestSlide(pxDiff)\n }\n }\n\n handleClick (e) {\n if (e.target === this.nextSlideTrigger) {\n e.preventDefault()\n e.stopPropagation() // Do not trigger \"clicked link\" event in amplitude\n\n if (this.transitioning) return\n\n this.changeSlide(1)\n } else if (e.target === this.previousSlideTrigger) {\n e.preventDefault()\n e.stopPropagation() // Do not trigger \"clicked link\" event in amplitude\n if (this.transitioning) return\n\n this.changeSlide(-1)\n }\n }\n\n runTransition () {\n this.transitioning = true\n this.classList.add('media-slider--transitioning')\n }\n\n handleTransitionEnd (e) {\n if (e.target === this.row) {\n this.classList.remove('media-slider--transitioning')\n this.transitioning = false\n\n this.assignSlides()\n this.resetPosition()\n }\n }\n\n closestSlide (pxDiff) {\n const frameWidth = this.frame.offsetWidth\n\n if (pxDiff > frameWidth / 2) {\n this.changeSlide(-1)\n } else if (pxDiff < -frameWidth / 2) {\n this.changeSlide(1)\n } else {\n if (Math.abs(pxDiff) > SWIPE_ANIMATION_THRESHOLD) this.runTransition()\n this.resetPosition()\n }\n }\n\n resetPosition () {\n this.row.style.transform = 'translateX(-100%)'\n }\n\n changeSlide (offset) {\n if (offset === 1) {\n this.controller.nextSlide()\n } else {\n this.controller.prevSlide()\n }\n\n this.runTransition()\n this.dispatchSlideChangeEvent()\n this.updateCurrentSlideIndicator()\n\n this.row.style.transform = `translateX(-${(1 + offset) * 100}%)`\n }\n\n jumpToSlide (slideIndex) {\n this.controller.jumpToSlide(slideIndex)\n\n this.dispatchSlideChangeEvent()\n this.updateCurrentSlideIndicator()\n this.assignSlides()\n this.resetPosition()\n }\n\n updateCurrentSlideIndicator () {\n if (this.controller.numberOfSlides > 1) {\n this.currentSlideIndicator.innerHTML = `${(this.currentSlideNumber)}/${this.controller.numberOfSlides}`\n }\n }\n\n sanitizeItems (items) {\n return items.filter(item => item.img)\n }\n\n // listen to thumbnail clicks and close button click in gallery overlay\n listenToGalleryEvents () {\n eventBus.on('open-gallery-cta-click', (e) => {\n if (e.targetSlider !== this.id) return\n\n // initialise the slider since it is visible now\n this.render()\n\n const slideStartingIndex = e.slideStartingIndex\n this.jumpToSlide(slideStartingIndex)\n })\n\n eventBus.on('gallery-thumbnail-click', (e) => {\n console.log('On Gallery Thumbnail click')\n if (e.targetSlider !== this.id) return\n\n const selectedThumbnailIndex = e.thumbnailIndex\n const offset = selectedThumbnailIndex - this.controller.currentSlideIndex\n\n if (Math.abs(offset) > 1) {\n this.jumpToSlide(selectedThumbnailIndex) // jumps over more than one slide\n } else {\n this.changeSlide(offset) // moves by one slide\n }\n })\n\n eventBus.on('close-gallery-click', (e) => {\n if (e.targetSlider !== this.id) return\n\n if (this.controller.currentSlide.playerInitiated) {\n this.controller.stop()\n }\n })\n }\n\n dispatchSlideChangeEvent () {\n eventBus.emit('slider-slide-change', {\n galleryId: this.id,\n inGallery: this.isInGalleryOverlay,\n currentSlideIndex: this.currentSlideNumber - 1\n })\n }\n}\n\nwindow.customElements.define('carwow-media-slider', CarwowMediaSlider)\n", "function toEmbedVideoUrl (videoLink, autoplay) {\n let youtubeId = videoLink.split('v=')[1]\n if (videoLink.indexOf('/embed/') !== -1) return videoLink\n youtubeId = youtubeId.split('&')[0]\n return `//www.youtube.com/embed/${youtubeId}?rel=0&wmode=transparent&autoplay=${autoplay}`\n}\n\nfunction setModalVideoUrl (videoLinkElement) {\n const videoUrl = videoLinkElement.dataset.href\n const autoplay = videoLinkElement.dataset.autoplay === 'true' ? '1' : '0'\n const embedVideoUrl = toEmbedVideoUrl(videoUrl, autoplay)\n\n const modalRadioId = videoLinkElement.getAttribute('for')\n const modalId = document.querySelector(`#${modalRadioId}`).getAttribute('name')\n\n document.querySelector(`#${modalId} iframe[data-carwow-video]`).setAttribute('src', embedVideoUrl)\n}\n\nfunction openVideoModal (whichModal) {\n const modalElement = whichModal.getAttribute('for')\n const openRadioBtn = document.querySelector(`#${modalElement}`)\n openRadioBtn.click()\n}\n\nfunction handleVideoLinkClick (e) {\n if (typeof e.target.closest !== 'function') return\n\n const thisVideoLinkElement = e.target.closest('[data-target=video-modal-link]')\n\n if (thisVideoLinkElement) {\n setModalVideoUrl(thisVideoLinkElement)\n }\n}\n\nfunction stopVideoOnClose (e) {\n if (typeof e.target.closest !== 'function') return\n\n const videoModalClose = e.target.closest('[data-stop-video-on-close]')\n\n if (videoModalClose && videoModalClose.checked) {\n document.querySelectorAll('iframe[data-carwow-video]').forEach(video => {\n video.setAttribute('src', '')\n })\n }\n}\n\nfunction autoOpenVideo () {\n const openVideo = window.location.search.indexOf('video=true') !== -1\n const thisVideoLinkElement = document.querySelector('[data-target=video-modal-link]')\n\n if (openVideo && thisVideoLinkElement) {\n setModalVideoUrl(thisVideoLinkElement)\n openVideoModal(thisVideoLinkElement)\n }\n}\n\nfunction openVideoByDefault () {\n const thisVideoLinkElement = document.querySelector('[data-video-open-by-default]')\n\n if (thisVideoLinkElement) {\n setModalVideoUrl(thisVideoLinkElement)\n openVideoModal(thisVideoLinkElement)\n }\n}\n\nfunction initVideoEventListeners () {\n if (document.querySelector('[data-target=video-modal-link]')) {\n document.addEventListener('click', (e) => {\n handleVideoLinkClick(e)\n })\n document.addEventListener('change', (e) => {\n stopVideoOnClose(e)\n })\n }\n}\n\nfunction initVideoPlayer () {\n initVideoEventListeners()\n autoOpenVideo()\n openVideoByDefault()\n}\n\ndocument.addEventListener('DOMContentLoaded', initVideoPlayer)\n\nwindow.Carwow = window.Carwow || {}\nwindow.Carwow.initVideoPlayer = initVideoPlayer\n", "import eventBus from '../event_bus'\n\nlet slideStartingIndex = 0\nlet selectedThumbnailIndex = 0\n\nfunction addThumbnailFocus (galleryThumbnails, targetThumbnail) {\n galleryThumbnails.forEach(galleryThumbnail => {\n galleryThumbnail.classList.remove('gallery__thumbnail--active')\n })\n\n targetThumbnail.classList.add('gallery__thumbnail--active')\n}\n\nfunction getMediaSlider (targetGalleryModal) {\n return document.querySelector(`#${targetGalleryModal} carwow-media-slider`)\n}\n\nfunction handleThumbnailClicks (galleryThumbnails) {\n galleryThumbnails.forEach(thumbnail => {\n thumbnail.addEventListener('click', () => {\n const previousSelectedThumbnailIndex = selectedThumbnailIndex\n selectedThumbnailIndex = parseInt(thumbnail.dataset.galleryThumbnailIndex, 10)\n\n // only do something if the thumbnail has not been clicked already\n if (selectedThumbnailIndex !== previousSelectedThumbnailIndex) {\n const targetSlider = thumbnail.dataset.galleryThumbnailForSlider\n\n eventBus.emit('gallery-thumbnail-click', {\n targetSlider,\n thumbnailIndex: selectedThumbnailIndex\n })\n }\n })\n })\n}\n\nfunction handleOpenCtaClicks (container) {\n container.querySelectorAll('[data-open-gallery]').forEach(cta => {\n const galleryName = cta.getAttribute('for').replace('-open', '')\n const gallery = container.querySelector(`[data-gallery-id='${galleryName}']`)\n if (!gallery) return\n const targetGalleryModal = gallery.dataset.galleryId + '-modal'\n const targetMediaSlider = getMediaSlider(targetGalleryModal)\n\n cta.addEventListener('click', () => {\n if (cta.hasAttribute('data-gallery-starting-index')) {\n slideStartingIndex = parseInt(cta.dataset.galleryStartingIndex, 10)\n }\n\n eventBus.emit('open-gallery-cta-click', {\n targetSlider: (targetMediaSlider && targetMediaSlider.id),\n slideStartingIndex\n })\n\n const openGalleryEvent = new window.CustomEvent('open-gallery-new-click', {\n detail: {\n targetGallery: targetMediaSlider && targetMediaSlider.id,\n slideStartingIndex\n }\n })\n document.dispatchEvent(openGalleryEvent)\n\n gallery.dataset.galleryOpen = true\n })\n\n const overlay = document.querySelector(`#${targetGalleryModal} .gallery`)\n if (overlay) {\n document.querySelector(`#${targetGalleryModal} .gallery`).addEventListener('click', (event) => {\n if (event.target === overlay) {\n event.stopImmediatePropagation()\n document.querySelector(`#${targetGalleryModal}`).querySelector('[data-gallery-close]').click()\n }\n })\n }\n })\n}\n\nfunction updateThumbnailFocus () {\n eventBus.on('slider-slide-change', (e) => {\n if (!e.inGallery) return\n\n const galleryThumbnails = document.querySelectorAll(`[data-gallery-thumbnail-for-slider='${e.galleryId}']`)\n const targetThumbnail = document.querySelector(`[data-gallery-thumbnail-for-slider='${e.galleryId}'][data-gallery-thumbnail-index='${e.currentSlideIndex}']`)\n selectedThumbnailIndex = e.currentSlideIndex\n\n if (targetThumbnail) {\n addThumbnailFocus(galleryThumbnails, targetThumbnail)\n }\n })\n}\n\nfunction resetGallery () {\n slideStartingIndex = 0\n selectedThumbnailIndex = 0\n\n const openGallery = document.querySelector('[data-gallery-open]')\n if (!openGallery) return\n\n const targetGalleryModal = openGallery.dataset.galleryId + '-modal'\n const targetMediaSlider = getMediaSlider(targetGalleryModal)\n\n eventBus.emit('close-gallery-click', {\n targetSlider: targetMediaSlider && targetMediaSlider.id\n })\n\n delete openGallery.dataset.galleryOpen\n}\n\nfunction handleGalleryClose (container) {\n container.querySelectorAll('[data-gallery-close]').forEach(button => {\n button.addEventListener('click', resetGallery)\n })\n}\n\nconst closeOnEscape = {\n handleEvent (e) {\n if (e.keyCode === 27) {\n resetGallery()\n }\n },\n init () {\n document.addEventListener('keyup', this)\n }\n}\n\nfunction hideOnImgError () {\n document.addEventListener('error', (e) => {\n if ((e.target.tagName.toLowerCase() === 'img') && (e.target.closest('[data-gallery-thumbnail-index]'))) {\n const thumbnailsContainer = e.target.closest('[data-gallery-thumbnails]')\n e.target.closest('[data-gallery-thumbnail-index]').remove()\n const thisGalleryThumbnails = thumbnailsContainer.querySelectorAll('[data-gallery-thumbnail-index]')\n\n // reset indexes\n thisGalleryThumbnails.forEach((thumb, index) => {\n thumb.setAttribute('data-gallery-thumbnail-index', index)\n })\n }\n }, true)\n}\n\nexport function initGallery (container) {\n const galleries = container.querySelectorAll('[data-gallery-id]')\n\n galleries.forEach(gallery => {\n const galleryThumbnails = gallery.querySelectorAll('[data-gallery-thumbnail-index]')\n\n handleThumbnailClicks(galleryThumbnails)\n })\n\n updateThumbnailFocus()\n handleOpenCtaClicks(container)\n handleGalleryClose(container)\n closeOnEscape.init()\n hideOnImgError()\n}\n", "import eventBus from '@carwow/carwow_theme/app/javascript/event_bus'\n\nexport default function initOpenGalleryOnIndex () {\n eventBus.on('slider-slide-change', (e) => {\n const slider = document.getElementById(e.galleryId)\n\n if (slider) {\n slider.parentElement.setAttribute('data-gallery-starting-index', e.currentSlideIndex)\n }\n })\n}\n", "/**\n * A lightweight youtube embed. Still should feel the same to the user, just MUCH faster to initialize and paint.\n *\n * Thx to these as the inspiration\n * https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html\n * https://autoplay-youtube-player.glitch.me/\n *\n * Once built it, I also found these:\n * https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube (\uD83D\uDC4D\uD83D\uDC4D)\n * https://github.com/Daugilas/lazyYT\n * https://github.com/vb/lazyframe\n */\nclass LiteYTEmbed extends HTMLElement {\n connectedCallback() {\n this.videoId = this.getAttribute('videoid');\n\n let playBtnEl = this.querySelector('.lty-playbtn');\n // A label for the button takes priority over a [playlabel] attribute on the custom-element\n this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play';\n\n this.dataset.title = this.getAttribute('title') || \"\";\n\n /**\n * Lo, the youtube poster image! (aka the thumbnail, image placeholder, etc)\n *\n * See https://github.com/paulirish/lite-youtube-embed/blob/master/youtube-thumbnail-urls.md\n */\n if (!this.style.backgroundImage) {\n this.style.backgroundImage = `url(\"https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg\")`;\n this.upgradePosterImage();\n }\n\n // Set up play button, and its visually hidden label\n if (!playBtnEl) {\n playBtnEl = document.createElement('button');\n playBtnEl.type = 'button';\n playBtnEl.classList.add('lty-playbtn');\n this.append(playBtnEl);\n }\n if (!playBtnEl.textContent) {\n const playBtnLabelEl = document.createElement('span');\n playBtnLabelEl.className = 'lyt-visually-hidden';\n playBtnLabelEl.textContent = this.playLabel;\n playBtnEl.append(playBtnLabelEl);\n }\n\n this.addNoscriptIframe();\n\n // for the PE pattern, change anchor's semantics to button\n if(playBtnEl.nodeName === 'A'){\n playBtnEl.removeAttribute('href');\n playBtnEl.setAttribute('tabindex', '0');\n playBtnEl.setAttribute('role', 'button');\n // fake button needs keyboard help\n playBtnEl.addEventListener('keydown', e => {\n if( e.key === 'Enter' || e.key === ' ' ){\n e.preventDefault();\n this.activate();\n }\n });\n }\n\n // On hover (or tap), warm up the TCP connections we're (likely) about to use.\n this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true});\n this.addEventListener('focusin', LiteYTEmbed.warmConnections, {once: true});\n\n // Once the user clicks, add the real iframe and drop our play button\n // TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time\n // We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003\n this.addEventListener('click', this.activate);\n\n // Chrome & Edge desktop have no problem with the basic YouTube Embed with ?autoplay=1\n // However Safari desktop and most/all mobile browsers do not successfully track the user gesture of clicking through the creation/loading of the iframe,\n // so they don't autoplay automatically. Instead we must load an additional 2 sequential JS files (1KB + 165KB) (un-br) for the YT Player API\n // TODO: Try loading the the YT API in parallel with our iframe and then attaching/playing it. #82\n this.needsYTApi = this.hasAttribute(\"js-api\") || navigator.vendor.includes('Apple') || navigator.userAgent.includes('Mobi');\n }\n\n /**\n * Add a to the head\n */\n static addPrefetch(kind, url, as) {\n const linkEl = document.createElement('link');\n linkEl.rel = kind;\n linkEl.href = url;\n if (as) {\n linkEl.as = as;\n }\n document.head.append(linkEl);\n }\n\n /**\n * Begin pre-connecting to warm up the iframe load\n * Since the embed's network requests load within its iframe,\n * preload/prefetch'ing them outside the iframe will only cause double-downloads.\n * So, the best we can do is warm up a few connections to origins that are in the critical path.\n *\n * Maybe `` would work, but it's unsupported: http://crbug.com/593267\n * But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.\n */\n static warmConnections() {\n if (LiteYTEmbed.preconnected) return;\n\n // The iframe document and most of its subresources come right off youtube.com\n LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');\n // The botguard script is fetched off from google.com\n LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');\n\n // Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.\n LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');\n LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');\n\n LiteYTEmbed.preconnected = true;\n }\n\n fetchYTPlayerApi() {\n if (window.YT || (window.YT && window.YT.Player)) return;\n\n this.ytApiPromise = new Promise((res, rej) => {\n var el = document.createElement('script');\n el.src = 'https://www.youtube.com/iframe_api';\n el.async = true;\n el.onload = _ => {\n YT.ready(res);\n };\n el.onerror = rej;\n this.append(el);\n });\n }\n\n /** Return the YT Player API instance. (Public L-YT-E API) */\n async getYTPlayer() {\n if(!this.playerPromise) {\n await this.activate();\n }\n\n return this.playerPromise;\n }\n\n async addYTPlayerIframe() {\n this.fetchYTPlayerApi();\n await this.ytApiPromise;\n\n const videoPlaceholderEl = document.createElement('div')\n this.append(videoPlaceholderEl);\n\n const paramsObj = Object.fromEntries(this.getParams().entries());\n\n this.playerPromise = new Promise(resolve => {\n let player = new YT.Player(videoPlaceholderEl, {\n width: '100%',\n videoId: this.videoId,\n playerVars: paramsObj,\n events: {\n 'onReady': event => {\n event.target.playVideo();\n resolve(player);\n }\n }\n });\n });\n }\n\n // Add the iframe within