{"version":3,"file":"share-this.js","sources":["../src/dom.js","../src/utils.js","../src/popover.js","../src/selection.js","../src/core.js","../src/render.js"],"sourcesContent":["export function getOffsetScroll(_window) {\n const body = _window.document.body;\n const scrollReference = _window.getComputedStyle(body).position === \"static\" ? body.parentNode : body;\n return scrollReference.getBoundingClientRect();\n}\n\nlet matchFunc;\nexport function matches(element, selector) {\n if (!matchFunc) matchFunc = getMatchFunctionName(element);\n return element[matchFunc](selector);\n}\n\nexport function closest(element, selector) {\n let target = element;\n while (target && (target.nodeType !== 1 /* === Node.ELEMENT_NODE */ || !matches(target, selector))) {\n target = target.parentNode;\n }\n\n return target;\n}\n\n// `contains` in IE doesn't work with text nodes\nexport function contains(ancestor, target) {\n const comparedPositions = ancestor.compareDocumentPosition(target);\n // eslint-disable-next-line no-bitwise\n return !comparedPositions || (comparedPositions & 16 /* === Node.DOCUMENT_POSITION_CONTAINED_BY */) > 0;\n}\n\n// eslint-disable-next-line consistent-return\nfunction getMatchFunctionName(element) {\n const suffix = \"atchesSelector\";\n for (const name of [ \"matches\", `m${suffix}`, `webkitM${suffix}`, `mozM${suffix}`, `msM${suffix}`, `oM${suffix}` ]) {\n if (element[name]) return name;\n }\n}\n","export function camelize(string) {\n return string.replace(/(?:^|-)([a-z])/g, (_, char) => char.toUpperCase());\n}\n\n// eslint-disable-next-line consistent-return\nexport function findByName(array, name) {\n // I would have used\n // for (const item of array) {\n // but transpilers generate A LOT of code in this specific case.\n for (let i = 0; i < array.length; i++) {\n const item = array[i];\n if (item.name === name) {\n return item;\n }\n }\n}\n\nexport function extend(dest, source) {\n if (source && typeof source === \"object\") {\n // eslint-disable-next-line guard-for-in\n for (const prop in source) {\n // eslint-disable-next-line no-param-reassign\n dest[prop] = source[prop];\n }\n }\n\n return dest;\n}\n\nexport function isCallable(func) {\n return typeof func === \"function\";\n}\n","import { getOffsetScroll, closest } from \"./dom.js\";\nimport { findByName, isCallable } from \"./utils.js\";\nimport { isSelectionForward, getEndLineRect } from \"./selection.js\";\n\nexport function stylePopover(popover, range, options) {\n const _document = options.document;\n const _window = _document.defaultView;\n const selection = _window.getSelection();\n const isForward = isSelectionForward(selection);\n const endLineRect = getEndLineRect(range, isForward);\n const offsetScroll = getOffsetScroll(_window);\n\n const style = popover.style;\n if (isForward) {\n style.right = `${_document.documentElement.clientWidth - endLineRect.right + offsetScroll.left}px`;\n } else {\n style.left = `${endLineRect.left - offsetScroll.left}px`;\n }\n style.width = `${endLineRect.right - endLineRect.left}px`;\n style.height = `${endLineRect.bottom - endLineRect.top}px`;\n style.top = `${endLineRect.top - offsetScroll.top}px`;\n style.position = \"absolute\";\n\n // eslint-disable-next-line no-param-reassign\n popover.className = options.popoverClass;\n}\n\nconst dataAttribute = \"data-share-via\";\nexport function popoverClick(sharers, event) {\n const item = closest(event.target, `[${dataAttribute}]`);\n if (!item) return;\n\n const via = item.getAttribute(dataAttribute);\n const sharer = findByName(sharers, via);\n if (sharer && isCallable(sharer.action)) {\n sharer.action(event, item);\n }\n}\n\nexport function lifeCycleFactory(document) {\n return {\n createPopover() {\n const popover = document.createElement(\"div\");\n popover.addEventListener(\"click\", function(event) {\n popoverClick(this.sharers, event);\n });\n return popover;\n },\n attachPopover(popover) {\n document.body.appendChild(popover);\n },\n removePopover(popover) {\n const parent = popover.parentNode;\n if (parent) parent.removeChild(popover);\n }\n };\n}\n","import { closest, contains } from \"./dom.js\";\n\nexport function isSelectionForward(selection) {\n if (selection.isCollapsed) return true;\n\n const comparedPositions = selection.anchorNode.compareDocumentPosition(selection.focusNode);\n if (!comparedPositions) {\n // It's the same node\n return selection.anchorOffset < selection.focusOffset;\n }\n\n // eslint-disable-next-line no-bitwise\n return (comparedPositions & 4 /* === Node.DOCUMENT_POSITION_FOLLOWING */) > 0;\n}\n\nexport function getEndLineRect(range, isForward) {\n let endLineRects;\n const rangeRects = range.getClientRects();\n const sliceRects = [].slice.bind(rangeRects);\n\n if (isForward) {\n let lastLeft = Infinity;\n let i = rangeRects.length;\n while (i--) {\n const rect = rangeRects[i];\n if (rect.left > lastLeft) break;\n lastLeft = rect.left;\n }\n endLineRects = sliceRects(i + 1);\n } else {\n let lastRight = -Infinity;\n let i = 0;\n for (; i < rangeRects.length; i++) {\n const rect = rangeRects[i];\n if (rect.right < lastRight) break;\n lastRight = rect.right;\n }\n endLineRects = sliceRects(0, i);\n }\n\n return {\n top: Math.min(...endLineRects.map(rect => rect.top)),\n bottom: Math.max(...endLineRects.map(rect => rect.bottom)),\n left: endLineRects[0].left,\n right: endLineRects[endLineRects.length - 1].right\n };\n}\n\nexport function constrainRange(range, selector) {\n const constrainedRange = range.cloneRange();\n if (range.collapsed || !selector) return constrainedRange;\n\n let ancestor = closest(range.startContainer, selector);\n if (ancestor) {\n if (!contains(ancestor, range.endContainer)) {\n constrainedRange.setEnd(ancestor, ancestor.childNodes.length);\n }\n } else {\n ancestor = closest(range.endContainer, selector);\n if (ancestor) constrainedRange.setStart(ancestor, 0);\n else constrainedRange.collapse();\n }\n\n return constrainedRange;\n}\n","import { stylePopover, lifeCycleFactory } from \"./popover.js\";\nimport { constrainRange } from \"./selection.js\";\nimport { extend, isCallable } from \"./utils.js\";\nimport render from \"./render.js\";\n\nlet _undefined;\nconst eventTypes = [ \"selectionchange\", \"mouseup\", \"touchend\", \"touchcancel\" ];\n\nexport default (opts) => {\n const options = (Object.assign || extend)({\n document,\n selector: \"body\",\n sharers: [],\n popoverClass: \"share-this-popover\",\n transformer: raw => raw.trim().replace(/\\s+/g, \" \")\n }, opts || {});\n\n let initialized = false;\n let destroyed = false;\n\n let _document = _undefined;\n let _window = _undefined;\n\n let popover = _undefined;\n let lifeCycle = _undefined;\n\n return {\n init() {\n if (initialized) return false;\n\n _document = options.document;\n _window = _document.defaultView;\n if (!_window.getSelection) {\n // eslint-disable-next-line no-console\n console.warn(\"share-this: Selection API isn't supported\");\n return false;\n }\n\n eventTypes.forEach(addListener);\n _window.addEventListener(\"resize\", resizeHandler);\n\n lifeCycle = lifeCycleFactory(_document);\n\n return initialized = true;\n },\n destroy() {\n if (!initialized || destroyed) return false;\n\n eventTypes.forEach(removeListener);\n _window.removeEventListener(\"resize\", resizeHandler);\n\n killPopover();\n\n _document = _undefined;\n _window = _undefined;\n\n return destroyed = true;\n },\n reposition() {\n if (popover) {\n stylePopover(popover, getConstrainedRange(), options);\n }\n return !!popover;\n }\n };\n\n function addListener(type) { _document.addEventListener(type, selectionCheck); }\n function removeListener(type) { _document.removeEventListener(type, selectionCheck); }\n function resizeHandler() {\n if (popover) {\n stylePopover(popover, getConstrainedRange(), options);\n }\n }\n\n function selectionCheck({ type }) {\n const shouldHavePopover = type === \"selectionchange\";\n if (!popover !== shouldHavePopover) {\n // Safari iOS fires selectionchange *before* click, so tapping on a sharer would be prevented\n setTimeout(() => {\n if (_window) {\n const range = getConstrainedRange();\n if (range) drawPopover(range);\n else killPopover();\n }\n }, 10);\n }\n }\n\n function getConstrainedRange() {\n const selection = _window.getSelection();\n const range = selection.rangeCount && selection.getRangeAt(0);\n if (!range) return;\n\n const constrainedRange = constrainRange(range, options.selector);\n if (constrainedRange.collapsed || !constrainedRange.getClientRects().length) return;\n\n // eslint-disable-next-line consistent-return\n return constrainedRange;\n }\n\n function drawPopover(range) {\n const toBeOpened = !popover;\n const rawText = range.toString();\n const text = options.transformer(rawText);\n const sharers = options.sharers.filter(sharerCheck.bind(null, text, rawText));\n\n if (!sharers.length) {\n if (popover) killPopover();\n return;\n }\n if (toBeOpened) popover = lifeCycle.createPopover();\n\n popover.sharers = sharers;\n popover.innerHTML = render(options, sharers, text, rawText);\n stylePopover(popover, range, options);\n\n if (!toBeOpened) return;\n\n lifeCycle.attachPopover(popover);\n\n if (isCallable(options.onOpen)) {\n options.onOpen(popover, text, rawText);\n }\n }\n\n function killPopover() {\n if (!popover) return;\n\n lifeCycle.removePopover(popover);\n popover = _undefined;\n if (isCallable(options.onClose)) {\n options.onClose();\n }\n }\n\n function sharerCheck(text, rawText, sharer) {\n const active = sharer.active;\n if (isCallable(active)) {\n return active(text, rawText);\n }\n\n if (active !== _undefined) {\n return active;\n }\n\n return true;\n }\n};\n","export default (options, sharers, text, rawText) => {\n const refUrl = options.shareUrl || options.document.defaultView.location;\n\n // eslint-disable-next-line prefer-template\n return \"