hypothesis 1.1069.0 → 1.1072.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/boot.js +1 -1
- package/build/manifest.json +12 -12
- package/build/scripts/annotator.bundle.js +68 -68
- package/build/scripts/annotator.bundle.js.map +1 -1
- package/build/scripts/sidebar.bundle.js +428 -428
- package/build/scripts/sidebar.bundle.js.map +1 -1
- package/build/scripts/ui-playground.bundle.js +125 -123
- package/build/scripts/ui-playground.bundle.js.map +1 -1
- package/build/styles/annotator.css +118 -9
- package/build/styles/annotator.css.map +1 -1
- package/build/styles/sidebar.css +186 -133
- package/build/styles/sidebar.css.map +1 -1
- package/build/styles/ui-playground.css +142 -17
- package/build/styles/ui-playground.css.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotator.bundle.js","sources":["app:///node_modules/focus-visible/dist/focus-visible.js","app:///node_modules/preact/dist/preact.mjs","app:///node_modules/preact/devtools/dist/devtools.mjs","app:///node_modules/preact/debug/dist/debug.mjs","app:///node_modules/classnames/index.js","app:///node_modules/preact/hooks/dist/hooks.mjs","app:///node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs","app:///node_modules/@hypothesis/frontend-shared/lib/components/SvgIcon.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/buttons.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Checkbox.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Dialog.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Icon.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Link.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Panel.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Spinner.js","app:///src/annotator/icons.js","app:///src/shared/listener-collection.js","app:///src/shared/random.js","app:///src/shared/messaging/port-util.js","app:///src/shared/messaging/port-finder.js","app:///node_modules/tiny-emitter/index.js","app:///src/shared/frame-error-capture.js","app:///src/shared/messaging/port-provider.js","app:///src/shared/messaging/port-rpc.js","app:///src/boot/parse-json-config.js","app:///src/shared/type-coercions.js","app:///src/annotator/config/url-from-link-tag.js","app:///src/annotator/config/settings.js","app:///src/annotator/config/config-func-settings-from.js","app:///src/annotator/config/index.js","app:///src/annotator/config/is-browser-extension.js","app:///src/shared/shortcut.js","app:///node_modules/@hypothesis/frontend-shared/lib/browser-compatibility-utils.js","app:///src/annotator/components/AdderToolbar.js","app:///src/shared/user-agent.js","app:///src/annotator/util/shadow-root.js","app:///src/annotator/adder.js","app:///src/annotator/anchoring/text-range.js","app:///src/annotator/anchoring/placeholder.js","app:///src/annotator/range-util.js","app:///src/annotator/highlighter.js","app:///src/annotator/bucket-bar-client.js","app:///src/annotator/util/buckets.js","app:///src/shared/warn-once.js","app:///src/annotator/features.js","app:///node_modules/approx-string-match/build/src/index.js","app:///src/annotator/anchoring/match-quote.js","app:///src/annotator/anchoring/xpath.js","app:///src/annotator/anchoring/types.js","app:///src/annotator/anchoring/html.js","app:///src/annotator/util/url.js","app:///src/annotator/integrations/html-metadata.js","app:///src/annotator/util/geometry.js","app:///src/annotator/integrations/html-side-by-side.js","app:///node_modules/scroll-into-view/scrollIntoView.js","app:///src/annotator/util/scroll.js","app:///src/annotator/integrations/html.js","app:///node_modules/lodash.debounce/index.js","app:///src/annotator/util/normalize.js","app:///src/annotator/anchoring/pdf.js","app:///src/annotator/components/Banners.js","app:///src/annotator/components/ContentPartnerBanner.js","app:///src/annotator/components/WarningBanner.js","app:///src/annotator/integrations/pdf-metadata.js","app:///src/annotator/integrations/pdf.js","app:///src/annotator/frame-observer.js","app:///src/annotator/integrations/image-text-layer.js","app:///src/annotator/hypothesis-injector.js","app:///src/annotator/integrations/vitalsource.js","app:///src/annotator/integrations/index.js","app:///src/annotator/selection-observer.js","app:///src/annotator/guest.js","app:///src/annotator/util/frame.js","app:///src/shared/config-fragment.js","app:///src/annotator/config/app.js","app:///src/annotator/components/NotebookModal.js","app:///src/annotator/notebook.js","app:///node_modules/hammerjs/hammer.js","app:///src/annotator/components/Buckets.js","app:///src/annotator/bucket-bar.js","app:///src/annotator/components/Toolbar.js","app:///src/annotator/toolbar.js","app:///src/annotator/sidebar.js","app:///src/annotator/annotation-counts.js","app:///src/annotator/sidebar-trigger.js","app:///src/annotator/util/emitter.js","app:///src/annotator/index.js"],"sourcesContent":["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on <html> whenever the\n // window blurs, even if you're tabbing out of the page. ¯\\_(ツ)_/¯\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n","var n,l,u,i,t,o,r,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function h(n){var l=n.parentNode;l&&l.removeChild(n)}function v(l,u,i){var t,o,r,f={};for(r in u)\"key\"==r?t=u[r]:\"ref\"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),\"function\"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return y(l,f,t,o,null)}function y(n,i,t,o,r){var f={type:n,props:i,key:t,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null==r&&null!=l.vnode&&l.vnode(f),f}function p(){return{current:null}}function d(n){return n.children}function _(n,l){this.props=n,this.context=l}function k(n,l){if(null==l)return n.__?k(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return\"function\"==typeof n.type?k(n):null}function b(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return b(n)}}function m(n){(!n.__d&&(n.__d=!0)&&t.push(n)&&!g.__r++||r!==l.debounceRendering)&&((r=l.debounceRendering)||o)(g)}function g(){for(var n;g.__r=t.length;)n=t.sort(function(n,l){return n.__v.__b-l.__v.__b}),t=[],n.some(function(n){var l,u,i,t,o,r;n.__d&&(o=(t=(l=n).__v).__e,(r=l.__P)&&(u=[],(i=a({},t)).__v=t.__v+1,j(r,t,i,l.__n,void 0!==r.ownerSVGElement,null!=t.__h?[o]:null,u,null==o?k(t):o,t.__h),z(u,t),t.__e!=o&&b(t)))})}function w(n,l,u,i,t,o,r,f,s,a){var h,v,p,_,b,m,g,w=i&&i.__k||c,A=w.length;for(u.__k=[],h=0;h<l.length;h++)if(null!=(_=u.__k[h]=null==(_=l[h])||\"boolean\"==typeof _?null:\"string\"==typeof _||\"number\"==typeof _||\"bigint\"==typeof _?y(null,_,null,null,_):Array.isArray(_)?y(d,{children:_},null,null,null):_.__b>0?y(_.type,_.props,_.key,null,_.__v):_)){if(_.__=u,_.__b=u.__b+1,null===(p=w[h])||p&&_.key==p.key&&_.type===p.type)w[h]=void 0;else for(v=0;v<A;v++){if((p=w[v])&&_.key==p.key&&_.type===p.type){w[v]=void 0;break}p=null}j(n,_,p=p||e,t,o,r,f,s,a),b=_.__e,(v=_.ref)&&p.ref!=v&&(g||(g=[]),p.ref&&g.push(p.ref,null,_),g.push(v,_.__c||b,_)),null!=b?(null==m&&(m=b),\"function\"==typeof _.type&&_.__k===p.__k?_.__d=s=x(_,s,n):s=P(n,_,p,w,b,s),\"function\"==typeof u.type&&(u.__d=s)):s&&p.__e==s&&s.parentNode!=n&&(s=k(p))}for(u.__e=m,h=A;h--;)null!=w[h]&&(\"function\"==typeof u.type&&null!=w[h].__e&&w[h].__e==u.__d&&(u.__d=k(i,h+1)),N(w[h],w[h]));if(g)for(h=0;h<g.length;h++)M(g[h],g[++h],g[++h])}function x(n,l,u){for(var i,t=n.__k,o=0;t&&o<t.length;o++)(i=t[o])&&(i.__=n,l=\"function\"==typeof i.type?x(i,l,u):P(u,i,i,t,i.__e,l));return l}function A(n,l){return l=l||[],null==n||\"boolean\"==typeof n||(Array.isArray(n)?n.some(function(n){A(n,l)}):l.push(n)),l}function P(n,l,u,i,t,o){var r,f,e;if(void 0!==l.__d)r=l.__d,l.__d=void 0;else if(null==u||t!=o||null==t.parentNode)n:if(null==o||o.parentNode!==n)n.appendChild(t),r=null;else{for(f=o,e=0;(f=f.nextSibling)&&e<i.length;e+=2)if(f==t)break n;n.insertBefore(t,o),r=o}return void 0!==r?r:t.nextSibling}function C(n,l,u,i,t){var o;for(o in u)\"children\"===o||\"key\"===o||o in l||H(n,o,null,u[o],i);for(o in l)t&&\"function\"!=typeof l[o]||\"children\"===o||\"key\"===o||\"value\"===o||\"checked\"===o||u[o]===l[o]||H(n,o,l[o],u[o],i)}function $(n,l,u){\"-\"===l[0]?n.setProperty(l,u):n[l]=null==u?\"\":\"number\"!=typeof u||s.test(l)?u:u+\"px\"}function H(n,l,u,i,t){var o;n:if(\"style\"===l)if(\"string\"==typeof u)n.style.cssText=u;else{if(\"string\"==typeof i&&(n.style.cssText=i=\"\"),i)for(l in i)u&&l in u||$(n.style,l,\"\");if(u)for(l in u)i&&u[l]===i[l]||$(n.style,l,u[l])}else if(\"o\"===l[0]&&\"n\"===l[1])o=l!==(l=l.replace(/Capture$/,\"\")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=u,u?i||n.addEventListener(l,o?T:I,o):n.removeEventListener(l,o?T:I,o);else if(\"dangerouslySetInnerHTML\"!==l){if(t)l=l.replace(/xlink(H|:h)/,\"h\").replace(/sName$/,\"s\");else if(\"href\"!==l&&\"list\"!==l&&\"form\"!==l&&\"tabIndex\"!==l&&\"download\"!==l&&l in n)try{n[l]=null==u?\"\":u;break n}catch(n){}\"function\"==typeof u||(null!=u&&(!1!==u||\"a\"===l[0]&&\"r\"===l[1])?n.setAttribute(l,u):n.removeAttribute(l))}}function I(n){this.l[n.type+!1](l.event?l.event(n):n)}function T(n){this.l[n.type+!0](l.event?l.event(n):n)}function j(n,u,i,t,o,r,f,e,c){var s,h,v,y,p,k,b,m,g,x,A,P,C,$=u.type;if(void 0!==u.constructor)return null;null!=i.__h&&(c=i.__h,e=u.__e=i.__e,u.__h=null,r=[e]),(s=l.__b)&&s(u);try{n:if(\"function\"==typeof $){if(m=u.props,g=(s=$.contextType)&&t[s.__c],x=s?g?g.props.value:s.__:t,i.__c?b=(h=u.__c=i.__c).__=h.__E:(\"prototype\"in $&&$.prototype.render?u.__c=h=new $(m,x):(u.__c=h=new _(m,x),h.constructor=$,h.render=O),g&&g.sub(h),h.props=m,h.state||(h.state={}),h.context=x,h.__n=t,v=h.__d=!0,h.__h=[]),null==h.__s&&(h.__s=h.state),null!=$.getDerivedStateFromProps&&(h.__s==h.state&&(h.__s=a({},h.__s)),a(h.__s,$.getDerivedStateFromProps(m,h.__s))),y=h.props,p=h.state,v)null==$.getDerivedStateFromProps&&null!=h.componentWillMount&&h.componentWillMount(),null!=h.componentDidMount&&h.__h.push(h.componentDidMount);else{if(null==$.getDerivedStateFromProps&&m!==y&&null!=h.componentWillReceiveProps&&h.componentWillReceiveProps(m,x),!h.__e&&null!=h.shouldComponentUpdate&&!1===h.shouldComponentUpdate(m,h.__s,x)||u.__v===i.__v){h.props=m,h.state=h.__s,u.__v!==i.__v&&(h.__d=!1),h.__v=u,u.__e=i.__e,u.__k=i.__k,u.__k.forEach(function(n){n&&(n.__=u)}),h.__h.length&&f.push(h);break n}null!=h.componentWillUpdate&&h.componentWillUpdate(m,h.__s,x),null!=h.componentDidUpdate&&h.__h.push(function(){h.componentDidUpdate(y,p,k)})}if(h.context=x,h.props=m,h.__v=u,h.__P=n,A=l.__r,P=0,\"prototype\"in $&&$.prototype.render)h.state=h.__s,h.__d=!1,A&&A(u),s=h.render(h.props,h.state,h.context);else do{h.__d=!1,A&&A(u),s=h.render(h.props,h.state,h.context),h.state=h.__s}while(h.__d&&++P<25);h.state=h.__s,null!=h.getChildContext&&(t=a(a({},t),h.getChildContext())),v||null==h.getSnapshotBeforeUpdate||(k=h.getSnapshotBeforeUpdate(y,p)),C=null!=s&&s.type===d&&null==s.key?s.props.children:s,w(n,Array.isArray(C)?C:[C],u,i,t,o,r,f,e,c),h.base=u.__e,u.__h=null,h.__h.length&&f.push(h),b&&(h.__E=h.__=null),h.__e=!1}else null==r&&u.__v===i.__v?(u.__k=i.__k,u.__e=i.__e):u.__e=L(i.__e,u,i,t,o,r,f,c);(s=l.diffed)&&s(u)}catch(n){u.__v=null,(c||null!=r)&&(u.__e=e,u.__h=!!c,r[r.indexOf(e)]=null),l.__e(n,u,i)}}function z(n,u){l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function L(l,u,i,t,o,r,f,c){var s,a,v,y=i.props,p=u.props,d=u.type,_=0;if(\"svg\"===d&&(o=!0),null!=r)for(;_<r.length;_++)if((s=r[_])&&\"setAttribute\"in s==!!d&&(d?s.localName===d:3===s.nodeType)){l=s,r[_]=null;break}if(null==l){if(null===d)return document.createTextNode(p);l=o?document.createElementNS(\"http://www.w3.org/2000/svg\",d):document.createElement(d,p.is&&p),r=null,c=!1}if(null===d)y===p||c&&l.data===p||(l.data=p);else{if(r=r&&n.call(l.childNodes),a=(y=i.props||e).dangerouslySetInnerHTML,v=p.dangerouslySetInnerHTML,!c){if(null!=r)for(y={},_=0;_<l.attributes.length;_++)y[l.attributes[_].name]=l.attributes[_].value;(v||a)&&(v&&(a&&v.__html==a.__html||v.__html===l.innerHTML)||(l.innerHTML=v&&v.__html||\"\"))}if(C(l,p,y,o,c),v)u.__k=[];else if(_=u.props.children,w(l,Array.isArray(_)?_:[_],u,i,t,o&&\"foreignObject\"!==d,r,f,r?r[0]:i.__k&&k(i,0),c),null!=r)for(_=r.length;_--;)null!=r[_]&&h(r[_]);c||(\"value\"in p&&void 0!==(_=p.value)&&(_!==l.value||\"progress\"===d&&!_||\"option\"===d&&_!==y.value)&&H(l,\"value\",_,y.value,!1),\"checked\"in p&&void 0!==(_=p.checked)&&_!==l.checked&&H(l,\"checked\",_,y.checked,!1))}return l}function M(n,u,i){try{\"function\"==typeof n?n(u):n.current=u}catch(n){l.__e(n,i)}}function N(n,u,i){var t,o;if(l.unmount&&l.unmount(n),(t=n.ref)&&(t.current&&t.current!==n.__e||M(t,null,u)),null!=(t=n.__c)){if(t.componentWillUnmount)try{t.componentWillUnmount()}catch(n){l.__e(n,u)}t.base=t.__P=null}if(t=n.__k)for(o=0;o<t.length;o++)t[o]&&N(t[o],u,\"function\"!=typeof n.type);i||null==n.__e||h(n.__e),n.__e=n.__d=void 0}function O(n,l,u){return this.constructor(n,u)}function S(u,i,t){var o,r,f;l.__&&l.__(u,i),r=(o=\"function\"==typeof t)?null:t&&t.__k||i.__k,f=[],j(i,u=(!o&&t||i).__k=v(d,null,[u]),r||e,e,void 0!==i.ownerSVGElement,!o&&t?[t]:r?null:i.firstChild?n.call(i.childNodes):null,f,!o&&t?t:r?r.__e:i.firstChild,o),z(f,u)}function q(n,l){S(n,l,q)}function B(l,u,i){var t,o,r,f=a({},l.props);for(r in u)\"key\"==r?t=u[r]:\"ref\"==r?o=u[r]:f[r]=u[r];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),y(l.type,f,t||l.key,o||l.ref,null)}function D(n,l){var u={__c:l=\"__cC\"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,i;return this.getChildContext||(u=[],(i={})[l]=this,this.getChildContext=function(){return i},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(m)},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=c.slice,l={__e:function(n,l,u,i){for(var t,o,r;l=l.__;)if((t=l.__c)&&!t.__)try{if((o=t.constructor)&&null!=o.getDerivedStateFromError&&(t.setState(o.getDerivedStateFromError(n)),r=t.__d),null!=t.componentDidCatch&&(t.componentDidCatch(n,i||{}),r=t.__d),r)return t.__E=t}catch(l){n=l}throw n}},u=0,i=function(n){return null!=n&&void 0===n.constructor},_.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=a({},this.state),\"function\"==typeof n&&(n=n(a({},u),this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),m(this))},_.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),m(this))},_.prototype.render=d,t=[],o=\"function\"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,g.__r=0,f=0;export{S as render,q as hydrate,v as createElement,v as h,d as Fragment,p as createRef,i as isValidElement,_ as Component,B as cloneElement,D as createContext,A as toChildArray,l as options};\n//# sourceMappingURL=preact.module.js.map\n","import{options as n,Fragment as o,Component as e}from\"preact\";function t(o,e){return n.__a&&n.__a(e),o}\"undefined\"!=typeof window&&window.__PREACT_DEVTOOLS__&&window.__PREACT_DEVTOOLS__.attachPreact(\"10.8.1\",n,{Fragment:o,Component:e});export{t as addHookName};\n//# sourceMappingURL=devtools.module.js.map\n","import{options as n,Fragment as t,Component as e}from\"preact\";import\"preact/devtools\";var o={};function r(){o={}}function a(n){return n.type===t?\"Fragment\":\"function\"==typeof n.type?n.type.displayName||n.type.name:\"string\"==typeof n.type?n.type:\"#text\"}var i=[],s=[];function c(){return i.length>0?i[i.length-1]:null}var l=!1;function u(n){return\"function\"==typeof n.type&&n.type!=t}function f(n){for(var t=[n],e=n;null!=e.__o;)t.push(e.__o),e=e.__o;return t.reduce(function(n,t){n+=\" in \"+a(t);var e=t.__source;return e?n+=\" (at \"+e.fileName+\":\"+e.lineNumber+\")\":l||(l=!0,console.warn(\"Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.\")),n+\"\\n\"},\"\")}var p=\"function\"==typeof WeakMap,d=e.prototype.setState;e.prototype.setState=function(n,t){return null==this.__v?null==this.state&&console.warn('Calling \"this.setState\" inside the constructor of a component is a no-op and might be a bug in your application. Instead, set \"this.state = {}\" directly.\\n\\n'+f(c())):null==this.__P&&console.warn('Can\\'t call \"this.setState\" on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.\\n\\n'+f(this.__v)),d.call(this,n,t)};var h=e.prototype.forceUpdate;function y(n){var t=n.props,e=a(n),o=\"\";for(var r in t)if(t.hasOwnProperty(r)&&\"children\"!==r){var i=t[r];\"function\"==typeof i&&(i=\"function \"+(i.displayName||i.name)+\"() {}\"),i=Object(i)!==i||i.toString?i+\"\":Object.prototype.toString.call(i),o+=\" \"+r+\"=\"+JSON.stringify(i)}var s=t.children;return\"<\"+e+o+(s&&s.length?\">..</\"+e+\">\":\" />\")}e.prototype.forceUpdate=function(n){return null==this.__v?console.warn('Calling \"this.forceUpdate\" inside the constructor of a component is a no-op and might be a bug in your application.\\n\\n'+f(c())):null==this.__P&&console.warn('Can\\'t call \"this.forceUpdate\" on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.\\n\\n'+f(this.__v)),h.call(this,n)},function(){!function(){var t=n.__b,e=n.diffed,o=n.__,r=n.vnode,a=n.__r;n.diffed=function(n){u(n)&&s.pop(),i.pop(),e&&e(n)},n.__b=function(n){u(n)&&i.push(n),t&&t(n)},n.__=function(n,t){s=[],o&&o(n,t)},n.vnode=function(n){n.__o=s.length>0?s[s.length-1]:null,r&&r(n)},n.__r=function(n){u(n)&&s.push(n),a&&a(n)}}();var t=!1,e=n.__b,r=n.diffed,c=n.vnode,l=n.__e,d=n.__,h=n.__h,m=p?{useEffect:new WeakMap,useLayoutEffect:new WeakMap,lazyPropTypes:new WeakMap}:null,v=[];n.__e=function(n,t,e,o){if(t&&t.__c&&\"function\"==typeof n.then){var r=n;n=new Error(\"Missing Suspense. The throwing component was: \"+a(t));for(var i=t;i;i=i.__)if(i.__c&&i.__c.__c){n=r;break}if(n instanceof Error)throw n}try{(o=o||{}).componentStack=f(t),l(n,t,e,o),\"function\"!=typeof n.then&&setTimeout(function(){throw n})}catch(n){throw n}},n.__=function(n,t){if(!t)throw new Error(\"Undefined parent passed to render(), this is the second argument.\\nCheck if the element is available in the DOM/has the correct id.\");var e;switch(t.nodeType){case 1:case 11:case 9:e=!0;break;default:e=!1}if(!e){var o=a(n);throw new Error(\"Expected a valid HTML node as a second argument to render.\\tReceived \"+t+\" instead: render(<\"+o+\" />, \"+t+\");\")}d&&d(n,t)},n.__b=function(n){var r=n.type,i=function n(t){return t?\"function\"==typeof t.type?n(t.__):t:{}}(n.__);if(t=!0,void 0===r)throw new Error(\"Undefined component passed to createElement()\\n\\nYou likely forgot to export your component or might have mixed up default and named imports\"+y(n)+\"\\n\\n\"+f(n));if(null!=r&&\"object\"==typeof r){if(void 0!==r.__k&&void 0!==r.__e)throw new Error(\"Invalid type passed to createElement(): \"+r+\"\\n\\nDid you accidentally pass a JSX literal as JSX twice?\\n\\n let My\"+a(n)+\" = \"+y(r)+\";\\n let vnode = <My\"+a(n)+\" />;\\n\\nThis usually happens when you export a JSX literal and not the component.\\n\\n\"+f(n));throw new Error(\"Invalid type passed to createElement(): \"+(Array.isArray(r)?\"array\":r))}if(\"thead\"!==r&&\"tfoot\"!==r&&\"tbody\"!==r||\"table\"===i.type?\"tr\"===r&&\"thead\"!==i.type&&\"tfoot\"!==i.type&&\"tbody\"!==i.type&&\"table\"!==i.type?console.error(\"Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot/table> parent.\"+y(n)+\"\\n\\n\"+f(n)):\"td\"===r&&\"tr\"!==i.type?console.error(\"Improper nesting of table. Your <td> should have a <tr> parent.\"+y(n)+\"\\n\\n\"+f(n)):\"th\"===r&&\"tr\"!==i.type&&console.error(\"Improper nesting of table. Your <th> should have a <tr>.\"+y(n)+\"\\n\\n\"+f(n)):console.error(\"Improper nesting of table. Your <thead/tbody/tfoot> should have a <table> parent.\"+y(n)+\"\\n\\n\"+f(n)),void 0!==n.ref&&\"function\"!=typeof n.ref&&\"object\"!=typeof n.ref&&!(\"$$typeof\"in n))throw new Error('Component\\'s \"ref\" property should be a function, or an object created by createRef(), but got ['+typeof n.ref+\"] instead\\n\"+y(n)+\"\\n\\n\"+f(n));if(\"string\"==typeof n.type)for(var s in n.props)if(\"o\"===s[0]&&\"n\"===s[1]&&\"function\"!=typeof n.props[s]&&null!=n.props[s])throw new Error(\"Component's \\\"\"+s+'\" property should be a function, but got ['+typeof n.props[s]+\"] instead\\n\"+y(n)+\"\\n\\n\"+f(n));if(\"function\"==typeof n.type&&n.type.propTypes){if(\"Lazy\"===n.type.displayName&&m&&!m.lazyPropTypes.has(n.type)){var c=\"PropTypes are not supported on lazy(). Use propTypes on the wrapped component itself. \";try{var l=n.type();m.lazyPropTypes.set(n.type,!0),console.warn(c+\"Component wrapped in lazy() is \"+a(l))}catch(n){console.warn(c+\"We will log the wrapped component's name once it is loaded.\")}}var u=n.props;n.type.__f&&delete(u=function(n,t){for(var e in t)n[e]=t[e];return n}({},u)).ref,function(n,t,e,r,a){Object.keys(n).forEach(function(e){var i;try{i=n[e](t,e,r,\"prop\",null,\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\")}catch(n){i=n}!i||i.message in o||(o[i.message]=!0,console.error(\"Failed prop type: \"+i.message+(a&&\"\\n\"+a()||\"\")))})}(n.type.propTypes,u,0,a(n),function(){return f(n)})}e&&e(n)},n.__h=function(n,e,o){if(!n||!t)throw new Error(\"Hook can only be invoked from render methods.\");h&&h(n,e,o)};var b=function(n,t){return{get:function(){var e=\"get\"+n+t;v&&v.indexOf(e)<0&&(v.push(e),console.warn(\"getting vnode.\"+n+\" is deprecated, \"+t))},set:function(){var e=\"set\"+n+t;v&&v.indexOf(e)<0&&(v.push(e),console.warn(\"setting vnode.\"+n+\" is not allowed, \"+t))}}},w={nodeName:b(\"nodeName\",\"use vnode.type\"),attributes:b(\"attributes\",\"use vnode.props\"),children:b(\"children\",\"use vnode.props.children\")},g=Object.create({},w);n.vnode=function(n){var t=n.props;if(null!==n.type&&null!=t&&(\"__source\"in t||\"__self\"in t)){var e=n.props={};for(var o in t){var r=t[o];\"__source\"===o?n.__source=r:\"__self\"===o?n.__self=r:e[o]=r}}n.__proto__=g,c&&c(n)},n.diffed=function(n){if(n.__k&&n.__k.forEach(function(t){if(t&&void 0===t.type){delete t.__,delete t.__b;var e=Object.keys(t).join(\",\");throw new Error(\"Objects are not valid as a child. Encountered an object with the keys {\"+e+\"}.\\n\\n\"+f(n))}}),t=!1,r&&r(n),null!=n.__k)for(var e=[],o=0;o<n.__k.length;o++){var a=n.__k[o];if(a&&null!=a.key){var i=a.key;if(-1!==e.indexOf(i)){console.error('Following component has two or more children with the same key attribute: \"'+i+'\". This may cause glitches and misbehavior in rendering process. Component: \\n\\n'+y(n)+\"\\n\\n\"+f(n));break}e.push(i)}}}}();export{r as resetPropWarnings};\n//# sourceMappingURL=debug.module.js.map\n","/*!\n Copyright (c) 2018 Jed Watson.\n Licensed under the MIT License (MIT), see\n http://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames() {\n\t\tvar classes = [];\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (!arg) continue;\n\n\t\t\tvar argType = typeof arg;\n\n\t\t\tif (argType === 'string' || argType === 'number') {\n\t\t\t\tclasses.push(arg);\n\t\t\t} else if (Array.isArray(arg)) {\n\t\t\t\tif (arg.length) {\n\t\t\t\t\tvar inner = classNames.apply(null, arg);\n\t\t\t\t\tif (inner) {\n\t\t\t\t\t\tclasses.push(inner);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (argType === 'object') {\n\t\t\t\tif (arg.toString === Object.prototype.toString) {\n\t\t\t\t\tfor (var key in arg) {\n\t\t\t\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\t\t\t\tclasses.push(key);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tclasses.push(arg.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn classes.join(' ');\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import{options as n}from\"preact\";var t,u,r,o,i=0,c=[],f=n.__b,e=n.__r,a=n.diffed,v=n.__c,l=n.unmount;function m(t,r){n.__h&&n.__h(u,t,i||r),i=0;var o=u.__H||(u.__H={__:[],__h:[]});return t>=o.__.length&&o.__.push({}),o.__[t]}function d(n){return i=1,p(z,n)}function p(n,r,o){var i=m(t++,2);return i.t=n,i.__c||(i.__=[o?o(r):z(void 0,r),function(n){var t=i.t(i.__[0],n);i.__[0]!==t&&(i.__=[t,i.__[1]],i.__c.setState({}))}],i.__c=u),i.__}function y(r,o){var i=m(t++,3);!n.__s&&w(i.__H,o)&&(i.__=r,i.u=o,u.__H.__h.push(i))}function h(r,o){var i=m(t++,4);!n.__s&&w(i.__H,o)&&(i.__=r,i.u=o,u.__h.push(i))}function s(n){return i=5,A(function(){return{current:n}},[])}function _(n,t,u){i=6,h(function(){return\"function\"==typeof n?(n(t()),function(){return n(null)}):n?(n.current=t(),function(){return n.current=null}):void 0},null==u?u:u.concat(n))}function A(n,u){var r=m(t++,7);return w(r.__H,u)?(r.o=n(),r.u=u,r.__h=n,r.o):r.__}function F(n,t){return i=8,A(function(){return n},t)}function T(n){var r=u.context[n.__c],o=m(t++,9);return o.c=n,r?(null==o.__&&(o.__=!0,r.sub(u)),r.props.value):n.__}function q(t,u){n.useDebugValue&&n.useDebugValue(u?u(t):t)}function x(n){var r=m(t++,10),o=d();return r.__=n,u.componentDidCatch||(u.componentDidCatch=function(n){r.__&&r.__(n),o[1](n)}),[o[0],function(){o[1](void 0)}]}function b(){for(var t;t=c.shift();)if(t.__P)try{t.__H.__h.forEach(j),t.__H.__h.forEach(k),t.__H.__h=[]}catch(u){t.__H.__h=[],n.__e(u,t.__v)}}n.__b=function(n){u=null,f&&f(n)},n.__r=function(n){e&&e(n),t=0;var o=(u=n.__c).__H;o&&(r===u?(o.__h=[],u.__h=[],o.__.forEach(function(n){n.o=n.u=void 0})):(o.__.forEach(function(n){n.u&&(n.__H=n.u),n.o&&(n.__=n.o),n.o=n.u=void 0}),o.__h.forEach(j),o.__h.forEach(k),o.__h=[])),r=u},n.diffed=function(t){a&&a(t);var i=t.__c;i&&i.__H&&i.__H.__h.length&&(1!==c.push(i)&&o===n.requestAnimationFrame||((o=n.requestAnimationFrame)||function(n){var t,u=function(){clearTimeout(r),g&&cancelAnimationFrame(t),setTimeout(n)},r=setTimeout(u,100);g&&(t=requestAnimationFrame(u))})(b)),u=null,r=null},n.__c=function(t,u){u.some(function(t){try{t.__H&&t.__H.__.forEach(function(n){n.u&&(n.__H=n.u),n.o&&(n.__=n.o),n.o=n.u=void 0}),t.__h.forEach(j),t.__h=t.__h.filter(function(n){return!n.__||k(n)})}catch(r){u.some(function(n){n.__h&&(n.__h=[])}),u=[],n.__e(r,t.__v)}}),v&&v(t,u)},n.unmount=function(t){l&&l(t);var u,r=t.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{j(n)}catch(n){u=n}}),u&&n.__e(u,r.__v))};var g=\"function\"==typeof requestAnimationFrame;function j(n){var t=u,r=n.__c;\"function\"==typeof r&&(n.__c=void 0,r()),u=t}function k(n){var t=u;n.__c=n.__(),u=t}function w(n,t){return!n||n.length!==t.length||t.some(function(t,u){return t!==n[u]})}function z(n,t){return\"function\"==typeof t?t(n):t}export{d as useState,p as useReducer,y as useEffect,h as useLayoutEffect,s as useRef,_ as useImperativeHandle,A as useMemo,F as useCallback,T as useContext,q as useDebugValue,x as useErrorBoundary};\n//# sourceMappingURL=hooks.module.js.map\n","import{options as r,Fragment as _}from\"preact\";export{Fragment}from\"preact\";var o=0;function e(_,e,n,t,f){var l,s,u={};for(s in e)\"ref\"==s?l=e[s]:u[s]=e[s];var a={type:_,props:u,key:n,ref:l,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:--o,__source:f,__self:t};if(\"function\"==typeof _&&(l=_.defaultProps))for(s in l)void 0===u[s]&&(u[s]=l[s]);return r.vnode&&r.vnode(a),a}export{e as jsx,e as jsxs,e as jsxDEV};\n//# sourceMappingURL=jsxRuntime.module.js.map\n","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/SvgIcon.js\";\nimport classnames from 'classnames';\nimport { useLayoutEffect, useRef } from 'preact/hooks';\n/**\n * Object mapping icon names to SVG markup.\n *\n * @typedef {Map<string|symbol, string>} IconMap\n */\n\n/**\n * @template T\n * @typedef {import(\"preact/hooks\").Ref<T>} Ref\n */\n\n/**\n * Map of icon name to SVG data.\n *\n * @type {IconMap}\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst iconRegistry = new Map();\n/**\n * @typedef SvgIconProps\n * @prop {string|symbol} name - The name of the icon to display.\n * The name must match a name that has already been registered using the\n * `registerIcon` or `registerIcons` functions.\n * @prop {string} [className] - A CSS class to apply to the `<svg>` element.\n * @prop {boolean} [inline] - Apply a style allowing for inline display of icon wrapper.\n * @prop {string} [title] - Optional title attribute to apply to the SVG's containing `span`.\n */\n\n/**\n * Component that renders icons using inline `<svg>` elements.\n * This enables their appearance to be customized via CSS.\n *\n * This matches the way we do icons on the website, see\n * https://github.com/hypothesis/h/pull/3675\n *\n * @param {SvgIconProps} props\n */\n\nexport function SvgIcon({\n name,\n className = '',\n inline = false,\n title = ''\n}) {\n const markup = iconRegistry.get(name);\n\n if (!markup) {\n throw new Error(`Icon \"${name.toString()}\" is not registered`);\n }\n\n const element =\n /** @type {{ current: HTMLElement }} */\n useRef();\n useLayoutEffect(() => {\n const svg = element.current.querySelector('svg'); // The icon should always contain an `<svg>` element, but check here as we\n // don't validate the markup when it is registered.\n\n if (svg) {\n svg.setAttribute('class', className);\n }\n }, [className, // `markup` is a dependency of this effect because the SVG is replaced if\n // it changes.\n markup]);\n const spanProps = {};\n\n if (title) {\n spanProps.title = title;\n }\n\n return _jsxDEV(\"span\", {\n className: classnames('Hyp-SvgIcon', {\n 'Hyp-SvgIcon--inline': inline\n }),\n dangerouslySetInnerHTML: {\n __html: markup\n },\n ref: element,\n ...spanProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 69,\n columnNumber: 5\n }, this);\n}\n/**\n * Register an icon for use with the `SvgIcon` component.\n *\n * Returns a symbol that can be passed as the `name` prop to `SvgIcon` in order\n * to render this icon.\n *\n * @param {string|symbol} name - A name for this icon\n * @param {string} markup - SVG markup for the icon\n * @return {symbol}\n */\n\nexport function registerIcon(name, markup) {\n const key = typeof name === 'string' ? Symbol(name) : name;\n iconRegistry.set(key, markup);\n return key;\n}\n/**\n * Register icons for use with the `SvgIcon` component.\n *\n * @deprecated Prefer the `registerIcon` function instead which will return a\n * key that does not conflict with existing icons.\n *\n * @param {Record<string, string>} icons\n * @param {Object} options\n * @param {boolean} [options.reset] - If `true`, remove existing registered icons.\n */\n\nexport function registerIcons(icons, {\n reset = false\n} = {}) {\n if (reset) {\n iconRegistry.clear();\n }\n\n for (let [key, value] of Object.entries(icons)) {\n iconRegistry.set(key, value);\n }\n}\n/**\n * Return the currently available icons.\n *\n * To register icons, don't mutate this directly but call `registerIcons`\n * instead.\n *\n * @return {IconMap}\n */\n\nexport function availableIcons() {\n return iconRegistry;\n}\n//# sourceMappingURL=SvgIcon.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/buttons.js\";\nimport classnames from 'classnames';\nimport { SvgIcon } from './SvgIcon';\n/**\n * @typedef ButtonProps\n * @prop {import('preact').Ref<HTMLButtonElement>} [buttonRef]\n * @prop {string} [classes] - Optional CSS class name(s) to use _in addition_\n * to the button component's own clases\n * @prop {string} [className] - Optional CSS class name that will _replace_\n * the button component's own classes\n * @prop {string|symbol} [icon] - Name of `SvgIcon` to render in the button\n * @prop {'left'|'right'} [iconPosition] - Icon positioned to left or to\n * right of button text\n * @prop {boolean} [expanded] - Is the element associated with this button\n * expanded (set `aria-expanded`)\n * @prop {never} [aria-expanded] - Use `expanded` prop instead\n * @prop {boolean} [pressed] - Is this button currently \"active?\" (set\n * `aria-pressed` or `aria-selected` depending on button `role`)\n * @prop {never} [aria-pressed] - Use `pressed` prop instead\n * @prop {'small'|'medium'|'large'} [size='medium'] - Relative button size:\n * affects padding\n * @prop {string} [title] - Button title; used for `aria-label` attribute\n * @prop {never} [aria-label] - Use `title` prop instead\n * @prop {'normal'|'primary'|'light'|'dark'} [variant='normal'] - For styling: element variant\n */\n\n/**\n * Fold in HTML button prop definitions into ButtonProps, but ignore `size` because it's inherited\n * from HTMLElement and conflicts with the _ButtonProps.size prop above. Ignore `icon`\n * because it is typed to `string` only and we need to be able to accept {string|symbol}\n *\n * @typedef {Omit<import('preact').JSX.HTMLAttributes<HTMLButtonElement>, 'size' | 'icon'> } HTMLButtonElementProps\n * @typedef {ButtonProps & HTMLButtonElementProps} ButtonBaseProps\n */\n\n/**\n * @typedef IconButtonBaseProps\n * @prop {string|symbol} icon - Icon is required for icon buttons\n * @prop {string} title - Title is required for icon buttons\n * @prop {never} [children] - children are not allowed (use LabeledButton instead)\n */\n\n/**\n * @typedef {ButtonBaseProps & IconButtonBaseProps} IconButtonProps\n */\n\n/**\n * @param {ButtonBaseProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n\nfunction ButtonBase({\n // Custom props.\n buttonRef,\n classes,\n className,\n icon,\n iconPosition = 'left',\n size = 'medium',\n variant = 'normal',\n expanded,\n pressed,\n // Standard <button> props.\n type = 'button',\n ...restProps\n}) {\n var _restProps$role;\n\n const role = (_restProps$role = restProps === null || restProps === void 0 ? void 0 : restProps.role) !== null && _restProps$role !== void 0 ? _restProps$role : 'button';\n const ariaProps = {\n 'aria-label': restProps.title\n }; // aria-pressed and aria-expanded are not allowed for buttons with\n // an aria role of `tab`. Instead, the aria-selected attribute is expected.\n\n if (role === 'tab') {\n ariaProps['aria-selected'] = pressed;\n } else {\n ariaProps['aria-pressed'] = pressed;\n ariaProps['aria-expanded'] = expanded;\n }\n\n return _jsxDEV(\"button\", {\n ref: buttonRef,\n className: classnames(className, `${className}--${size}`, `${className}--${variant}`, {\n [`${className}--icon-${iconPosition}`]: icon\n }, classes),\n type: type,\n ...ariaProps,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 82,\n columnNumber: 5\n }, this);\n}\n/**\n * An icon-only button\n *\n * @param {IconButtonProps} props\n */\n\n\nexport function IconButton({\n className = 'Hyp-IconButton',\n ...restProps\n}) {\n const {\n icon\n } = restProps;\n return _jsxDEV(ButtonBase, {\n className: className,\n ...restProps,\n children: _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 109,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 108,\n columnNumber: 5\n }, this);\n}\n/**\n * A labeled button, with or without an icon\n *\n * @param {ButtonBaseProps} props\n */\n\nexport function LabeledButton({\n children,\n className = 'Hyp-LabeledButton',\n ...restProps\n}) {\n const {\n icon,\n iconPosition = 'left'\n } = restProps;\n return _jsxDEV(ButtonBase, {\n className: className,\n ...restProps,\n children: [icon && iconPosition === 'left' && _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 127,\n columnNumber: 43\n }, this), children, icon && iconPosition === 'right' && _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 129,\n columnNumber: 44\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 126,\n columnNumber: 5\n }, this);\n}\n/**\n * A button styled to appear as an HTML link (<a>)\n *\n * @param {ButtonBaseProps} props\n */\n\nexport function LinkButton(props) {\n return _jsxDEV(ButtonBase, {\n className: \"Hyp-LinkButton\",\n ...props\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 140,\n columnNumber: 10\n }, this);\n}\n//# sourceMappingURL=buttons.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Checkbox.js\";\n// @ts-ignore\nimport checkboxSVG from '../../images/icons/checkbox.svg';\nimport classnames from 'classnames';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the checkbox icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nimport { Fragment as _Fragment } from \"preact/jsx-dev-runtime\";\nconst checkboxIcon = registerIcon('checkbox', checkboxSVG);\n/**\n * @typedef CheckboxBaseProps\n * @prop {string} [classes] - Additional CSS classes to apply to the <input>\n * @prop {string} name - The `name` of the checkbox.\n * @prop {import('preact').Ref<HTMLInputElement>} [inputRef] - Access to the input\n * element in case a parent element wants for example to focus on it.\n * @prop {(checked: boolean) => void} [onToggle] - Callback when checkbox is\n * checked/unchecked\n * @prop {never} [type] - Type is always 'checkbox'\n * @prop {never} [children] - Children are not allowed\n *\n * The props for Checkbox component extends and narrows the attributes of the native input element.\n * `onToggle` event should only be associated to HTMLDetailsElement, but Preact is not very strict with types.\n * We omit the `onToggle` because it clashes with our definition.\n * @typedef {Omit<import('preact').JSX.HTMLAttributes<HTMLInputElement>, 'onToggle'> & CheckboxBaseProps} CheckboxProps\n */\n\n/**\n * @typedef LabeledCheckboxBaseProps\n * @prop {import('preact').ComponentChildren} children - Label text or elements\n * @prop {string} [containerClasses] - Optional additional classes for the container\n * <label> element\n *\n * @typedef {Omit<CheckboxProps, 'children'> & LabeledCheckboxBaseProps} LabeledCheckboxProps\n */\n\n/**\n * A checkbox input.\n *\n * A checkbox component is a combination of an <input> element and a sibling\n * <svg> element that is used for the visual appearance of the checkbox.\n *\n * @param {CheckboxProps} props\n */\n\nexport function Checkbox({\n classes = '',\n inputRef,\n onToggle,\n onClick,\n ...restProps\n}) {\n /**\n * @param {import('preact').JSX.TargetedMouseEvent<HTMLInputElement>} event\n * @this HTMLInputElement\n */\n function onPressed(event) {\n onToggle === null || onToggle === void 0 ? void 0 : onToggle(event.currentTarget.checked); // preact event handlers expects `this` context to be of type `never`\n // https://github.com/preactjs/preact/issues/3137\n\n onClick === null || onClick === void 0 ? void 0 : onClick.call(\n /** @type {never} */\n this, event);\n }\n\n return _jsxDEV(_Fragment, {\n children: [_jsxDEV(\"input\", {\n className: classnames('Hyp-Checkbox', classes),\n ref: inputRef,\n type: \"checkbox\",\n onClick: onPressed,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 64,\n columnNumber: 7\n }, this), _jsxDEV(SvgIcon, {\n className: \"hyp-svg-checkbox\",\n name: checkboxIcon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 71,\n columnNumber: 7\n }, this)]\n }, void 0, true);\n}\n/**\n * A labeled checkbox input\n *\n * @param {LabeledCheckboxProps} props\n */\n\nexport function LabeledCheckbox({\n children,\n id,\n containerClasses = '',\n ...restProps\n}) {\n var _id;\n\n (_id = id) !== null && _id !== void 0 ? _id : id = restProps.name;\n return _jsxDEV(\"label\", {\n htmlFor: id,\n className: classnames('Hyp-LabeledCheckbox', containerClasses),\n children: [_jsxDEV(Checkbox, {\n id: id,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 93,\n columnNumber: 7\n }, this), _jsxDEV(\"span\", {\n \"data-testid\": \"label-text\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 94,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 89,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Checkbox.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Dialog.js\";\nimport classnames from 'classnames';\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; // @ts-ignore\n\nimport cancelSVG from '../../images/icons/cancel.svg';\nimport { IconButton, LabeledButton } from './buttons';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the checkbox icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst cancelIcon = registerIcon('cancel', cancelSVG);\nlet idCounter = 0;\n/**\n * Return an element ID beginning with `prefix` that is unique per component instance.\n *\n * This avoids different instances of a component re-using the same ID.\n *\n * @param {string} prefix\n */\n\nfunction useUniqueId(prefix) {\n const [id] = useState(() => {\n ++idCounter;\n return `${prefix}-${idCounter}`;\n });\n return id;\n}\n/**\n * @typedef {import('preact').ComponentChildren} Children\n *\n * @typedef DialogProps\n * @prop {Children} [buttons] -\n * Additional `Button` elements to display at the bottom of the dialog.\n * A \"Cancel\" button is added automatically if the `onCancel` prop is set.\n * @prop {string} [cancelLabel] - Label for the cancel button\n * @prop {Children} children\n * @prop {string} [contentClass] - CSS class to apply to the dialog's content\n * @prop {string|symbol} [icon] - Name of optional icon to render in header\n * @prop {import(\"preact/hooks\").Ref<HTMLElement>|null} [initialFocus] -\n * Child element to focus when the dialog is rendered. If not provided,\n * the Dialog's container will be automatically focused on opening. Set to\n * `null` to opt out of automatic focus control.\n * @prop {() => void} [onCancel] -\n * A callback to invoke when the user cancels the dialog. If provided, a\n * \"Cancel\" button will be displayed.\n * @prop {'dialog'|'alertdialog'} [role] - The aria role for the dialog (defaults to\" dialog\")\n * @prop {string} title\n * @prop {boolean} [withCancelButton=true] - If `onCancel` is provided, render\n * a Cancel button as one of the Dialog's buttons (along with any other\n * `buttons`)\n * @prop {boolean} [withCloseButton=true] - If `onCancel` is provided, render\n * a close button (X icon) in the Dialog's header\n */\n\n/**\n * HTML control that can be disabled.\n *\n * @typedef {HTMLElement & { disabled: boolean }} InputElement\n */\n\n/**\n * Render a \"panel\"-like interface with a title and optional icon and/or\n * close button. Grabs focus on initial render, defaulting to the entire\n * Dialog container element, or `initialFocus` HTMLElement if provided.\n *\n * @param {DialogProps} props\n */\n\n\nexport function Dialog({\n buttons,\n cancelLabel = 'Cancel',\n children,\n contentClass,\n icon,\n initialFocus,\n onCancel,\n role = 'dialog',\n title,\n withCancelButton = true,\n withCloseButton = true\n}) {\n const dialogDescriptionId = useUniqueId('dialog-description');\n const dialogTitleId = useUniqueId('dialog-title');\n const rootEl =\n /** @type {{ current: HTMLDivElement }} */\n useRef();\n useEffect(() => {\n // Setting `initialFocus` to `null` opts out of focus handling\n if (initialFocus !== null) {\n const focusEl =\n /** @type {InputElement|null} */\n initialFocus === null || initialFocus === void 0 ? void 0 : initialFocus.current;\n\n if (focusEl && !focusEl.disabled) {\n focusEl.focus();\n } else {\n // The `initialFocus` prop has not been set, so use automatic focus handling.\n // Modern accessibility guidance is to focus the dialog itself rather than\n // trying to be smart about focusing a particular control within the\n // dialog.\n rootEl.current.focus();\n }\n } // We only want to run this effect once when the dialog is mounted.\n //\n // eslint-disable-next-line react-hooks/exhaustive-deps\n\n }, []); // Try to assign the dialog an accessible description, using the content of\n // the first paragraph of text in it.\n //\n // A limitation of this approach is that it doesn't update if the dialog's\n // content changes after the initial render.\n\n useLayoutEffect(() => {\n const description = rootEl.current.querySelector('p');\n\n if (description) {\n description.id = dialogDescriptionId;\n rootEl.current.setAttribute('aria-describedby', dialogDescriptionId);\n }\n }, [dialogDescriptionId]);\n const hasCancelButton = onCancel && withCancelButton;\n const hasCloseButton = onCancel && withCloseButton;\n const hasButtons = buttons || hasCancelButton;\n return _jsxDEV(\"div\", {\n \"aria-labelledby\": dialogTitleId,\n className: classnames('Hyp-Dialog', {\n 'Hyp-Dialog--closeable': hasCloseButton\n }, contentClass),\n ref: rootEl,\n role: role,\n tabIndex: -1,\n children: [_jsxDEV(\"header\", {\n className: \"Hyp-Dialog__header\",\n children: [icon && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__header-icon\",\n children: _jsxDEV(SvgIcon, {\n name: icon,\n title: title,\n \"data-testid\": \"header-icon\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 139,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 138,\n columnNumber: 11\n }, this), _jsxDEV(\"h2\", {\n className: \"Hyp-Dialog__title\",\n id: dialogTitleId,\n children: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 142,\n columnNumber: 9\n }, this), onCancel && withCloseButton && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__close\",\n children: _jsxDEV(IconButton, {\n \"data-testid\": \"close-button\",\n icon: cancelIcon,\n title: \"Close\",\n onClick: onCancel\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 147,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 146,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 136,\n columnNumber: 7\n }, this), children, hasButtons && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__actions\",\n children: [hasCancelButton && _jsxDEV(LabeledButton, {\n \"data-testid\": \"cancel-button\",\n onClick: onCancel,\n children: cancelLabel\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 160,\n columnNumber: 13\n }, this), buttons]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 158,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 125,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Dialog.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Icon.js\";\nimport classnames from 'classnames';\nimport { useLayoutEffect, useRef } from 'preact/hooks';\nimport { availableIcons } from './SvgIcon';\n/**\n * @template T\n * @typedef {import(\"preact/hooks\").Ref<T>} Ref\n */\n\n/**\n * @typedef IconProps\n * @prop {string|symbol} name - The name of the icon to display.\n * The name must match a name that has already been registered using the\n * `registerIcon` or `registerIcons` functions.\n * @prop {string} [classes] - CSS classes to apply to the `<svg>` element.\n * @prop {string} [containerClasses] - CSS classes to apply to wrapping element.\n * @prop {string} [title] - Optional title attribute to apply to the SVG's containing `span`.\n */\n\n/**\n * Component that renders icons using inline `<svg>` elements.\n *\n * @param {IconProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nexport function Icon({\n name,\n classes = '',\n containerClasses = '',\n title = ''\n}) {\n const registeredIcons = availableIcons();\n const markup = registeredIcons.get(name);\n\n if (!markup) {\n throw new Error(`Icon \"${name.toString()}\" is not registered`);\n }\n\n const element =\n /** @type {{ current: HTMLElement }} */\n useRef();\n useLayoutEffect(() => {\n const svg = element.current.querySelector('svg'); // The icon should always contain an `<svg>` element, but check here as we\n // don't validate the markup when it is registered.\n\n if (svg) {\n svg.setAttribute('class', classnames('Hyp-Icon', classes));\n }\n }, [classes, // `markup` is a dependency of this effect because the SVG is replaced if\n // it changes.\n markup]);\n const spanProps = {};\n\n if (title) {\n spanProps.title = title;\n }\n\n return _jsxDEV(\"span\", {\n className: containerClasses,\n dangerouslySetInnerHTML: {\n __html: markup\n },\n ref: element,\n ...spanProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 60,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Icon.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Link.js\";\nimport classnames from 'classnames';\n/**\n * @typedef {import('preact').ComponentChildren} Children\n *\n * @typedef LinkBaseProps\n * @prop {Children} children\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {import('preact').Ref<HTMLAnchorElement>} [linkRef] - Optional ref for\n * the rendered anchor element\n */\n\n/**\n * @typedef {LinkBaseProps & import('preact').JSX.HTMLAttributes<HTMLAnchorElement>} LinkProps\n */\n\n/**\n * Style and add some attributes to an anchor (`<a>`) element\n *\n * @param {LinkProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nexport function Link({\n children,\n classes = '',\n linkRef,\n ...restProps\n}) {\n return _jsxDEV(\"a\", {\n className: classnames('Hyp-Link', classes),\n ref: linkRef,\n rel: \"noopener noreferrer\",\n ...restProps,\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 24,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Link.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Panel.js\";\nimport classnames from 'classnames'; // @ts-ignore\n\nimport cancelSVG from '../../images/icons/cancel.svg';\nimport { IconButton } from './buttons';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the cancel icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst cancelIcon = registerIcon('cancel', cancelSVG);\n/**\n * @typedef PanelProps\n * @prop {import(\"preact\").ComponentChildren} children\n * @prop {string} [icon] - Name of optional icon to render in header\n * @prop {() => void} [onClose] - handler for closing the panel; if provided,\n * will render a close button that invokes this onClick\n * @prop {string} title\n */\n\n/**\n * Render a \"panel\"-like interface with a title and optional icon and/or\n * close button.\n *\n * @param {PanelProps} props\n */\n\nexport function Panel({\n children,\n icon,\n onClose,\n title\n}) {\n const withCloseButton = !!onClose;\n return _jsxDEV(\"div\", {\n className: classnames('Hyp-Panel', {\n 'Hyp-Panel--closeable': withCloseButton\n }),\n children: [_jsxDEV(\"header\", {\n className: \"Hyp-Panel__header\",\n children: [icon && _jsxDEV(\"div\", {\n className: \"Hyp-Panel__header-icon\",\n children: _jsxDEV(SvgIcon, {\n name: icon,\n title: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 37,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 36,\n columnNumber: 11\n }, this), _jsxDEV(\"h2\", {\n className: \"Hyp-Panel__title\",\n children: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 40,\n columnNumber: 9\n }, this), withCloseButton && _jsxDEV(\"div\", {\n className: \"Hyp-Panel__close\",\n children: _jsxDEV(IconButton, {\n icon: cancelIcon,\n title: \"Close\",\n onClick: onClose\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 43,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 42,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 34,\n columnNumber: 7\n }, this), _jsxDEV(\"div\", {\n className: \"Hyp-Panel__content\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 47,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 29,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Panel.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Spinner.js\";\nimport classnames from 'classnames'; // @ts-ignore\n\nimport spinnerSVG from '../../images/icons/spinner--spokes.svg';\nimport { Icon } from './Icon';\nimport { registerIcon } from './SvgIcon'; // Register the spinner icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst spinnerIcon = registerIcon('spinner', spinnerSVG);\n/**\n * @typedef SpinnerProps\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {'small'|'medium'|'large'} [size='medium'] - Relative size of spinner\n * to surrounding content\n */\n\n/**\n * @typedef FullScreenSpinnerProps\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {string} [containerClasses] - CSS classes to apply to wrapping element.\n */\n\n/**\n * Loading indicator.\n *\n * @param {SpinnerProps} props\n */\n\nexport function Spinner({\n classes = '',\n size = 'medium'\n}) {\n const baseClass = `Hyp-Spinner--${size}`;\n return _jsxDEV(Icon, {\n name: spinnerIcon,\n containerClasses: classnames(baseClass, classes)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 32,\n columnNumber: 5\n }, this);\n}\n/**\n * Full-screen loading indicator.\n *\n * @param {FullScreenSpinnerProps} props\n */\n\nexport function FullScreenSpinner({\n classes = '',\n containerClasses = ''\n}) {\n return _jsxDEV(\"div\", {\n className: classnames('Hyp-FullScreenSpinner', containerClasses),\n children: _jsxDEV(Spinner, {\n classes: classnames('Hyp-FullScreenSpinner__spinner', classes),\n size: \"large\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 47,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 46,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Spinner.js.map","// @ts-nocheck\n\nimport {\n cancel,\n caretLeft,\n caretRight,\n caution,\n hide,\n highlight,\n note,\n show,\n} from '@hypothesis/frontend-shared/lib/icons';\n\n// Different variant than shared icon\nimport annotateIcon from '../images/icons/annotate.svg';\n// Not in shared icon set\nimport jstor from '../images/icons/jstor.svg';\nimport pointerIcon from '../images/icons/pointer.svg';\n\n/**\n * Set of icons used by the annotator part of the application via the `Icon`\n * component.\n */\nexport const annotatorIcons = {\n annotate: annotateIcon,\n cancel,\n caution,\n 'caret-left': caretLeft,\n 'caret-right': caretRight,\n hide,\n highlight,\n jstor,\n note,\n pointer: pointerIcon,\n show,\n};\n","/**\n * @typedef Listener\n * @prop {EventTarget} eventTarget\n * @prop {string} eventType\n * @prop {(event: Event) => void} listener\n */\n\n/**\n * Return the event type that a listener will receive.\n *\n * For example `EventType<HTMLElement, 'keydown'>` evaluates to `KeyboardEvent`.\n *\n * The event type is extracted from the target's `on${Type}` property (eg.\n * `HTMLElement.onkeydown` here) If there is no such property, the type defaults\n * to `Event`.\n *\n * @template {EventTarget} Target\n * @template {string} Type\n * @typedef {`on${Type}` extends keyof Target ?\n * Target[`on${Type}`] extends ((...args: any[]) => void)|null ?\n * Parameters<NonNullable<Target[`on${Type}`]>>[0]\n * : Event : Event} EventType\n */\n\n/**\n * Utility that provides a way to conveniently remove a set of DOM event\n * listeners when they are no longer needed.\n */\nexport class ListenerCollection {\n constructor() {\n /** @type {Map<Symbol, Listener>} */\n this._listeners = new Map();\n }\n\n /**\n * Add a listener and return an ID that can be used to remove it later\n *\n * @template {string} Type\n * @template {EventTarget} Target\n * @param {Target} eventTarget\n * @param {Type} eventType\n * @param {(event: EventType<Target, Type>) => void} listener\n * @param {AddEventListenerOptions} [options]\n */\n add(eventTarget, eventType, listener, options) {\n eventTarget.addEventListener(\n eventType,\n /** @type {EventListener} */ (listener),\n options\n );\n const symbol = Symbol();\n this._listeners.set(symbol, {\n eventTarget,\n eventType,\n // eslint-disable-next-line object-shorthand\n listener: /** @type {EventListener} */ (listener),\n });\n return symbol;\n }\n\n /**\n * Remove a specific listener.\n *\n * @param {Symbol} listenerId\n */\n remove(listenerId) {\n const event = this._listeners.get(listenerId);\n if (event) {\n const { eventTarget, eventType, listener } = event;\n eventTarget.removeEventListener(eventType, listener);\n this._listeners.delete(listenerId);\n }\n }\n\n removeAll() {\n this._listeners.forEach(({ eventTarget, eventType, listener }) => {\n eventTarget.removeEventListener(eventType, listener);\n });\n this._listeners.clear();\n }\n}\n","/** @param {number} val */\nfunction byteToHex(val) {\n const str = val.toString(16);\n return str.length === 1 ? '0' + str : str;\n}\n\n/**\n * Generate a random hex string of `len` chars.\n *\n * @param {number} len - An even-numbered length string to generate.\n * @return {string}\n */\nexport function generateHexString(len) {\n const bytes = new Uint8Array(len / 2);\n window.crypto.getRandomValues(bytes);\n return Array.from(bytes).map(byteToHex).join('');\n}\n","/**\n * These types are the used in by `PortProvider` and `PortFinder` for the\n * inter-frame discovery and communication processes.\n *\n * @typedef {'guest'|'host'|'notebook'|'sidebar'} Frame\n *\n * @typedef Message\n * @prop {Frame} frame1\n * @prop {Frame} frame2\n * @prop {'offer'|'request'} type\n * @prop {string} requestId - ID of the request. Used to associate `offer`\n * responses with requests and enable PortProvider to ignore re-sent requests.\n */\n\n/**\n * Return true if an object, eg. from the data field of a `MessageEvent`, is a\n * valid `Message`.\n *\n * @param {any} data\n * @return {data is Message}\n */\nexport function isMessage(data) {\n if (data === null || typeof data !== 'object') {\n return false;\n }\n\n for (let property of ['frame1', 'frame2', 'type', 'requestId']) {\n if (typeof data[property] !== 'string') {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Return true if the data payload from a MessageEvent matches `message`.\n *\n * @param {any} data\n * @param {Partial<Message>} message\n */\nexport function isMessageEqual(data, message) {\n if (!isMessage(data)) {\n return false;\n }\n\n // We assume `JSON.stringify` cannot throw because `isMessage` verifies that\n // all the fields we serialize here are serializable values.\n return (\n JSON.stringify(data, Object.keys(message).sort()) ===\n JSON.stringify(message, Object.keys(message).sort())\n );\n}\n\n/**\n * Check that source is of type Window.\n *\n * @param {MessageEventSource|null} source\n * @return {source is Window}\n */\nexport function isSourceWindow(source) {\n if (\n // `source` can be of type Window, MessagePort, ServiceWorker, or null.\n // `source instanceof Window` doesn't work in Chrome if `source` is a\n // cross-origin window.\n source === null ||\n source instanceof MessagePort ||\n (window.ServiceWorker && source instanceof ServiceWorker)\n ) {\n return false;\n }\n\n return true;\n}\n","import { ListenerCollection } from '../listener-collection';\nimport { generateHexString } from '../random';\nimport { isMessageEqual } from './port-util';\n\n/** Timeout for waiting for the host frame to respond to a port request. */\nexport const MAX_WAIT_FOR_PORT = 1000 * 20;\n\n/** Polling interval for requests to the host frame for a port. */\nexport const POLLING_INTERVAL_FOR_PORT = 250;\n\n/**\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n * @typedef {import('./port-util').Message} Message\n * @typedef {import('./port-util').Frame} Frame\n */\n\n/**\n * PortFinder is used by non-host frames in the client to establish a\n * MessagePort-based connection to other frames. It is used together with\n * PortProvider which runs in the host frame. See PortProvider for an overview.\n *\n * @implements {Destroyable}\n */\nexport class PortFinder {\n /**\n * @param {object} options\n * @param {Exclude<Frame, 'host'>} options.source - the role of this frame\n * @param {Window} options.hostFrame - the frame where the `PortProvider` is\n * listening for messages.\n */\n constructor({ hostFrame, source }) {\n this._hostFrame = hostFrame;\n this._source = source;\n this._listeners = new ListenerCollection();\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Request a specific port from the host frame\n *\n * @param {Frame} target - the frame aiming to be discovered\n * @return {Promise<MessagePort>}\n */\n async discover(target) {\n let isValidRequest = false;\n if (\n (this._source === 'guest' && target === 'host') ||\n (this._source === 'guest' && target === 'sidebar') ||\n (this._source === 'sidebar' && target === 'host') ||\n (this._source === 'notebook' && target === 'sidebar')\n ) {\n isValidRequest = true;\n }\n\n if (!isValidRequest) {\n throw new Error('Invalid request of channel/port');\n }\n\n const requestId = generateHexString(6);\n\n return new Promise((resolve, reject) => {\n const postRequest = () => {\n this._hostFrame.postMessage(\n {\n frame1: this._source,\n frame2: target,\n type: 'request',\n requestId,\n },\n '*'\n );\n };\n\n // Because `guest` iframes load in parallel to the `host` frame, we can\n // not assume that the code in the `host` frame is executed before the\n // code in a `guest` frame. Hence, we can't assume that `PortProvider` (in\n // the `host` frame) is initialized before `PortFinder` (in the non-host\n // frames). Therefore, for the `PortFinder`, we implement a polling\n // strategy (sending a message every N milliseconds) until a response is\n // received.\n const intervalId = setInterval(postRequest, POLLING_INTERVAL_FOR_PORT);\n\n // The `host` frame maybe busy, that's why we should wait.\n const timeoutId = setTimeout(() => {\n clearInterval(intervalId);\n reject(\n new Error(\n `Unable to establish ${this._source}-${target} communication channel`\n )\n );\n }, MAX_WAIT_FOR_PORT);\n\n const listenerId = this._listeners.add(window, 'message', event => {\n const { data, ports } = event;\n if (\n isMessageEqual(data, {\n frame1: this._source,\n frame2: target,\n type: 'offer',\n requestId,\n })\n ) {\n clearInterval(intervalId);\n clearTimeout(timeoutId);\n this._listeners.remove(listenerId);\n resolve(ports[0]);\n }\n });\n\n postRequest();\n });\n }\n}\n","function E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n","/** @type {Window|null} */\nlet errorDestination = null;\n\n/**\n * Wrap a callback with an error handler which forwards errors to another frame\n * using {@link sendError}.\n *\n * @template {unknown[]} Args\n * @template Result\n * @param {(...args: Args) => Result} callback\n * @param {string} context - A short message indicating where the error happened.\n * @return {(...args: Args) => Result}\n */\nexport function captureErrors(callback, context) {\n return (...args) => {\n try {\n return callback(...args);\n } catch (err) {\n sendError(err, context);\n throw err;\n }\n };\n}\n\n/**\n * @typedef ErrorData\n * @prop {string} message\n * @prop {string} [stack]\n */\n\n/**\n * Return a cloneable representation of an Error.\n *\n * This is needed in browsers that don't support structured-cloning of Error\n * objects, or if the error is not cloneable for some reason.\n *\n * @param {Error|unknown} err\n * @return {ErrorData}\n */\nfunction serializeError(err) {\n if (!(err instanceof Error)) {\n return { message: String(err), stack: undefined };\n }\n\n return {\n message: err.message,\n stack: err.stack,\n };\n}\n\n/**\n * Convert error data serialized by {@link serializeError} back into an Error.\n *\n * @param {ErrorData} data\n * @return {Error}\n */\nfunction deserializeError(data) {\n const err = new Error(data.message);\n err.stack = data.stack;\n return err;\n}\n\n/**\n * Forward an error to the frame registered with {@link sendErrorsTo}.\n *\n * Errors are delivered on a best-effort basis. If no error handling frame has\n * been registered or the frame is still loading, the error will not be received.\n *\n * Ideally we would use a more robust delivery system which can queue messages\n * until they can be processed (eg. using MessagePort). We use `window.postMessage`\n * for the moment because we are trying to rule out problems with\n * MessageChannel/MessagePort when setting up sidebar <-> host communication.\n *\n * @param {unknown} error\n * @param {string} context - A short message indicating where the error happened.\n */\nexport function sendError(error, context) {\n if (!errorDestination) {\n return;\n }\n\n const data = {\n type: 'hypothesis-error',\n error: error instanceof Error ? error : serializeError(error),\n context,\n };\n\n try {\n // Try to send the error. If this fails because the browser doesn't support\n // structured cloning of errors, use a fallback.\n try {\n errorDestination.postMessage(data, '*');\n } catch (postErr) {\n if (\n postErr instanceof DOMException &&\n postErr.name === 'DataCloneError'\n ) {\n data.error = serializeError(data.error);\n errorDestination.postMessage(data, '*');\n } else {\n throw postErr;\n }\n }\n } catch (sendErr) {\n console.warn('Unable to report Hypothesis error', sendErr);\n }\n}\n\n/**\n * Register a handler for errors sent to the current frame using {@link sendError}\n *\n * @param {(error: unknown, context: string) => void} callback\n * @return {() => void} A function that unregisters the handler\n */\nexport function handleErrorsInFrames(callback) {\n /** @param {MessageEvent} event */\n const handleMessage = event => {\n const { data } = event;\n if (data && data?.type === 'hypothesis-error') {\n const { context, error } = data;\n callback(\n error instanceof Error ? error : deserializeError(error),\n context\n );\n }\n };\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n}\n\n/**\n * Register a destination frame that {@link sendError} should submit errors to.\n *\n * @param {Window|null} destination\n */\nexport function sendErrorsTo(destination) {\n errorDestination = destination;\n}\n","import { TinyEmitter } from 'tiny-emitter';\n\nimport { captureErrors, sendError } from '../frame-error-capture';\nimport { ListenerCollection } from '../listener-collection';\nimport { isMessage, isMessageEqual, isSourceWindow } from './port-util';\n\n/**\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n * @typedef {import('./port-util').Message} Message\n * @typedef {import('./port-util').Frame} Frame\n * @typedef {'guest-host'|'guest-sidebar'|'notebook-sidebar'|'sidebar-host'} Channel\n */\n\n/**\n * PortProvider creates a `MessageChannel` for communication between two\n * frames.\n *\n * There are 4 types of frames:\n * - `host`: frame where the Hypothesis client is initially loaded.\n * - `guest`: frames with annotatable content. In some instances a `guest`\n * frame can be the same as the `host` frame, in other cases, it is an iframe\n * where either (1) the hypothesis client has been injected or (2) the\n * hypothesis client has been configured to act exclusively as a `guest` (not\n * showing the sidebar).\n * - `notebook`: another hypothesis app that runs in a separate iframe.\n * - `sidebar`: the main hypothesis app. It runs in an iframe on a different\n * origin than the host and is responsible for the communication with the\n * backend (fetching and saving annotations).\n *\n * This layout represents the current arrangement of frames:\n *\n * `host` frame\n * |-> `sidebar` iframe\n * |-> `notebook` iframe\n * |-> [`guest` iframes]\n *\n * Currently, we support communication between the following pairs of frames:\n * - `guest-host`\n * - `guest-sidebar`\n * - `notebook-sidebar`\n * - `sidebar-host`\n *\n * `PortProvider` is used only in the `host` frame. The other frames use the\n * companion class, `PortFinder`. `PortProvider` creates a `MessageChannel`\n * for two frames to communicate with each other. It also listens to requests for\n * particular `MessagePort` and dispatches the corresponding `MessagePort`.\n *\n *\n * PortFinder (non-host frame) | PortProvider (host frame)\n * -----------------------------------------------------------------------------------------------\n * 1. Request `MessagePort` via `window.postMessage` ---> 2. Receive request and create port pair\n * |\n * V\n * 4. Receive requested port <---------------------- 3. Send first port to requesting frame\n * 5. Send second port to other frame\n * (eg. via MessageChannel connection\n * between host and other frame)\n *\n * @implements {Destroyable}\n */\nexport class PortProvider {\n /**\n * Begin listening to port requests from other frames.\n *\n * @param {string} hypothesisAppsOrigin - the origin of the hypothesis apps\n * is use to send the notebook and sidebar ports to only the frames that\n * match the origin.\n */\n constructor(hypothesisAppsOrigin) {\n this._hypothesisAppsOrigin = hypothesisAppsOrigin;\n this._emitter = new TinyEmitter();\n\n /**\n * IDs of port requests that have been handled.\n *\n * This is used to avoid responding to the same request multiple times.\n * Guest frames in particular may send duplicate requests (see comments in\n * PortFinder).\n *\n * @type {Set<string>}\n */\n this._handledRequests = new Set();\n\n // Create the `sidebar-host` channel immediately, while other channels are\n // created on demand\n this._sidebarHostChannel = new MessageChannel();\n\n this._listeners = new ListenerCollection();\n\n /** @type {Array<Partial<Message> & {allowedOrigin: string}>} */\n this._allowedMessages = [\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'sidebar',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'sidebar',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'notebook',\n frame2: 'sidebar',\n type: 'request',\n },\n ];\n\n this._listen();\n }\n\n _listen() {\n const errorContext = 'Handling port request';\n const sentErrors = /** @type {Set<string>} */ (new Set());\n\n /** @param {string} message */\n const reportError = message => {\n if (sentErrors.has(message)) {\n // PortFinder will send requests repeatedly until it gets a response or\n // a timeout is reached.\n //\n // Only send errors once to avoid spamming Sentry.\n return;\n }\n sentErrors.add(message);\n sendError(new Error(message), errorContext);\n };\n\n /** @param {MessageEvent} event */\n const handleRequest = event => {\n const { data, origin, source } = event;\n\n if (!isMessage(data) || data.type !== 'request') {\n // If this does not look like a message intended for us, ignore it.\n return;\n }\n\n const { frame1, frame2, requestId } = data;\n const channel = /** @type {Channel} */ (`${frame1}-${frame2}`);\n\n if (!isSourceWindow(source)) {\n reportError(\n `Ignored port request for channel ${channel} from non-Window source`\n );\n return;\n }\n\n const match = this._allowedMessages.find(\n ({ allowedOrigin, ...allowedMessage }) =>\n this._messageMatches({\n allowedMessage,\n allowedOrigin,\n data,\n origin,\n })\n );\n\n if (match === undefined) {\n reportError(\n `Ignored invalid port request for channel ${channel} from ${origin}`\n );\n return;\n }\n\n if (this._handledRequests.has(requestId)) {\n return;\n }\n this._handledRequests.add(requestId);\n\n // Create the channel for these two frames to communicate and send the\n // corresponding ports to them.\n const messageChannel =\n channel === 'sidebar-host'\n ? this._sidebarHostChannel\n : new MessageChannel();\n\n const message = { frame1, frame2, type: 'offer', requestId };\n\n // If the source window has an opaque origin [1], `event.origin` will be\n // the string \"null\". This is not a legal value for the `targetOrigin`\n // parameter to `postMessage`, so remap it to \"*\".\n //\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin.\n // Documents with opaque origins include file:// URLs and\n // sandboxed iframes.\n const targetOrigin = origin === 'null' ? '*' : origin;\n source.postMessage(message, targetOrigin, [messageChannel.port1]);\n\n if (frame2 === 'sidebar') {\n this._sidebarHostChannel.port2.postMessage(message, [\n messageChannel.port2,\n ]);\n } else if (frame2 === 'host') {\n this._emitter.emit('frameConnected', frame1, messageChannel.port2);\n }\n };\n\n this._listeners.add(\n window,\n 'message',\n captureErrors(handleRequest, errorContext)\n );\n }\n\n /**\n * @param {object} options\n * @param {Partial<Message>} options.allowedMessage - the `data` must match this\n * `Message`.\n * @param {string} options.allowedOrigin - the `origin` must match this\n * value. If `allowedOrigin` is '*', the origin is ignored.\n * @param {unknown} options.data - the data to be compared with `allowedMessage`.\n * @param {string} options.origin - the origin to be compared with\n * `allowedOrigin`.\n */\n _messageMatches({ allowedMessage, allowedOrigin, data, origin }) {\n if (allowedOrigin !== '*' && origin !== allowedOrigin) {\n return false;\n }\n\n return isMessageEqual(data, allowedMessage);\n }\n\n /**\n * @param {'frameConnected'} eventName\n * @param {(source: 'guest'|'sidebar', port: MessagePort) => void} handler - this handler\n * fires when a frame connects to the host frame\n */\n on(eventName, handler) {\n this._emitter.on(eventName, handler);\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n}\n","import { ListenerCollection } from '../listener-collection';\n\n/*\n This module was adapted from `index.js` in https://github.com/substack/frame-rpc.\n\n This software is released under the MIT license:\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nconst VERSION = '1.0.0';\nconst PROTOCOL = 'frame-rpc';\n\n/**\n * Format of messages sent between frames.\n *\n * See https://github.com/substack/frame-rpc#protocol\n *\n * @typedef RequestMessage\n * @prop {unknown[]} arguments\n * @prop {string} method\n * @prop {PROTOCOL} protocol\n * @prop {number} sequence\n * @prop {VERSION} version\n *\n * @typedef ResponseMessage\n * @prop {unknown[]} arguments\n * @prop {PROTOCOL} protocol\n * @prop {number} response\n * @prop {VERSION} version\n *\n * @typedef {RequestMessage|ResponseMessage} Message\n *\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Send a PortRPC method call.\n *\n * @param {MessagePort} port\n * @param {string} method\n * @param {unknown[]} [args]\n * @param {number} [sequence] - Sequence number used for replies\n */\nfunction sendCall(port, method, args = [], sequence = -1) {\n port.postMessage(\n /** @type {RequestMessage} */ ({\n protocol: PROTOCOL,\n version: VERSION,\n arguments: args,\n method,\n sequence,\n })\n );\n}\n\n/**\n * Install a message listener that ensures proper delivery of \"close\" notifications\n * from {@link PortRPC}s in Safari <= 15.\n *\n * This must be called in the _parent_ frame of the frame that owns the port.\n * In Hypothesis this means for example that the workaround would be installed\n * in the host frame to ensure delivery of \"close\" notifications from \"guest\"\n * frames.\n *\n * @param {string} [userAgent] - Test seam\n * @return {() => void} - Callback that removes the listener\n */\nexport function installPortCloseWorkaroundForSafari(\n /* istanbul ignore next */\n userAgent = navigator.userAgent\n) {\n if (!shouldUseSafariWorkaround(userAgent)) {\n return () => {};\n }\n\n /** @param {MessageEvent} event */\n const handler = event => {\n if (event.data?.type === 'hypothesisPortClosed' && event.ports[0]) {\n const port = event.ports[0];\n sendCall(port, 'close');\n port.close();\n }\n };\n\n window.addEventListener('message', handler);\n return () => window.removeEventListener('message', handler);\n}\n\n/**\n * Test whether this browser needs the workaround for https://bugs.webkit.org/show_bug.cgi?id=231167.\n *\n * @param {string} userAgent\n */\nfunction shouldUseSafariWorkaround(userAgent) {\n const webkitVersionMatch = userAgent.match(/\\bAppleWebKit\\/([0-9]+)\\b/);\n if (!webkitVersionMatch) {\n return false;\n }\n const version = parseInt(webkitVersionMatch[1]);\n\n // Chrome's user agent contains the token \"AppleWebKit/537.36\", where the\n // version number is frozen. This corresponds to a version of Safari from 2013,\n // which is older than all supported Safari versions.\n if (version <= 537) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Callback type used for RPC method handlers and result callbacks.\n *\n * @typedef {(...args: unknown[]) => void} Callback\n */\n\n/**\n * @param {any} value\n * @return {value is Callback}\n */\nfunction isCallback(value) {\n return typeof value === 'function';\n}\n\n/**\n * PortRPC provides remote procedure calls between frames or workers. It uses\n * the Channel Messaging API [1] as a transport.\n *\n * To communicate between two frames with this class, construct a PortRPC\n * instance in each and register method handlers with {@link on}. Create a\n * {@link MessageChannel} and send one of its two ports to each frame. Then call\n * {@link connect} to make the PortRPC instance in each frame use the corresponding\n * port.\n *\n * In addition to the custom methods that a PortRPC handles, there are several\n * built-in events which are sent automatically:\n *\n * - \"connect\" is sent when a PortRPC connects to a port. This event can\n * be used to confirm that the sending frame has received the port and will\n * send a \"close\" event when it goes away.\n * - \"close\" is sent when a PortRPC is destroyed or the owning frame is\n * unloaded. This event may not be dispatched if the guest frame terminates\n * abnormally (eg. due to an OS process crash).\n *\n * [1] https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API\n *\n * @template {string} OnMethod - Names of RPC methods this client responds to\n * @template {string} CallMethod - Names of RPC methods this client invokes\n * @implements {Destroyable}\n */\nexport class PortRPC {\n /**\n * @param {object} options\n * @param {string} [options.userAgent] - Test seam\n * @param {Window} [options.currentWindow] - Test seam\n */\n constructor({\n userAgent = navigator.userAgent,\n currentWindow = window,\n } = {}) {\n /** @type {MessagePort|null} */\n this._port = null;\n\n /** @type {Map<string, Callback>} */\n this._methods = new Map();\n\n this._sequence = 1;\n\n /** @type {Map<number, Callback>} */\n this._callbacks = new Map();\n\n this._listeners = new ListenerCollection();\n this._listeners.add(currentWindow, 'unload', () => {\n if (this._port) {\n // Send \"close\" notification directly. This works in Chrome, Firefox and\n // Safari >= 16.\n sendCall(this._port, 'close');\n\n // To work around a bug in Safari <= 15 which prevents sending messages\n // while a window is unloading, try transferring the port to the parent frame\n // and re-sending the \"close\" event from there.\n if (\n currentWindow !== currentWindow.parent &&\n shouldUseSafariWorkaround(userAgent)\n ) {\n currentWindow.parent.postMessage(\n { type: 'hypothesisPortClosed' },\n '*',\n [this._port]\n );\n }\n }\n });\n\n /**\n * Method and arguments of pending RPC calls made before a port was connected.\n *\n * @type {Array<[CallMethod, unknown[]]>}\n */\n this._pendingCalls = [];\n\n this._receivedCloseEvent = false;\n }\n\n /**\n * Register a method handler for incoming RPC requests.\n *\n * This can also be used to register a handler for the built-in \"connect\"\n * and \"close\" events.\n *\n * All handlers must be registered before {@link connect} is invoked.\n *\n * @template {function} Handler\n * @param {OnMethod|'close'|'connect'} method\n * @param {Handler} handler\n */\n on(method, handler) {\n if (this._port) {\n throw new Error('Cannot add a method handler after a port is connected');\n }\n this._methods.set(method, /** @type {any} */ (handler));\n }\n\n /**\n * Connect to a MessagePort and process any queued RPC requests.\n *\n * @param {MessagePort} port\n */\n connect(port) {\n this._port = port;\n this._listeners.add(port, 'message', event => this._handle(event));\n port.start();\n sendCall(port, 'connect');\n\n for (let [method, args] of this._pendingCalls) {\n this.call(method, ...args);\n }\n this._pendingCalls = [];\n }\n\n /**\n * Disconnect the RPC channel and close the MessagePort.\n */\n destroy() {\n if (this._port) {\n sendCall(this._port, 'close');\n this._port.close();\n }\n this._destroyed = true;\n this._listeners.removeAll();\n }\n\n /**\n * Send an RPC request via the connected port.\n *\n * If this client is not yet connected to a port, the call will be queued and\n * sent when {@link connect} is called.\n *\n * If the final argument in `args` is a function, it is treated as a callback\n * which is invoked with the response in the form of (error, result) arguments.\n *\n * @param {CallMethod} method\n * @param {unknown[]} args\n */\n call(method, ...args) {\n if (!this._port) {\n this._pendingCalls.push([method, args]);\n }\n\n if (!this._port || this._destroyed) {\n return;\n }\n\n const seq = this._sequence++;\n const finalArg = args[args.length - 1];\n if (isCallback(finalArg)) {\n this._callbacks.set(seq, finalArg);\n args = args.slice(0, -1);\n }\n\n sendCall(this._port, method, args, seq);\n }\n\n /**\n * Validate message\n *\n * @param {MessageEvent} event\n * @return {null|Message}\n */\n _parseMessage({ data }) {\n if (!data || typeof data !== 'object') {\n return null;\n }\n if (data.protocol !== PROTOCOL) {\n return null;\n }\n if (data.version !== VERSION) {\n return null;\n }\n if (!Array.isArray(data.arguments)) {\n return null;\n }\n\n return data;\n }\n\n /**\n * @param {MessageEvent} event\n */\n _handle(event) {\n const msg = this._parseMessage(event);\n const port = this._port;\n\n if (!msg || !port) {\n return;\n }\n\n if ('method' in msg) {\n // Only allow close event to fire once.\n if (msg.method === 'close') {\n if (this._receivedCloseEvent) {\n return;\n } else {\n this._receivedCloseEvent = true;\n }\n }\n\n const handler = this._methods.get(msg.method);\n if (!handler) {\n return;\n }\n\n /** @param {unknown[]} args */\n const callback = (...args) => {\n /** @type {ResponseMessage} */\n const message = {\n arguments: args,\n protocol: PROTOCOL,\n response: msg.sequence,\n version: VERSION,\n };\n\n port.postMessage(message);\n };\n handler(...msg.arguments, callback);\n } else if ('response' in msg) {\n const cb = this._callbacks.get(msg.response);\n this._callbacks.delete(msg.response);\n if (cb) {\n cb(...msg.arguments);\n }\n }\n }\n}\n","/**\n * `Object.assign()`-like helper. Used because this script needs to work\n * in IE 10/11 without polyfills.\n *\n * @param {Record<string, unknown>} dest\n * @param {Record<string, unknown>} src\n */\nfunction assign(dest, src) {\n for (const k in src) {\n if (src.hasOwnProperty(k)) {\n dest[k] = src[k];\n }\n }\n return dest;\n}\n\n/**\n * Return a parsed `js-hypothesis-config` object from the document, or `{}`.\n *\n * Find all `<script class=\"js-hypothesis-config\">` tags in the given document,\n * parse them as JSON, and return the parsed object.\n *\n * If there are no `js-hypothesis-config` tags in the document then return\n * `{}`.\n *\n * If there are multiple `js-hypothesis-config` tags in the document then merge\n * them into a single returned object (when multiple scripts contain the same\n * setting names, scripts further down in the document override those further\n * up).\n *\n * @param {Document|Element} document - The root element to search.\n */\nexport function parseJsonConfig(document) {\n /** @type {Record<string, unknown>} */\n const config = {};\n const settingsElements = document.querySelectorAll(\n 'script.js-hypothesis-config'\n );\n\n for (let i = 0; i < settingsElements.length; i++) {\n let settings;\n try {\n settings = JSON.parse(settingsElements[i].textContent || '');\n } catch (err) {\n console.warn(\n 'Could not parse settings from js-hypothesis-config tags',\n err\n );\n settings = {};\n }\n assign(config, settings);\n }\n\n return config;\n}\n","/**\n * Type conversion methods that coerce incoming configuration values to an\n * expected type or format that other parts of the UI may make assumptions\n * on. This is needed for incoming configuration values that are otherwise\n * not sanitized.\n *\n * Note that if the values passed are plain javascript values (such as ones\n * produced from JSON.parse), then these methods do not throw errors.\n */\n\n/**\n * Returns a boolean\n *\n * @param {any} value - initial value\n * @return {boolean}\n */\nexport function toBoolean(value) {\n if (typeof value === 'string') {\n if (value.trim().toLocaleLowerCase() === 'false') {\n // \"false\", \"False\", \" false\", \"FALSE\" all return false\n return false;\n }\n }\n const numericalVal = Number(value);\n if (!isNaN(numericalVal)) {\n return Boolean(numericalVal);\n }\n // Any non numerical or falsely string should return true, otherwise return false\n return typeof value === 'string';\n}\n\n/**\n * Returns either an integer or NaN\n *\n * @param {any} value - initial value\n * @return {number}\n */\nexport function toInteger(value) {\n // Acts as a simple wrapper\n return parseInt(value);\n}\n\n/**\n * Returns either the value if its an object or an empty object\n *\n * @param {any} value - initial value\n * @return {object}\n */\nexport function toObject(value) {\n if (typeof value === 'object' && value !== null) {\n return value;\n }\n // Don't attempt to fix the values, just ensure type correctness\n return {};\n}\n\n/**\n * Returns the value as a string or an empty string if the\n * value undefined, null or otherwise falsely.\n *\n * @param {any} value - initial value\n * @return {string}\n */\nexport function toString(value) {\n if (value && typeof value.toString === 'function') {\n return value.toString();\n }\n return '';\n}\n\n/**\n * @template T\n * @typedef {import('preact').Ref<T>} Ref\n */\n\n/**\n * Helper for downcasting a ref to a more specific type, where that is safe\n * to do.\n *\n * This is mainly useful to cast a generic `Ref<HTMLElement>` to a more specific\n * element type (eg. `Ref<HTMLDivElement>`) for use with the `ref` prop of a JSX element.\n * Since Preact only writes to the `ref` prop, such a cast is safe.\n *\n * @template T\n * @template {T} U\n * @param {Ref<T>|undefined} ref\n * @return {Ref<U>|undefined}\n */\nexport function downcastRef(ref) {\n return /** @type {Ref<U>|undefined} */ (ref);\n}\n","/**\n * Return the URL of a resource related to the Hypothesis client that has been stored in\n * the page via a `<link type=\"application/annotator+{type}\">` tag.\n *\n * These link tags are generally written to the page by the boot script, which may be executed\n * in a separate JavaScript realm (eg. in the browser extension), and thus can share information\n * with the annotator code via the DOM but not JS globals.\n *\n * @param {Window} window_\n * @param {string} rel - The `rel` attribute to match\n * @param {'javascript'|'html'} type - Type of resource that the link refers to\n * @throws {Error} - If there's no link with the `rel` indicated, or the first\n * matching link has no `href`\n */\nexport function urlFromLinkTag(window_, rel, type) {\n const link = /** @type {HTMLLinkElement} */ (\n window_.document.querySelector(\n `link[type=\"application/annotator+${type}\"][rel=\"${rel}\"]`\n )\n );\n\n if (!link) {\n throw new Error(\n `No application/annotator+${type} (rel=\"${rel}\") link in the document`\n );\n }\n\n if (!link.href) {\n throw new Error(\n `application/annotator+${type} (rel=\"${rel}\") link has no href`\n );\n }\n\n return link.href;\n}\n","import { parseJsonConfig } from '../../boot/parse-json-config';\nimport { toBoolean } from '../../shared/type-coercions';\n\nimport { configFuncSettingsFrom } from './config-func-settings-from';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\n/**\n * @typedef SettingsGetters\n * @prop {string|null} annotations\n * @prop {string|null} query\n * @prop {string|null} group\n * @prop {string} showHighlights\n * @prop {string} clientUrl\n * @prop {string} sidebarAppUrl\n * @prop {string} notebookAppUrl\n * @prop {(name: string) => unknown} hostPageSetting\n */\n\n/**\n * Discard a setting if it is not a string.\n *\n * @param {unknown} value\n */\nfunction checkIfString(value) {\n return typeof value === 'string' ? value : null;\n}\n\n/**\n * @param {Window} window_\n * @return {SettingsGetters}\n */\nexport function settingsFrom(window_) {\n // Prioritize the `window.hypothesisConfig` function over the JSON format\n // Via uses `window.hypothesisConfig` and makes it non-configurable and non-writable.\n // In addition, Via sets the `ignoreOtherConfiguration` option to prevent configuration merging.\n const configFuncSettings = configFuncSettingsFrom(window_);\n\n /** @type {Record<string, unknown>} */\n let jsonConfigs;\n if (toBoolean(configFuncSettings.ignoreOtherConfiguration)) {\n jsonConfigs = {};\n } else {\n jsonConfigs = parseJsonConfig(window_.document);\n }\n\n /**\n * Return the `#annotations:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:<ANNOTATION_ID>` fragment then return\n * the annotation ID extracted from the fragment. Otherwise return `null`.\n *\n * @return {string|null} - The extracted ID, or null.\n */\n function annotations() {\n /** Return the annotations from the URL, or null. */\n function annotationsFromURL() {\n // Annotation IDs are url-safe-base64 identifiers\n // See https://tools.ietf.org/html/rfc4648#page-7\n const annotFragmentMatch = window_.location.href.match(\n /#annotations:([A-Za-z0-9_-]+)$/\n );\n if (annotFragmentMatch) {\n return annotFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.annotations) || annotationsFromURL();\n }\n\n /**\n * Return the `#annotations:group:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:group:<GROUP_ID>` fragment then return\n * the group ID extracted from the fragment. Otherwise return `null`.\n *\n * @return {string|null} - The extracted ID, or null.\n */\n function group() {\n function groupFromURL() {\n const groupFragmentMatch = window_.location.href.match(\n /#annotations:group:([A-Za-z0-9_-]+)$/\n );\n if (groupFragmentMatch) {\n return groupFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.group) || groupFromURL();\n }\n\n function showHighlights() {\n const value = hostPageSetting('showHighlights');\n\n switch (value) {\n case 'always':\n case 'never':\n case 'whenSidebarOpen':\n return value;\n case true:\n return 'always';\n case false:\n return 'never';\n default:\n return 'always';\n }\n }\n\n /**\n * Return the config.query setting from the host page or from the URL.\n *\n * If the host page contains a js-hypothesis-config script containing a\n * query setting then return that.\n *\n * Otherwise if the host page's URL has a `#annotations:query:*` (or\n * `#annotations:q:*`) fragment then return the query value from that.\n *\n * Otherwise return null.\n *\n * @return {string|null} - The config.query setting, or null.\n */\n function query() {\n /** Return the query from the URL, or null. */\n function queryFromURL() {\n const queryFragmentMatch = window_.location.href.match(\n /#annotations:(query|q):(.+)$/i\n );\n if (queryFragmentMatch) {\n try {\n return decodeURIComponent(queryFragmentMatch[2]);\n } catch (err) {\n // URI Error should return the page unfiltered.\n }\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.query) || queryFromURL();\n }\n\n /**\n * Returns the first setting value found from the respective sources in order.\n *\n * 1. window.hypothesisConfig()\n * 2. <script class=\"js-hypothesis-config\">\n *\n * If the setting is not found in either source, then return undefined.\n *\n * @param {string} name - Unique name of the setting\n */\n function hostPageSetting(name) {\n if (configFuncSettings.hasOwnProperty(name)) {\n return configFuncSettings[name];\n }\n\n if (jsonConfigs.hasOwnProperty(name)) {\n return jsonConfigs[name];\n }\n\n return undefined;\n }\n\n return {\n get annotations() {\n return annotations();\n },\n get clientUrl() {\n return urlFromLinkTag(window_, 'hypothesis-client', 'javascript');\n },\n get group() {\n return group();\n },\n get notebookAppUrl() {\n return urlFromLinkTag(window_, 'notebook', 'html');\n },\n get showHighlights() {\n return showHighlights();\n },\n get sidebarAppUrl() {\n return urlFromLinkTag(window_, 'sidebar', 'html');\n },\n get query() {\n return query();\n },\n hostPageSetting,\n };\n}\n","/**\n * @typedef HypothesisWindowProps\n * @prop {() => Record<string, unknown>} [hypothesisConfig] - Function that returns configuration\n * for the Hypothesis client\n */\n\n/**\n * Return an object containing config settings from window.hypothesisConfig().\n *\n * Return an object containing config settings returned by the\n * window.hypothesisConfig() function provided by the host page:\n *\n * {\n * fooSetting: 'fooValue',\n * barSetting: 'barValue',\n * ...\n * }\n *\n * If there's no window.hypothesisConfig() function then return {}.\n *\n * @param {Window & HypothesisWindowProps} window_ - The window to search for a hypothesisConfig() function\n * @return {Record<string, unknown>} - Any config settings returned by hypothesisConfig()\n */\nexport function configFuncSettingsFrom(window_) {\n if (!window_.hasOwnProperty('hypothesisConfig')) {\n return {};\n }\n\n if (typeof window_.hypothesisConfig !== 'function') {\n const docs =\n 'https://h.readthedocs.io/projects/client/en/latest/publishers/config/#window.hypothesisConfig';\n console.warn('hypothesisConfig must be a function, see: ' + docs);\n return {};\n }\n\n return window_.hypothesisConfig();\n}\n","import { isBrowserExtension } from './is-browser-extension';\nimport { settingsFrom } from './settings';\nimport { toBoolean } from '../../shared/type-coercions';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\n/**\n * @typedef {import('./settings').SettingsGetters} SettingsGetters\n * @typedef {(settings: SettingsGetters, name: string) => any} ValueGetter\n *\n * @typedef ConfigDefinition\n * @prop {ValueGetter} getValue - Method to retrieve the value from the incoming source\n * @prop {boolean} allowInBrowserExt -\n * Allow this setting to be read in the browser extension. If this is false\n * and browser extension context is true, use `defaultValue` if provided otherwise\n * ignore the config key\n * @prop {any} [defaultValue] - Sets a default if `getValue` returns undefined\n * @prop {(value: any) => any} [coerce] - Transform a value's type, value or both\n *\n * @typedef {Record<string, ConfigDefinition>} ConfigDefinitionMap\n */\n\n/**\n * Named subset of the Hypothesis client configuration that is relevant in\n * a particular context.\n *\n * @typedef {'sidebar'|'notebook'|'annotator'|'all'} Context\n */\n\n/**\n * Returns the configuration keys that are relevant to a particular context.\n *\n * @param {Context} context\n */\nfunction configurationKeys(context) {\n const contexts = {\n annotator: ['clientUrl', 'contentPartner', 'subFrameIdentifier'],\n sidebar: [\n 'appType',\n 'annotations',\n 'branding',\n 'enableExperimentalNewNoteButton',\n 'externalContainerSelector',\n 'focus',\n 'group',\n 'onLayoutChange',\n 'openSidebar',\n 'query',\n 'requestConfigFromFrame',\n 'services',\n 'showHighlights',\n 'sidebarAppUrl',\n 'theme',\n 'usernameUrl',\n ],\n notebook: [\n 'branding',\n 'group',\n 'notebookAppUrl',\n 'requestConfigFromFrame',\n 'services',\n 'theme',\n 'usernameUrl',\n ],\n };\n\n switch (context) {\n case 'annotator':\n return contexts.annotator;\n case 'sidebar':\n return contexts.sidebar;\n case 'notebook':\n return contexts.notebook;\n case 'all':\n // Complete list of configuration keys used for testing.\n return [...contexts.annotator, ...contexts.sidebar, ...contexts.notebook];\n default:\n throw new Error(`Invalid application context used: \"${context}\"`);\n }\n}\n\n/** @type {ValueGetter} */\nfunction getHostPageSetting(settings, name) {\n return settings.hostPageSetting(name);\n}\n\n/**\n * Definitions of configuration keys\n *\n * @type {ConfigDefinitionMap}\n */\nconst configDefinitions = {\n annotations: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.annotations,\n },\n appType: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n branding: {\n defaultValue: null,\n allowInBrowserExt: false,\n getValue: getHostPageSetting,\n },\n // URL of the client's boot script. Used when injecting the client into\n // child iframes.\n clientUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.clientUrl,\n },\n contentPartner: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n enableExperimentalNewNoteButton: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n group: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.group,\n },\n focus: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n theme: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n usernameUrl: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n onLayoutChange: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n openSidebar: {\n allowInBrowserExt: true,\n defaultValue: false,\n coerce: toBoolean,\n getValue: getHostPageSetting,\n },\n query: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.query,\n },\n requestConfigFromFrame: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n services: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n showHighlights: {\n allowInBrowserExt: false,\n defaultValue: 'always',\n getValue: settings => settings.showHighlights,\n },\n notebookAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.notebookAppUrl,\n },\n sidebarAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.sidebarAppUrl,\n },\n // Sub-frame identifier given when a frame is being embedded into\n // by a top level client\n subFrameIdentifier: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n externalContainerSelector: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n};\n\n/**\n * Return the subset of Hypothesis client configuration that is relevant in\n * a particular context.\n *\n * See https://h.readthedocs.io/projects/client/en/latest/publishers/config/\n * for details of all available configuration and the different ways they\n * can be included on the page. In addition to the configuration provided by\n * the embedder, the boot script also passes some additional configuration\n * to the annotator, such as URLs of the various sub-applications and the\n * boot script itself.\n *\n * @param {Context} context\n */\nexport function getConfig(context, window_ = window) {\n const settings = settingsFrom(window_);\n\n /** @type {Record<string, unknown>} */\n const config = {};\n\n // Filter the config based on the application context as some config values\n // may be inappropriate or erroneous for some applications.\n for (let key of configurationKeys(context)) {\n const configDef = configDefinitions[key];\n const hasDefault = configDef.defaultValue !== undefined; // A default could be null\n const isURLFromBrowserExtension = isBrowserExtension(\n urlFromLinkTag(window_, 'sidebar', 'html')\n );\n\n // Only allow certain values in the browser extension context\n if (!configDef.allowInBrowserExt && isURLFromBrowserExtension) {\n // If the value is not allowed here, then set to the default if provided, otherwise ignore\n // the key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Get the value from the configuration source\n const value = configDef.getValue(settings, key);\n if (value === undefined) {\n // If there is no value (e.g. undefined), then set to the default if provided,\n // otherwise ignore the config key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Finally, run the value through an optional coerce method\n config[key] = configDef.coerce ? configDef.coerce(value) : value;\n }\n\n return config;\n}\n","/**\n * Return true if the client is from a browser extension.\n *\n * @param {string} url\n * @return {boolean} - Returns true if this instance of the Hypothesis client is one\n * distributed in a browser extension, false if it's one embedded in a\n * website.\n *\n */\nexport function isBrowserExtension(url) {\n return !(url.startsWith('http://') || url.startsWith('https://'));\n}\n","import { normalizeKeyName } from '@hypothesis/frontend-shared';\n\nimport { useEffect } from 'preact/hooks';\n\n/**\n * Bit flags indicating modifiers required by a shortcut or pressed in a key event.\n *\n * @type {Record<string, number>}\n */\nconst modifiers = {\n alt: 1,\n ctrl: 2,\n meta: 4,\n shift: 8,\n};\n\n/**\n * Match a `shortcut` key sequence against a keydown event.\n *\n * A shortcut key sequence is a string of \"+\"-separated keyboard modifiers and\n * keys. The list may contain zero or more modifiers and must contain exactly\n * one non-modifier key. The key and modifier names are case-insensitive.\n * For example \"Ctrl+Enter\", \"shift+a\". Key names are matched against `KeyboardEvent.key`.\n *\n * @param {KeyboardEvent} event\n * @param {string} shortcut\n * @return {boolean}\n */\nexport function matchShortcut(event, shortcut) {\n const parts = shortcut.split('+').map(p => p.toLowerCase());\n\n let requiredModifiers = 0;\n let requiredKey = null;\n\n for (let part of parts) {\n const modifierFlag = modifiers[part];\n if (modifierFlag) {\n requiredModifiers |= modifierFlag;\n } else if (requiredKey === null) {\n requiredKey = part;\n } else {\n throw new Error('Multiple non-modifier keys specified');\n }\n }\n\n if (!requiredKey) {\n throw new Error(`Invalid shortcut: ${shortcut}`);\n }\n\n const actualModifiers =\n (event.ctrlKey ? modifiers.ctrl : 0) |\n (event.metaKey ? modifiers.meta : 0) |\n (event.altKey ? modifiers.alt : 0) |\n (event.shiftKey ? modifiers.shift : 0);\n\n return (\n actualModifiers === requiredModifiers &&\n normalizeKeyName(event.key).toLowerCase() === requiredKey\n );\n}\n\n/**\n * @typedef ShortcutOptions\n * @prop {HTMLElement} [rootElement] -\n * Element on which the key event listener should be installed. Defaults to\n * `document.body`.\n */\n\n/**\n * Install a shortcut key listener on the document.\n *\n * This can be used directly outside of a component. To use within a Preact\n * component, you probably want the `useShortcut` hook.\n *\n * @param {string} shortcut - Shortcut key sequence. See `matchShortcut`.\n * @param {(e: KeyboardEvent) => void} onPress - A function to call when the shortcut matches\n * @param {ShortcutOptions} [options]\n * @return {() => void} A function that removes the shortcut\n */\nexport function installShortcut(\n shortcut,\n onPress,\n {\n // We use `documentElement` as the root element rather than `document.body`\n // which is used as a root element in some other places because the body\n // element is not keyboard-focusable in XHTML documents in Safari/Chrome.\n // See https://github.com/hypothesis/client/issues/4364.\n rootElement = document.documentElement,\n } = {}\n) {\n /** @param {KeyboardEvent} event */\n const onKeydown = event => {\n if (matchShortcut(event, shortcut)) {\n onPress(event);\n }\n };\n rootElement.addEventListener('keydown', onKeydown);\n return () => rootElement.removeEventListener('keydown', onKeydown);\n}\n\n/**\n * An effect hook that installs a shortcut using `installShortcut` and removes\n * it when the component is unmounted.\n *\n * This provides a convenient way to enable a document-level shortcut while\n * a component is mounted. This differs from adding an `onKeyDown` handler to\n * one of the component's DOM elements in that it doesn't require the component\n * to have focus.\n *\n * To conditionally disable the shortcut, set `shortcut` to `null`.\n *\n * @param {string|null} shortcut -\n * A shortcut key sequence to match or `null` to disable. See `matchShortcut`.\n * @param {(e: KeyboardEvent) => void} onPress - A function to call when the shortcut matches\n * @param {ShortcutOptions} [options]\n */\nexport function useShortcut(shortcut, onPress, { rootElement } = {}) {\n useEffect(() => {\n if (!shortcut) {\n return undefined;\n }\n return installShortcut(shortcut, onPress, { rootElement });\n }, [shortcut, onPress, rootElement]);\n}\n","/**\n * Normalize a keyboard event key name.\n *\n * Some old Microsoft browsers, such as IE 11 and Edge Legacy [1], use non-standard\n * names for some keys. If any abnormal keys are used, this method returns the\n * normalized name so our UI components don't require a special case.\n *\n * [1] https://caniuse.com/keyboardevent-key\n *\n * @param {string} key - The keyboard event `key` name\n * @return {string} - Normalized `key` name\n */\nexport function normalizeKeyName(key) {\n const mappings = {\n Left: 'ArrowLeft',\n Up: 'ArrowUp',\n Down: 'ArrowDown',\n Right: 'ArrowRight',\n Spacebar: ' ',\n Del: 'Delete'\n };\n return mappings[key] ? mappings[key] : key;\n}\n//# sourceMappingURL=browser-compatibility-utils.js.map","import classnames from 'classnames';\nimport { LabeledButton, Icon } from '@hypothesis/frontend-shared';\n\nimport { useShortcut } from '../../shared/shortcut';\n\n/**\n * Render an inverted light-on-dark \"pill\" with the given `badgeCount`\n * (annotation count). This is rendered instead of an icon on the toolbar\n * button for \"show\"-ing associated annotations for the current selection.\n *\n * @param {object} props\n * @param {number} props.badgeCount\n */\nfunction NumberIcon({ badgeCount }) {\n return (\n <span\n className={classnames(\n 'rounded px-1 py-0.5',\n 'text-color-text-inverted font-bold bg-grey-7',\n 'dim-bg'\n )}\n >\n {badgeCount}\n </span>\n );\n}\n\n/**\n * Render an arrow pointing up or down from the AdderToolbar. This arrow\n * should point roughly to the end of the user selection in the document.\n *\n * @param {object} props\n * @param {'up'|'down'} props.arrowDirection\n */\nfunction AdderToolbarArrow({ arrowDirection }) {\n return (\n <Icon\n name=\"pointer\"\n classes={classnames(\n // Position the arrow in the horizontal center at the bottom of the\n // container (toolbar). Note: the arrow is pointing up at this point.\n 'absolute left-1/2 -translate-x-1/2',\n // Override `1em` width/height rules in `Icon` to size the arrow as\n // its SVG dimensions dictate\n 'h-auto w-auto z-2',\n 'text-grey-3 fill-white',\n {\n // Down arrow: transform to point the arrow down\n 'rotate-180': arrowDirection === 'down',\n // Up arrow: position vertically above the toolbar\n 'top-0 -translate-y-full': arrowDirection === 'up',\n }\n )}\n />\n );\n}\n\n/**\n * @param {object} props\n * @param {number} [props.badgeCount]\n * @param {string} [props.icon]\n * @param {string} props.label\n * @param {() => void} props.onClick\n * @param {string|null} props.shortcut\n */\nfunction ToolbarButton({ badgeCount, icon, label, onClick, shortcut }) {\n useShortcut(shortcut, onClick);\n\n const title = shortcut ? `${label} (${shortcut})` : label;\n\n return (\n <LabeledButton\n className={classnames(\n 'flex flex-col gap-y-1 items-center py-2.5 px-2',\n 'text-annotator-sm leading-none text-grey-7',\n 'transition-colors duration-200',\n 'dim-item'\n )}\n onClick={onClick}\n title={title}\n >\n {icon && <Icon classes=\"text-annotator-lg\" name={icon} title={title} />}\n {typeof badgeCount === 'number' && <NumberIcon badgeCount={badgeCount} />}\n <span className=\"font-normal\">{label}</span>\n </LabeledButton>\n );\n}\n\n/**\n * Union of possible toolbar commands.\n *\n * @typedef {'annotate'|'highlight'|'show'|'hide'} Command\n */\n\n/**\n * @typedef AdderToolbarProps\n * @prop {'up'|'down'} arrowDirection -\n * Whether the arrow pointing out of the toolbar towards the selected text\n * should appear above the toolbar pointing Up or below the toolbar pointing\n * Down.\n * @prop {boolean} isVisible - Whether to show the toolbar or not.\n * @prop {(c: Command) => void} onCommand - Called when a toolbar button is clicked.\n * @prop {number} [annotationCount] -\n * Number of annotations associated with the selected text.\n * If non-zero, a \"Show\" button is displayed to allow the user to see the\n * annotations that correspond to the selection.\n */\n\n/**\n * The toolbar that is displayed above or below selected text in the document,\n * providing options to create annotations or highlights.\n *\n * @param {AdderToolbarProps} props\n */\nexport default function AdderToolbar({\n arrowDirection,\n isVisible,\n onCommand,\n annotationCount = 0,\n}) {\n // Since the selection toolbar is only shown when there is a selection\n // of static text, we can use a plain key without any modifier as\n // the shortcut. This avoids conflicts with browser/OS shortcuts.\n const annotateShortcut = isVisible ? 'a' : null;\n const highlightShortcut = isVisible ? 'h' : null;\n const showShortcut = isVisible ? 's' : null;\n const hideShortcut = isVisible ? 'Escape' : null;\n\n // Add a shortcut to close the adder. Note, there is no button associated with this\n // shortcut because any outside click will also hide the adder.\n useShortcut(hideShortcut, () => onCommand('hide'));\n\n // nb. The adder is hidden using the `visibility` property rather than `display`\n // so that we can compute its size in order to position it before display.\n return (\n <div\n className={classnames(\n 'AdderToolbar',\n 'absolute select-none bg-white rounded shadow-adder-toolbar',\n // Because `.AdderToolbar` rules reset `all:initial`, we cannot use\n // default border values from Tailwind and have to be explicit about\n // all border attributes\n 'border border-solid border-grey-3',\n // Start at a very low opacity as we're going to fade in in the animation\n 'opacity-5',\n {\n 'animate-adder-pop-up': arrowDirection === 'up' && isVisible,\n 'animate-adder-pop-down': arrowDirection === 'down' && isVisible,\n }\n )}\n dir=\"ltr\"\n style={{\n visibility: isVisible ? 'visible' : 'hidden',\n }}\n >\n <div className=\"flex dim-items-on-hover\">\n <ToolbarButton\n icon=\"annotate\"\n onClick={() => onCommand('annotate')}\n label=\"Annotate\"\n shortcut={annotateShortcut}\n />\n <ToolbarButton\n icon=\"highlight\"\n onClick={() => onCommand('highlight')}\n label=\"Highlight\"\n shortcut={highlightShortcut}\n />\n {annotationCount > 0 && (\n <>\n <div\n className={classnames(\n // Style a vertical separator line\n 'm-1.5 border-r border-grey-4 border-solid'\n )}\n />\n <ToolbarButton\n badgeCount={annotationCount}\n onClick={() => onCommand('show')}\n label=\"Show\"\n shortcut={showShortcut}\n />\n </>\n )}\n </div>\n <AdderToolbarArrow arrowDirection={arrowDirection} />\n </div>\n );\n}\n","/**\n * Helper methods to identify browser versions and os types\n */\n\n/**\n * Returns true when the OS is Mac OS.\n *\n * @param _userAgent {string} - Test seam\n */\nexport const isMacOS = (_userAgent = window.navigator.userAgent) => {\n return _userAgent.indexOf('Mac OS') >= 0;\n};\n\n/**\n * Returns true when device is iOS.\n * https://stackoverflow.com/a/9039885/14463679\n *\n * @param _navigator {{platform: string, userAgent: string}}\n * @param _ontouchend {boolean}\n */\nexport const isIOS = (\n _navigator = window.navigator,\n _ontouchend = 'ontouchend' in document\n) => {\n return (\n [\n 'iPad Simulator',\n 'iPhone Simulator',\n 'iPod Simulator',\n 'iPad',\n 'iPhone',\n 'iPod',\n ].includes(_navigator.platform) ||\n // iPad on iOS 13 detection\n (_navigator.userAgent.includes('Mac') && _ontouchend)\n );\n};\n\n/**\n * Returns true when the device is a touch device such\n * as android or iOS.\n * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer#browser_compatibility\n *\n * @param _window {Window} - Test seam\n */\nexport const isTouchDevice = (_window = window) => {\n return _window.matchMedia('(pointer: coarse)').matches;\n};\n","/**\n * Load stylesheets for annotator UI components into the shadow DOM root.\n *\n * @param {ShadowRoot} shadowRoot\n */\nfunction loadStyles(shadowRoot) {\n // Find the preloaded stylesheet added by the boot script.\n const url = /** @type {HTMLLinkElement|undefined} */ (\n document.querySelector(\n 'link[rel=\"preload\"][href*=\"/build/styles/annotator.css\"]'\n )\n )?.href;\n\n if (!url) {\n return;\n }\n\n const linkEl = document.createElement('link');\n linkEl.rel = 'stylesheet';\n linkEl.href = url;\n shadowRoot.appendChild(linkEl);\n}\n\n/**\n * Create the shadow root for an annotator UI component and load the annotator\n * CSS styles into it.\n *\n * @param {HTMLElement} container - Container element to render the UI into\n * @return {ShadowRoot}\n */\nexport function createShadowRoot(container) {\n const shadowRoot = container.attachShadow({ mode: 'open' });\n loadStyles(shadowRoot);\n\n // @ts-ignore The window doesn't know about the polyfill\n // applyFocusVisiblePolyfill comes from the `focus-visible` package.\n const applyFocusVisible = window.applyFocusVisiblePolyfill;\n if (applyFocusVisible) {\n applyFocusVisible(shadowRoot);\n }\n\n stopEventPropagation(shadowRoot);\n return shadowRoot;\n}\n\n/**\n * Stop bubbling up of several events.\n *\n * This makes the host page a little bit less aware of the annotator activity.\n * It is still possible for the host page to manipulate the events on the capturing\n * face.\n *\n * Another benefit is that click and touchstart typically causes the sidebar to close.\n * By preventing the bubble up of these events, we don't have to individually stop\n * the propagation.\n *\n * @param {HTMLElement|ShadowRoot} element\n */\nfunction stopEventPropagation(element) {\n element.addEventListener('mouseup', event => event.stopPropagation());\n element.addEventListener('mousedown', event => event.stopPropagation());\n element.addEventListener('touchstart', event => event.stopPropagation(), {\n passive: true,\n });\n}\n","import { render } from 'preact';\n\nimport AdderToolbar from './components/AdderToolbar';\nimport { isTouchDevice } from '../shared/user-agent';\nimport { createShadowRoot } from './util/shadow-root';\n\n/**\n * @typedef {1} ArrowPointingDown\n * Show the adder above the selection with an arrow pointing down at the\n * selected text.\n */\nexport const ARROW_POINTING_DOWN = 1;\n\n/**\n * @typedef {2} ArrowPointingUp\n * Show the adder above the selection with an arrow pointing up at the\n * selected text.\n */\nexport const ARROW_POINTING_UP = 2;\n\n/**\n * @typedef {ArrowPointingDown|ArrowPointingUp} ArrowDirection\n * Show the adder above the selection with an arrow pointing up at the\n * selected text.\n */\n\n/**\n * @typedef Target\n * @prop {number} left - Offset from left edge of viewport.\n * @prop {number} top - Offset from top edge of viewport.\n * @prop {ArrowDirection} arrowDirection - Direction of the adder's arrow.\n */\n\n/** @param {number} pixels */\nfunction toPx(pixels) {\n return pixels.toString() + 'px';\n}\n\nconst ARROW_HEIGHT = 10;\n\n// The preferred gap between the end of the text selection and the adder's\n// arrow position.\nconst ARROW_H_MARGIN = 20;\n\n/**\n * Return the closest ancestor of `el` which has been positioned.\n *\n * If no ancestor has been positioned, returns the root element.\n *\n * @param {Element} el\n * @return {Element}\n */\nfunction nearestPositionedAncestor(el) {\n let parentEl = /** @type {Element} */ (el.parentElement);\n while (parentEl.parentElement) {\n if (getComputedStyle(parentEl).position !== 'static') {\n break;\n }\n parentEl = parentEl.parentElement;\n }\n return parentEl;\n}\n\n/**\n * @typedef AdderOptions\n * @prop {() => void} onAnnotate - Callback invoked when \"Annotate\" button is clicked\n * @prop {() => void} onHighlight - Callback invoked when \"Highlight\" button is clicked\n * @prop {(tags: string[]) => void} onShowAnnotations -\n * Callback invoked when \"Show\" button is clicked\n *\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Container for the 'adder' toolbar which provides controls for the user to\n * annotate and highlight the selected text.\n *\n * The toolbar implementation is split between this class, which is\n * the container for the toolbar that positions it on the page and isolates\n * it from the page's styles using shadow DOM, and the `AdderToolbar` Preact\n * component which actually renders the toolbar.\n *\n * @implements {Destroyable}\n */\nexport class Adder {\n /**\n * Create the toolbar's container and hide it.\n *\n * The adder is initially hidden.\n *\n * @param {HTMLElement} element - The DOM element into which the adder will be created\n * @param {AdderOptions} options - Options object specifying `onAnnotate` and `onHighlight`\n * event handlers.\n */\n constructor(element, options) {\n this._outerContainer = document.createElement('hypothesis-adder');\n element.appendChild(this._outerContainer);\n this._shadowRoot = createShadowRoot(this._outerContainer);\n\n // Set initial style\n Object.assign(this._outerContainer.style, {\n // take position out of layout flow initially\n position: 'absolute',\n top: 0,\n left: 0,\n });\n\n this._view = /** @type {Window} */ (element.ownerDocument.defaultView);\n\n this._width = () => {\n const firstChild = /** @type {Element} */ (this._shadowRoot.firstChild);\n return firstChild.getBoundingClientRect().width;\n };\n\n this._height = () => {\n const firstChild = /** @type {Element} */ (this._shadowRoot.firstChild);\n return firstChild.getBoundingClientRect().height;\n };\n\n this._isVisible = false;\n\n /** @type {'up'|'down'} */\n this._arrowDirection = 'up';\n\n this._onAnnotate = options.onAnnotate;\n this._onHighlight = options.onHighlight;\n this._onShowAnnotations = options.onShowAnnotations;\n\n /**\n * Annotation tags associated with the current selection. If non-empty,\n * a \"Show\" button appears in the toolbar. Clicking the button calls the\n * `onShowAnnotations` callback with the current value of `annotationsForSelection`.\n *\n * @type {string[]}\n */\n this.annotationsForSelection = [];\n\n this._render();\n }\n\n /** Hide the adder */\n hide() {\n this._isVisible = false;\n this._render();\n // Reposition the outerContainer because it affects the responsiveness of host page\n // https://github.com/hypothesis/client/issues/3193\n Object.assign(this._outerContainer.style, {\n top: 0,\n left: 0,\n });\n }\n\n destroy() {\n render(null, this._shadowRoot); // First, unload the Preact component\n this._outerContainer.remove();\n }\n\n /**\n * Display the adder in the best position in order to target the\n * selected text in `selectionRect`.\n *\n * @param {DOMRect} selectionRect - The rect of text to target, in viewport\n * coordinates.\n * @param {boolean} isRTLselection - True if the selection was made\n * rigth-to-left, such that the focus point is mosty likely at the\n * top-left edge of `targetRect`.\n */\n show(selectionRect, isRTLselection) {\n const { left, top, arrowDirection } = this._calculateTarget(\n selectionRect,\n isRTLselection\n );\n this._showAt(left, top);\n\n this._isVisible = true;\n this._arrowDirection = arrowDirection === ARROW_POINTING_UP ? 'up' : 'down';\n\n this._render();\n }\n\n /**\n * Determine the best position for the Adder and its pointer-arrow.\n * - Position the pointer-arrow near the end of the selection (where the user's\n * cursor/input is most likely to be)\n * - Position the Adder to center horizontally on the pointer-arrow\n * - Position the Adder below the selection (arrow pointing up) for LTR selections\n * and above (arrow down) for RTL selections\n *\n * @param {DOMRect} selectionRect - The rect of text to target, in viewport\n * coordinates.\n * @param {boolean} isRTLselection - True if the selection was made\n * rigth-to-left, such that the focus point is mosty likely at the\n * top-left edge of `targetRect`.\n * @return {Target}\n */\n _calculateTarget(selectionRect, isRTLselection) {\n // Set the initial arrow direction based on whether the selection was made\n // forwards/upwards or downwards/backwards.\n /** @type {ArrowDirection} */ let arrowDirection;\n if (isRTLselection && !isTouchDevice()) {\n arrowDirection = ARROW_POINTING_DOWN;\n } else {\n // Render the adder below the selection for touch devices due to competing\n // space with the native copy/paste bar that typical (not always) renders above\n // the selection.\n arrowDirection = ARROW_POINTING_UP;\n }\n let top;\n let left;\n\n // Position the adder such that the arrow it is above or below the selection\n // and close to the end.\n const hMargin = Math.min(ARROW_H_MARGIN, selectionRect.width);\n const adderWidth = this._width();\n // Render the adder a little lower on touch devices to provide room for the native\n // selection handles so that the interactions with selection don't compete with the adder.\n const touchScreenOffset = isTouchDevice() ? 10 : 0;\n const adderHeight = this._height();\n if (isRTLselection) {\n left = selectionRect.left - adderWidth / 2 + hMargin;\n } else {\n left =\n selectionRect.left + selectionRect.width - adderWidth / 2 - hMargin;\n }\n\n // Flip arrow direction if adder would appear above the top or below the\n // bottom of the viewport.\n if (\n selectionRect.top - adderHeight < 0 &&\n arrowDirection === ARROW_POINTING_DOWN\n ) {\n arrowDirection = ARROW_POINTING_UP;\n } else if (selectionRect.top + adderHeight > this._view.innerHeight) {\n arrowDirection = ARROW_POINTING_DOWN;\n }\n\n if (arrowDirection === ARROW_POINTING_UP) {\n top =\n selectionRect.top +\n selectionRect.height +\n ARROW_HEIGHT +\n touchScreenOffset;\n } else {\n top = selectionRect.top - adderHeight - ARROW_HEIGHT;\n }\n\n // Constrain the adder to the viewport.\n left = Math.max(left, 0);\n left = Math.min(left, this._view.innerWidth - adderWidth);\n\n top = Math.max(top, 0);\n top = Math.min(top, this._view.innerHeight - adderHeight);\n\n return { top, left, arrowDirection };\n }\n\n /**\n * Find a Z index value that will cause the adder to appear on top of any\n * content in the document when the adder is shown at (left, top).\n *\n * @param {number} left - Horizontal offset from left edge of viewport.\n * @param {number} top - Vertical offset from top edge of viewport.\n * @return {number} - greatest zIndex (default value of 1)\n */\n _findZindex(left, top) {\n if (document.elementsFromPoint === undefined) {\n // In case of not being able to use `document.elementsFromPoint`,\n // default to the large arbitrary number (2^15)\n return 32768;\n }\n\n const adderWidth = this._width();\n const adderHeight = this._height();\n\n // Find the Z index of all the elements in the screen for five positions\n // around the adder (left-top, left-bottom, middle-center, right-top,\n // right-bottom) and use the greatest.\n\n // Unique elements so `getComputedStyle` is called the minimum amount of times.\n const elements = new Set([\n ...document.elementsFromPoint(left, top),\n ...document.elementsFromPoint(left, top + adderHeight),\n ...document.elementsFromPoint(\n left + adderWidth / 2,\n top + adderHeight / 2\n ),\n ...document.elementsFromPoint(left + adderWidth, top),\n ...document.elementsFromPoint(left + adderWidth, top + adderHeight),\n ]);\n\n const zIndexes = [...elements]\n .map(element => +getComputedStyle(element).zIndex)\n .filter(Number.isInteger);\n\n // Make sure the array contains at least one element,\n // otherwise `Math.max(...[])` results in +Infinity\n zIndexes.push(0);\n\n return Math.max(...zIndexes) + 1;\n }\n\n /**\n * Show the adder at the given position and with the arrow pointing in\n * `arrowDirection`.\n *\n * @param {number} left - Horizontal offset from left edge of viewport.\n * @param {number} top - Vertical offset from top edge of viewport.\n */\n _showAt(left, top) {\n // Translate the (left, top) viewport coordinates into positions relative to\n // the adder's nearest positioned ancestor (NPA).\n //\n // Typically the adder is a child of the `<body>` and the NPA is the root\n // `<html>` element. However page styling may make the `<body>` positioned.\n // See https://github.com/hypothesis/client/issues/487.\n const positionedAncestor = nearestPositionedAncestor(this._outerContainer);\n const parentRect = positionedAncestor.getBoundingClientRect();\n\n const zIndex = this._findZindex(left, top);\n\n Object.assign(this._outerContainer.style, {\n left: toPx(left - parentRect.left),\n top: toPx(top - parentRect.top),\n zIndex,\n });\n }\n\n _render() {\n /** @param {import('./components/AdderToolbar').Command} command */\n const handleCommand = command => {\n switch (command) {\n case 'annotate':\n this._onAnnotate();\n this.hide();\n break;\n case 'highlight':\n this._onHighlight();\n this.hide();\n break;\n case 'show':\n this._onShowAnnotations(this.annotationsForSelection);\n break;\n case 'hide':\n this.hide();\n break;\n default:\n break;\n }\n };\n\n render(\n <AdderToolbar\n isVisible={this._isVisible}\n arrowDirection={this._arrowDirection}\n onCommand={handleCommand}\n annotationCount={this.annotationsForSelection.length}\n />,\n this._shadowRoot\n );\n }\n}\n","/**\n * Return the combined length of text nodes contained in `node`.\n *\n * @param {Node} node\n */\nfunction nodeTextLength(node) {\n switch (node.nodeType) {\n case Node.ELEMENT_NODE:\n case Node.TEXT_NODE:\n // nb. `textContent` excludes text in comments and processing instructions\n // when called on a parent element, so we don't need to subtract that here.\n\n return /** @type {string} */ (node.textContent).length;\n default:\n return 0;\n }\n}\n\n/**\n * Return the total length of the text of all previous siblings of `node`.\n *\n * @param {Node} node\n */\nfunction previousSiblingsTextLength(node) {\n let sibling = node.previousSibling;\n let length = 0;\n while (sibling) {\n length += nodeTextLength(sibling);\n sibling = sibling.previousSibling;\n }\n return length;\n}\n\n/**\n * Resolve one or more character offsets within an element to (text node, position)\n * pairs.\n *\n * @param {Element} element\n * @param {number[]} offsets - Offsets, which must be sorted in ascending order\n * @return {{ node: Text, offset: number }[]}\n */\nfunction resolveOffsets(element, ...offsets) {\n let nextOffset = offsets.shift();\n const nodeIter = /** @type {Document} */ (\n element.ownerDocument\n ).createNodeIterator(element, NodeFilter.SHOW_TEXT);\n const results = [];\n\n let currentNode = nodeIter.nextNode();\n let textNode;\n let length = 0;\n\n // Find the text node containing the `nextOffset`th character from the start\n // of `element`.\n while (nextOffset !== undefined && currentNode) {\n textNode = /** @type {Text} */ (currentNode);\n if (length + textNode.data.length > nextOffset) {\n results.push({ node: textNode, offset: nextOffset - length });\n nextOffset = offsets.shift();\n } else {\n currentNode = nodeIter.nextNode();\n length += textNode.data.length;\n }\n }\n\n // Boundary case.\n while (nextOffset !== undefined && textNode && length === nextOffset) {\n results.push({ node: textNode, offset: textNode.data.length });\n nextOffset = offsets.shift();\n }\n\n if (nextOffset !== undefined) {\n throw new RangeError('Offset exceeds text length');\n }\n\n return results;\n}\n\nexport let RESOLVE_FORWARDS = 1;\nexport let RESOLVE_BACKWARDS = 2;\n\n/**\n * Represents an offset within the text content of an element.\n *\n * This position can be resolved to a specific descendant node in the current\n * DOM subtree of the element using the `resolve` method.\n */\nexport class TextPosition {\n /**\n * Construct a `TextPosition` that refers to the text position `offset` within\n * the text content of `element`.\n *\n * @param {Element} element\n * @param {number} offset\n */\n constructor(element, offset) {\n if (offset < 0) {\n throw new Error('Offset is invalid');\n }\n\n /** Element that `offset` is relative to. */\n this.element = element;\n\n /** Character offset from the start of the element's `textContent`. */\n this.offset = offset;\n }\n\n /**\n * Return a copy of this position with offset relative to a given ancestor\n * element.\n *\n * @param {Element} parent - Ancestor of `this.element`\n * @return {TextPosition}\n */\n relativeTo(parent) {\n if (!parent.contains(this.element)) {\n throw new Error('Parent is not an ancestor of current element');\n }\n\n let el = this.element;\n let offset = this.offset;\n while (el !== parent) {\n offset += previousSiblingsTextLength(el);\n el = /** @type {Element} */ (el.parentElement);\n }\n\n return new TextPosition(el, offset);\n }\n\n /**\n * Resolve the position to a specific text node and offset within that node.\n *\n * Throws if `this.offset` exceeds the length of the element's text. In the\n * case where the element has no text and `this.offset` is 0, the `direction`\n * option determines what happens.\n *\n * Offsets at the boundary between two nodes are resolved to the start of the\n * node that begins at the boundary.\n *\n * @param {object} [options]\n * @param {RESOLVE_FORWARDS|RESOLVE_BACKWARDS} [options.direction] -\n * Specifies in which direction to search for the nearest text node if\n * `this.offset` is `0` and `this.element` has no text. If not specified\n * an error is thrown.\n * @return {{ node: Text, offset: number }}\n * @throws {RangeError}\n */\n resolve(options = {}) {\n try {\n return resolveOffsets(this.element, this.offset)[0];\n } catch (err) {\n if (this.offset === 0 && options.direction !== undefined) {\n const tw = document.createTreeWalker(\n this.element.getRootNode(),\n NodeFilter.SHOW_TEXT\n );\n tw.currentNode = this.element;\n const forwards = options.direction === RESOLVE_FORWARDS;\n const text = /** @type {Text|null} */ (\n forwards ? tw.nextNode() : tw.previousNode()\n );\n if (!text) {\n throw err;\n }\n return { node: text, offset: forwards ? 0 : text.data.length };\n } else {\n throw err;\n }\n }\n }\n\n /**\n * Construct a `TextPosition` that refers to the `offset`th character within\n * `node`.\n *\n * @param {Node} node\n * @param {number} offset\n * @return {TextPosition}\n */\n static fromCharOffset(node, offset) {\n switch (node.nodeType) {\n case Node.TEXT_NODE:\n return TextPosition.fromPoint(node, offset);\n case Node.ELEMENT_NODE:\n return new TextPosition(/** @type {Element} */ (node), offset);\n default:\n throw new Error('Node is not an element or text node');\n }\n }\n\n /**\n * Construct a `TextPosition` representing the range start or end point (node, offset).\n *\n * @param {Node} node - Text or Element node\n * @param {number} offset - Offset within the node.\n * @return {TextPosition}\n */\n static fromPoint(node, offset) {\n switch (node.nodeType) {\n case Node.TEXT_NODE: {\n if (offset < 0 || offset > /** @type {Text} */ (node).data.length) {\n throw new Error('Text node offset is out of range');\n }\n\n if (!node.parentElement) {\n throw new Error('Text node has no parent');\n }\n\n // Get the offset from the start of the parent element.\n const textOffset = previousSiblingsTextLength(node) + offset;\n\n return new TextPosition(node.parentElement, textOffset);\n }\n case Node.ELEMENT_NODE: {\n if (offset < 0 || offset > node.childNodes.length) {\n throw new Error('Child node offset is out of range');\n }\n\n // Get the text length before the `offset`th child of element.\n let textOffset = 0;\n for (let i = 0; i < offset; i++) {\n textOffset += nodeTextLength(node.childNodes[i]);\n }\n\n return new TextPosition(/** @type {Element} */ (node), textOffset);\n }\n default:\n throw new Error('Point is not in an element or text node');\n }\n }\n}\n\n/**\n * Represents a region of a document as a (start, end) pair of `TextPosition` points.\n *\n * Representing a range in this way allows for changes in the DOM content of the\n * range which don't affect its text content, without affecting the text content\n * of the range itself.\n */\nexport class TextRange {\n /**\n * Construct an immutable `TextRange` from a `start` and `end` point.\n *\n * @param {TextPosition} start\n * @param {TextPosition} end\n */\n constructor(start, end) {\n this.start = start;\n this.end = end;\n }\n\n /**\n * Return a copy of this range with start and end positions relative to a\n * given ancestor. See `TextPosition.relativeTo`.\n *\n * @param {Element} element\n */\n relativeTo(element) {\n return new TextRange(\n this.start.relativeTo(element),\n this.end.relativeTo(element)\n );\n }\n\n /**\n * Resolve the `TextRange` to a DOM range.\n *\n * The resulting DOM Range will always start and end in a `Text` node.\n * Hence `TextRange.fromRange(range).toRange()` can be used to \"shrink\" a\n * range to the text it contains.\n *\n * May throw if the `start` or `end` positions cannot be resolved to a range.\n *\n * @return {Range}\n */\n toRange() {\n let start;\n let end;\n\n if (\n this.start.element === this.end.element &&\n this.start.offset <= this.end.offset\n ) {\n // Fast path for start and end points in same element.\n [start, end] = resolveOffsets(\n this.start.element,\n this.start.offset,\n this.end.offset\n );\n } else {\n start = this.start.resolve({ direction: RESOLVE_FORWARDS });\n end = this.end.resolve({ direction: RESOLVE_BACKWARDS });\n }\n\n const range = new Range();\n range.setStart(start.node, start.offset);\n range.setEnd(end.node, end.offset);\n return range;\n }\n\n /**\n * Convert an existing DOM `Range` to a `TextRange`\n *\n * @param {Range} range\n * @return {TextRange}\n */\n static fromRange(range) {\n const start = TextPosition.fromPoint(\n range.startContainer,\n range.startOffset\n );\n const end = TextPosition.fromPoint(range.endContainer, range.endOffset);\n return new TextRange(start, end);\n }\n\n /**\n * Return a `TextRange` from the `start`th to `end`th characters in `root`.\n *\n * @param {Element} root\n * @param {number} start\n * @param {number} end\n */\n static fromOffsets(root, start, end) {\n return new TextRange(\n new TextPosition(root, start),\n new TextPosition(root, end)\n );\n }\n}\n","/**\n * CSS selector that will match the placeholder within a page/tile container.\n */\nconst placeholderSelector = '.annotator-placeholder';\n\n/**\n * Create or return a placeholder element for anchoring.\n *\n * In document viewers such as PDF.js which only render a subset of long\n * documents at a time, it may not be possible to anchor annotations to the\n * actual text in pages which are off-screen. For these non-rendered pages,\n * a \"placeholder\" element is created in the approximate X/Y location (eg.\n * middle of the page) where the content will appear. Any highlights for that\n * page are then rendered inside the placeholder.\n *\n * When the viewport is scrolled to the non-rendered page, the placeholder\n * is removed and annotations are re-anchored to the real content.\n *\n * @param {HTMLElement} container - The container element for the page or tile\n * which is not rendered.\n */\nexport function createPlaceholder(container) {\n let placeholder = container.querySelector(placeholderSelector);\n if (placeholder) {\n return placeholder;\n }\n placeholder = document.createElement('span');\n placeholder.classList.add('annotator-placeholder');\n placeholder.textContent = 'Loading annotations...';\n container.appendChild(placeholder);\n return placeholder;\n}\n\n/**\n * Return true if a page/tile container has a placeholder.\n *\n * @param {HTMLElement} container\n */\nexport function hasPlaceholder(container) {\n return container.querySelector(placeholderSelector) !== null;\n}\n\n/**\n * Remove the placeholder element in `container`, if present.\n *\n * @param {HTMLElement} container\n */\nexport function removePlaceholder(container) {\n container.querySelector(placeholderSelector)?.remove();\n}\n\n/**\n * Return true if `node` is inside a placeholder element created with `createPlaceholder`.\n *\n * This is typically used to test if a highlight element associated with an\n * anchor is inside a placeholder.\n *\n * @param {Node} node\n */\nexport function isInPlaceholder(node) {\n if (!node.parentElement) {\n return false;\n }\n return node.parentElement.closest(placeholderSelector) !== null;\n}\n","/**\n * Returns true if the start point of a selection occurs after the end point,\n * in document order.\n *\n * @param {Selection} selection\n */\nexport function isSelectionBackwards(selection) {\n if (selection.focusNode === selection.anchorNode) {\n return selection.focusOffset < selection.anchorOffset;\n }\n\n const range = selection.getRangeAt(0);\n // Does not work correctly on iOS when selecting nodes backwards.\n // https://bugs.webkit.org/show_bug.cgi?id=220523\n return range.startContainer === selection.focusNode;\n}\n\n/**\n * Returns true if any part of `node` lies within `range`.\n *\n * @param {Range} range\n * @param {Node} node\n */\nexport function isNodeInRange(range, node) {\n try {\n const length = node.nodeValue?.length ?? node.childNodes.length;\n return (\n // Check start of node is before end of range.\n range.comparePoint(node, 0) <= 0 &&\n // Check end of node is after start of range.\n range.comparePoint(node, length) >= 0\n );\n } catch (e) {\n // `comparePoint` may fail if the `range` and `node` do not share a common\n // ancestor or `node` is a doctype.\n return false;\n }\n}\n\n/**\n * Iterate over all Node(s) which overlap `range` in document order and invoke\n * `callback` for each of them.\n *\n * @param {Range} range\n * @param {(n: Node) => void} callback\n */\nexport function forEachNodeInRange(range, callback) {\n const root = range.commonAncestorContainer;\n const nodeIter = /** @type {Document} */ (\n root.ownerDocument\n ).createNodeIterator(root, NodeFilter.SHOW_ALL);\n\n let currentNode;\n while ((currentNode = nodeIter.nextNode())) {\n if (isNodeInRange(range, currentNode)) {\n callback(currentNode);\n }\n }\n}\n\n/**\n * Returns the bounding rectangles of non-whitespace text nodes in `range`.\n *\n * @param {Range} range\n * @return {Array<DOMRect>} Array of bounding rects in viewport coordinates.\n */\nexport function getTextBoundingBoxes(range) {\n const whitespaceOnly = /^\\s*$/;\n const textNodes = /** @type {Text[]} */ ([]);\n forEachNodeInRange(range, node => {\n if (\n node.nodeType === Node.TEXT_NODE &&\n !(/** @type {string} */ (node.textContent).match(whitespaceOnly))\n ) {\n textNodes.push(/** @type {Text} */ (node));\n }\n });\n\n /** @type {DOMRect[]} */\n let rects = [];\n textNodes.forEach(node => {\n const nodeRange = node.ownerDocument.createRange();\n nodeRange.selectNodeContents(node);\n if (node === range.startContainer) {\n nodeRange.setStart(node, range.startOffset);\n }\n if (node === range.endContainer) {\n nodeRange.setEnd(node, range.endOffset);\n }\n if (nodeRange.collapsed) {\n // If the range ends at the start of this text node or starts at the end\n // of this node then do not include it.\n return;\n }\n\n // Measure the range and translate from viewport to document coordinates\n const viewportRects = Array.from(nodeRange.getClientRects());\n nodeRange.detach();\n rects = rects.concat(viewportRects);\n });\n return rects;\n}\n\n/**\n * Returns the rectangle, in viewport coordinates, for the line of text\n * containing the focus point of a Selection.\n *\n * Returns null if the selection is empty.\n *\n * @param {Selection} selection\n * @return {DOMRect|null}\n */\nexport function selectionFocusRect(selection) {\n if (selection.isCollapsed) {\n return null;\n }\n const textBoxes = getTextBoundingBoxes(selection.getRangeAt(0));\n if (textBoxes.length === 0) {\n return null;\n }\n\n if (isSelectionBackwards(selection)) {\n return textBoxes[0];\n } else {\n return textBoxes[textBoxes.length - 1];\n }\n}\n\n/**\n * Retrieve a set of items associated with nodes in a given range.\n *\n * An `item` can be any data that the caller wishes to compute from or associate\n * with a node. Only unique items, as determined by `Object.is`, are returned.\n *\n * @template T\n * @param {Range} range\n * @param {(n: Node) => T} itemForNode - Callback returning the item for a given node\n * @return {NonNullable<T>[]} items\n */\nexport function itemsForRange(range, itemForNode) {\n /** @type {Set<Node>} */\n const checkedNodes = new Set();\n /** @type {Set<NonNullable<T>>} */\n const items = new Set();\n\n forEachNodeInRange(range, node => {\n /** @type {Node|null} */\n let current = node;\n while (current) {\n if (checkedNodes.has(current)) {\n break;\n }\n checkedNodes.add(current);\n\n const item = /** @type {NonNullable<T>|null|undefined} */ (\n itemForNode(current)\n );\n if (item !== null && item !== undefined) {\n items.add(item);\n }\n\n current = current.parentNode;\n }\n });\n\n return [...items];\n}\n","import { isInPlaceholder } from './anchoring/placeholder';\nimport { isNodeInRange } from './range-util';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\n/**\n * Return the canvas element underneath a highlight element in a PDF page's\n * text layer.\n *\n * Returns `null` if the highlight is not above a PDF canvas.\n *\n * @param {HTMLElement} highlightEl -\n * A `<hypothesis-highlight>` element in the page's text layer\n * @return {HTMLCanvasElement|null}\n */\nfunction getPDFCanvas(highlightEl) {\n // This code assumes that PDF.js renders pages with a structure like:\n //\n // <div class=\"page\">\n // <div class=\"canvasWrapper\">\n // <canvas></canvas> <!-- The rendered PDF page -->\n // </div>\n // <div class=\"textLayer\">\n // <!-- Transparent text layer with text spans used to enable text selection -->\n // </div>\n // </div>\n //\n // It also assumes that the `highlightEl` element is somewhere under\n // the `.textLayer` div.\n\n const pageEl = highlightEl.closest('.page');\n if (!pageEl) {\n return null;\n }\n\n const canvasEl = pageEl.querySelector('.canvasWrapper > canvas');\n if (!canvasEl) {\n return null;\n }\n\n return /** @type {HTMLCanvasElement} */ (canvasEl);\n}\n\n/**\n * Draw highlights in an SVG layer overlaid on top of a PDF.js canvas.\n *\n * The created SVG elements are stored in the `svgHighlight` property of\n * each `HighlightElement`.\n *\n * @param {HighlightElement[]} highlightEls -\n * An element that wraps the highlighted text in the transparent text layer\n * above the PDF.\n */\nfunction drawHighlightsAbovePDFCanvas(highlightEls) {\n if (highlightEls.length === 0) {\n return;\n }\n\n // Get the <canvas> for the PDF page containing the highlight. We assume all\n // the highlights are on the same page.\n const canvasEl = getPDFCanvas(highlightEls[0]);\n if (!canvasEl || !canvasEl.parentElement) {\n return;\n }\n\n /** @type {SVGElement|null} */\n let svgHighlightLayer = canvasEl.parentElement.querySelector(\n '.hypothesis-highlight-layer'\n );\n\n if (!svgHighlightLayer) {\n // Create SVG layer. This must be in the same stacking context as\n // the canvas so that CSS `mix-blend-mode` can be used to control how SVG\n // content blends with the canvas below.\n svgHighlightLayer = document.createElementNS(SVG_NAMESPACE, 'svg');\n svgHighlightLayer.setAttribute('class', 'hypothesis-highlight-layer');\n canvasEl.parentElement.appendChild(svgHighlightLayer);\n\n // Overlay SVG layer above canvas.\n canvasEl.parentElement.style.position = 'relative';\n\n const svgStyle = svgHighlightLayer.style;\n svgStyle.position = 'absolute';\n svgStyle.left = '0';\n svgStyle.top = '0';\n svgStyle.width = '100%';\n svgStyle.height = '100%';\n\n // Use multiply blending so that highlights drawn on top of text darken it\n // rather than making it lighter. This improves contrast and thus readability\n // of highlighted text, especially for overlapping highlights.\n //\n // This choice optimizes for the common case of dark text on a light background.\n svgStyle.mixBlendMode = 'multiply';\n }\n\n const canvasRect = canvasEl.getBoundingClientRect();\n const highlightRects = highlightEls.map(highlightEl => {\n const highlightRect = highlightEl.getBoundingClientRect();\n\n // Create SVG element for the current highlight element.\n const rect = document.createElementNS(SVG_NAMESPACE, 'rect');\n rect.setAttribute('x', (highlightRect.left - canvasRect.left).toString());\n rect.setAttribute('y', (highlightRect.top - canvasRect.top).toString());\n rect.setAttribute('width', highlightRect.width.toString());\n rect.setAttribute('height', highlightRect.height.toString());\n rect.setAttribute('class', 'hypothesis-svg-highlight');\n\n // Make the highlight in the text layer transparent.\n highlightEl.classList.add('is-transparent');\n\n // Associate SVG element with highlight for use by `removeHighlights`.\n highlightEl.svgHighlight = rect;\n\n return rect;\n });\n\n svgHighlightLayer.append(...highlightRects);\n}\n\n/**\n * Additional properties added to text highlight HTML elements.\n *\n * @typedef HighlightProps\n * @prop {SVGElement} [svgHighlight]\n */\n\n/**\n * @typedef {HTMLElement & HighlightProps} HighlightElement\n */\n\n/**\n * Return text nodes which are entirely inside `range`.\n *\n * If a range starts or ends part-way through a text node, the node is split\n * and the part inside the range is returned.\n *\n * @param {Range} range\n * @return {Text[]}\n */\nfunction wholeTextNodesInRange(range) {\n if (range.collapsed) {\n // Exit early for an empty range to avoid an edge case that breaks the algorithm\n // below. Splitting a text node at the start of an empty range can leave the\n // range ending in the left part rather than the right part.\n return [];\n }\n\n /** @type {Node|null} */\n let root = range.commonAncestorContainer;\n if (root.nodeType !== Node.ELEMENT_NODE) {\n // If the common ancestor is not an element, set it to the parent element to\n // ensure that the loop below visits any text nodes generated by splitting\n // the common ancestor.\n //\n // Note that `parentElement` may be `null`.\n root = root.parentElement;\n }\n if (!root) {\n // If there is no root element then we won't be able to insert highlights,\n // so exit here.\n return [];\n }\n\n const textNodes = [];\n const nodeIter = /** @type {Document} */ (\n root.ownerDocument\n ).createNodeIterator(\n root,\n NodeFilter.SHOW_TEXT // Only return `Text` nodes.\n );\n let node;\n while ((node = nodeIter.nextNode())) {\n if (!isNodeInRange(range, node)) {\n continue;\n }\n let text = /** @type {Text} */ (node);\n\n if (text === range.startContainer && range.startOffset > 0) {\n // Split `text` where the range starts. The split will create a new `Text`\n // node which will be in the range and will be visited in the next loop iteration.\n text.splitText(range.startOffset);\n continue;\n }\n\n if (text === range.endContainer && range.endOffset < text.data.length) {\n // Split `text` where the range ends, leaving it as the part in the range.\n text.splitText(range.endOffset);\n }\n\n textNodes.push(text);\n }\n\n return textNodes;\n}\n\n/**\n * Wraps the DOM Nodes within the provided range with a highlight\n * element of the specified class and returns the highlight Elements.\n *\n * @param {Range} range - Range to be highlighted\n * @param {string} cssClass - A CSS class to use for the highlight\n * @return {HighlightElement[]} - Elements wrapping text in `normedRange` to add a highlight effect\n */\nexport function highlightRange(range, cssClass = 'hypothesis-highlight') {\n const textNodes = wholeTextNodesInRange(range);\n\n // Check if this range refers to a placeholder for not-yet-rendered content in\n // a PDF. These highlights should be invisible.\n const inPlaceholder = textNodes.length > 0 && isInPlaceholder(textNodes[0]);\n\n // Group text nodes into spans of adjacent nodes. If a group of text nodes are\n // adjacent, we only need to create one highlight element for the group.\n let textNodeSpans = /** @type {Text[][]} */ ([]);\n let prevNode = /** @type {Node|null} */ (null);\n let currentSpan = null;\n\n textNodes.forEach(node => {\n if (prevNode && prevNode.nextSibling === node) {\n currentSpan.push(node);\n } else {\n currentSpan = [node];\n textNodeSpans.push(currentSpan);\n }\n prevNode = node;\n });\n\n // Filter out text node spans that consist only of white space. This avoids\n // inserting highlight elements in places that can only contain a restricted\n // subset of nodes such as table rows and lists.\n const whitespace = /^\\s*$/;\n textNodeSpans = textNodeSpans.filter(span =>\n // Check for at least one text node with non-space content.\n span.some(node => !whitespace.test(node.data))\n );\n\n // Wrap each text node span with a `<hypothesis-highlight>` element.\n const highlights = /** @type {HighlightElement[]} */ ([]);\n textNodeSpans.forEach(nodes => {\n // A custom element name is used here rather than `<span>` to reduce the\n // likelihood of highlights being hidden by page styling.\n\n /** @type {HighlightElement} */\n const highlightEl = document.createElement('hypothesis-highlight');\n highlightEl.className = cssClass;\n\n const parent = /** @type {Node} */ (nodes[0].parentNode);\n parent.replaceChild(highlightEl, nodes[0]);\n nodes.forEach(node => highlightEl.appendChild(node));\n\n highlights.push(highlightEl);\n });\n\n // For PDF highlights, create the highlight effect by using an SVG placed\n // above the page's canvas rather than CSS `background-color` on the highlight\n // element. This enables more control over blending of the highlight with the\n // content below.\n //\n // Drawing these SVG highlights involves measuring the `<hypothesis-highlight>`\n // elements, so we create them only after those elements have all been created\n // to reduce the number of forced reflows. We also skip creating them for\n // unrendered pages for performance reasons.\n if (!inPlaceholder) {\n drawHighlightsAbovePDFCanvas(highlights);\n }\n\n return highlights;\n}\n\n/**\n * Replace a child `node` with `replacements`.\n *\n * nb. This is like `ChildNode.replaceWith` but it works in older browsers.\n *\n * @param {ChildNode} node\n * @param {Node[]} replacements\n */\nfunction replaceWith(node, replacements) {\n const parent = /** @type {Node} */ (node.parentNode);\n replacements.forEach(r => parent.insertBefore(r, node));\n node.remove();\n}\n\n/**\n * Remove all highlights under a given root element.\n *\n * @param {HTMLElement} root\n */\nexport function removeAllHighlights(root) {\n const highlights = Array.from(root.querySelectorAll('hypothesis-highlight'));\n removeHighlights(/** @type {HighlightElement[]} */ (highlights));\n}\n\n/**\n * Remove highlights from a range previously highlighted with `highlightRange`.\n *\n * @param {HighlightElement[]} highlights - The highlight elements returned by `highlightRange`\n */\nexport function removeHighlights(highlights) {\n for (let h of highlights) {\n if (h.parentNode) {\n const children = Array.from(h.childNodes);\n replaceWith(h, children);\n }\n\n if (h.svgHighlight) {\n h.svgHighlight.remove();\n }\n }\n}\n\n/**\n * Set whether the given highlight elements should appear \"focused\".\n *\n * A highlight can be displayed in a different (\"focused\") style to indicate\n * that it is current in some other context - for example the user has selected\n * the corresponding annotation in the sidebar.\n *\n * @param {HighlightElement[]} highlights\n * @param {boolean} focused\n */\nexport function setHighlightsFocused(highlights, focused) {\n highlights.forEach(h => {\n // In PDFs the visible highlight is created by an SVG element, so the focused\n // effect is applied to that. In other documents the effect is applied to the\n // `<hypothesis-highlight>` element.\n if (h.svgHighlight) {\n h.svgHighlight.classList.toggle('is-focused', focused);\n\n // Ensure that focused highlights are drawn above un-focused highlights\n // on the same page.\n //\n // SVG elements are rendered in document order so to achieve this we need\n // to move the element to be the last child of its parent.\n if (focused) {\n const parent = /** @type {SVGElement} */ (h.svgHighlight.parentNode);\n parent.append(h.svgHighlight);\n }\n } else {\n h.classList.toggle('hypothesis-highlight-focused', focused);\n }\n });\n}\n\n/**\n * Set whether highlights under the given root element should be visible.\n *\n * @param {HTMLElement} root\n * @param {boolean} visible\n */\nexport function setHighlightsVisible(root, visible) {\n const showHighlightsClass = 'hypothesis-highlights-always-on';\n root.classList.toggle(showHighlightsClass, visible);\n}\n\n/**\n * Get the highlight elements that contain the given node.\n *\n * @param {Node} node\n * @return {HighlightElement[]}\n */\nexport function getHighlightsContainingNode(node) {\n let el =\n node.nodeType === Node.ELEMENT_NODE\n ? /** @type {Element} */ (node)\n : node.parentElement;\n\n const highlights = [];\n\n while (el) {\n if (el.classList.contains('hypothesis-highlight')) {\n highlights.push(/** @type {HighlightElement} */ (el));\n }\n el = el.parentElement;\n }\n\n return highlights;\n}\n\n/**\n * Subset of `DOMRect` interface.\n *\n * @typedef Rect\n * @prop {number} top\n * @prop {number} left\n * @prop {number} bottom\n * @prop {number} right\n */\n\n/**\n * Get the bounding client rectangle of a collection in viewport coordinates.\n * Unfortunately, Chrome has issues ([1]) with Range.getBoundingClient rect or we\n * could just use that.\n *\n * [1] https://bugs.chromium.org/p/chromium/issues/detail?id=324437\n *\n * @param {HTMLElement[]} collection\n * @return {Rect}\n */\nexport function getBoundingClientRect(collection) {\n // Reduce the client rectangles of the highlights to a bounding box\n const rects = collection.map(\n n => /** @type {Rect} */ (n.getBoundingClientRect())\n );\n return rects.reduce((acc, r) => ({\n top: Math.min(acc.top, r.top),\n left: Math.min(acc.left, r.left),\n bottom: Math.max(acc.bottom, r.bottom),\n right: Math.max(acc.right, r.right),\n }));\n}\n","import { ListenerCollection } from '../shared/listener-collection';\n\nimport { computeAnchorPositions } from './util/buckets';\n\n/**\n * @typedef {import('../shared/messaging').PortRPC<HostToGuestEvent, GuestToHostEvent>} HostRPC\n * @typedef {import('../types/annotator').Anchor} Anchor\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n */\n\n/**\n * Communicate to the host frame when:\n *\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n *\n * @implements {Destroyable}\n */\nexport class BucketBarClient {\n /**\n * @param {object} options\n * @param {Element} options.contentContainer - The scrollable container element for the\n * document content. All of the highlights that the bucket bar's buckets point\n * at should be contained within this element.\n * @param {HostRPC} options.hostRPC\n */\n constructor({ contentContainer, hostRPC }) {\n this._hostRPC = hostRPC;\n this._updatePending = false;\n /** @type {Anchor[]} */\n this._anchors = [];\n this._listeners = new ListenerCollection();\n\n this._listeners.add(window, 'resize', () => this.update());\n this._listeners.add(window, 'scroll', () => this.update());\n this._listeners.add(contentContainer, 'scroll', () => this.update());\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Notifies the BucketBar in the host frame when:\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n *\n * Updates are debounced to reduce the overhead of gathering and sending anchor\n * position data across frames.\n *\n * @param {Anchor[]} [anchors] - pass this option when anchors are added or\n * deleted\n */\n update(anchors) {\n if (anchors) {\n this._anchors = anchors;\n }\n\n if (this._updatePending) {\n return;\n }\n\n this._updatePending = true;\n requestAnimationFrame(() => {\n const positions = computeAnchorPositions(this._anchors);\n this._hostRPC.call('anchorsChanged', positions);\n this._updatePending = false;\n });\n }\n}\n","import { getBoundingClientRect } from '../highlighter';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').AnchorPosition} AnchorPosition\n */\n\n/**\n * @typedef Bucket\n * @prop {Set<string>} tags - The annotation tags in this bucket\n * @prop {number} position - The vertical pixel offset where this bucket should\n * appear in the bucket bar\n */\n\n/**\n * @typedef BucketSet\n * @prop {Bucket} above - A single bucket containing all the annotation\n * tags whose anchors are offscreen upwards\n * @prop {Bucket} below - A single bucket containing all the annotation\n * tags which anchors are offscreen downwards\n * @prop {Bucket[]} buckets - On-screen buckets\n */\n\n/**\n * @typedef WorkingBucket\n * @prop {Set<string>} tags - The annotation tags in this bucket\n * @prop {number} position - The computed position (offset) for this bucket,\n * based on the current anchors. This is centered between `top` and `bottom`\n * @prop {number} top - The uppermost (lowest) vertical offset for the anchors\n * in this bucket — the lowest `top` position value, akin to the top offset of\n * a theoretical box drawn around all of the anchor highlights in this bucket\n * @prop {number} bottom - The bottommost (highest) vertical offset for the\n * anchors in this bucket — the highest `top` position value, akin to the\n * bottom of a theoretical box drawn around all of the anchor highlights in\n * this bucket\n */\n\n// Only anchors with top offsets between `BUCKET_TOP_THRESHOLD` and\n// `window.innerHeight - BUCKET_BOTTOM_THRESHOLD` are considered \"on-screen\"\n// and will be bucketed. This is to account for bucket-bar tool buttons (top\n// and the height of the bottom navigation bucket (bottom)\nconst BUCKET_TOP_THRESHOLD = 137;\nconst BUCKET_BOTTOM_THRESHOLD = 22;\n// Generated buckets of annotation anchor highlights should be spaced by\n// at least this amount, in pixels\nconst BUCKET_GAP_SIZE = 60;\n\n/**\n * Find the closest valid anchor in `anchors` that is offscreen in the direction\n * indicated.\n *\n * @param {Anchor[]} anchors\n * @param {'up'|'down'} direction\n * @return {Anchor|null} - The closest anchor or `null` if no valid anchor found\n */\nexport function findClosestOffscreenAnchor(anchors, direction) {\n let closestAnchor = null;\n let closestTop = 0;\n\n for (let anchor of anchors) {\n if (!anchor.highlights?.length) {\n continue;\n }\n\n const top = getBoundingClientRect(anchor.highlights).top;\n\n // Verify that the anchor is offscreen in the direction we're headed\n if (direction === 'up' && top >= BUCKET_TOP_THRESHOLD) {\n // We're headed up but the anchor is already below the\n // visible top of the bucket bar: it's not our guy\n continue;\n } else if (\n direction === 'down' &&\n top <= window.innerHeight - BUCKET_BOTTOM_THRESHOLD\n ) {\n // We're headed down but this anchor is already above\n // the usable bottom of the screen: it's not our guy\n continue;\n }\n\n if (\n !closestAnchor ||\n (direction === 'up' && top > closestTop) ||\n (direction === 'down' && top < closestTop)\n ) {\n // This anchor is either:\n // - The first anchor we've encountered off-screen in the direction\n // we're headed, or\n // - Closer to the screen than the previous `closestAnchor`\n closestAnchor = anchor;\n closestTop = top;\n }\n }\n\n return closestAnchor;\n}\n\n/**\n * Compute the top and bottom positions for the set of anchors' highlights, sorted\n * vertically, from top to bottom.\n *\n * @param {Anchor[]} anchors\n * @return {AnchorPosition[]}\n */\nexport function computeAnchorPositions(anchors) {\n /** @type {AnchorPosition[]} */\n const positions = [];\n\n anchors.forEach(({ annotation, highlights }) => {\n if (!highlights?.length) {\n return;\n }\n\n const { top, bottom } = getBoundingClientRect(highlights);\n\n if (top >= bottom) {\n // Empty rect. The highlights may be disconnected from the document or hidden.\n return;\n }\n\n positions.push({\n tag: annotation.$tag,\n top,\n bottom,\n });\n });\n\n // Sort anchors vertically from top to bottom\n positions.sort((anchor1, anchor2) => anchor1.top - anchor2.top);\n\n return positions;\n}\n\n/**\n * Compute buckets\n *\n * @param {AnchorPosition[]} anchorPositions\n * @return {BucketSet}\n */\nexport function computeBuckets(anchorPositions) {\n /** @type {Set<string>} */\n const aboveTags = new Set();\n /** @type {Set<string>} */\n const belowTags = new Set();\n /** @type {Bucket[]} */\n const buckets = [];\n\n // Hold current working anchors and positions as we build each bucket\n /** @type {WorkingBucket|null} */\n let currentBucket = null;\n\n /**\n * Create a new working bucket based on the provided `AnchorPosition`\n *\n * @param {AnchorPosition} anchorPosition\n * @return {WorkingBucket}\n */\n function newBucket({ bottom, tag, top }) {\n const anchorHeight = bottom - top;\n const bucketPosition = top + anchorHeight / 2;\n return {\n bottom,\n position: bucketPosition,\n tags: new Set([tag]),\n top,\n };\n }\n\n // Build buckets from position information\n anchorPositions.forEach(aPos => {\n if (aPos.top < BUCKET_TOP_THRESHOLD) {\n aboveTags.add(aPos.tag);\n return;\n } else if (aPos.top > window.innerHeight - BUCKET_BOTTOM_THRESHOLD) {\n belowTags.add(aPos.tag);\n return;\n }\n\n if (!currentBucket) {\n // We've encountered our first on-screen anchor position:\n // We'll need a bucket!\n currentBucket = newBucket(aPos);\n return;\n }\n // We want to contain overlapping highlights and those near each other\n // within a shared bucket\n const isContainedWithin =\n aPos.top > currentBucket.top && aPos.bottom < currentBucket.bottom;\n\n // The new anchor's position is far enough below the bottom of the current\n // bucket to justify starting a new bucket\n const isLargeGap = aPos.top - currentBucket.bottom > BUCKET_GAP_SIZE;\n\n if (isLargeGap && !isContainedWithin) {\n // We need to start a new bucket; push the working bucket and create\n // a new bucket\n buckets.push(currentBucket);\n currentBucket = newBucket(aPos);\n } else {\n // We'll add this anchor to the current working bucket and update\n // offset properties accordingly.\n // We can be confident that `aPos.top` is >= `currentBucket.top` because\n // AnchorPositions are sorted by their `top` offset — meaning that\n // `currentBucket.top` still accurately represents the `top` offset of\n // the virtual rectangle enclosing all anchors in this bucket. But\n // let's check to see if the bottom is larger/lower:\n const updatedBottom =\n aPos.bottom > currentBucket.bottom ? aPos.bottom : currentBucket.bottom;\n const updatedHeight = updatedBottom - currentBucket.top;\n\n currentBucket.tags.add(aPos.tag);\n currentBucket.bottom = updatedBottom;\n currentBucket.position = currentBucket.top + updatedHeight / 2;\n }\n });\n\n if (currentBucket) {\n buckets.push(currentBucket);\n }\n\n // Add an upper \"navigation\" bucket with offscreen-above anchors\n const above = {\n tags: aboveTags,\n position: BUCKET_TOP_THRESHOLD,\n };\n\n // Add a lower \"navigation\" bucket with offscreen-below anchors\n const below = {\n tags: belowTags,\n position: window.innerHeight - BUCKET_BOTTOM_THRESHOLD,\n };\n\n return {\n above,\n below,\n buckets,\n };\n}\n","/** @type {Set<string>} */\nlet shownWarnings = new Set();\n\n/**\n * Log a warning if it has not already been reported.\n *\n * This is useful to avoid spamming the console if a warning is emitted in a\n * context that may be called frequently.\n *\n * @param {...unknown} args -\n * Arguments to forward to `console.warn`. The arguments `toString()` values\n * are concatenated into a string key which is used to determine if the warning\n * has been logged before.\n */\nexport function warnOnce(...args) {\n const key = args.join();\n if (shownWarnings.has(key)) {\n return;\n }\n console.warn(...args);\n shownWarnings.add(key);\n}\n\nwarnOnce.reset = () => {\n shownWarnings.clear();\n};\n","import { TinyEmitter } from 'tiny-emitter';\n\nimport { warnOnce } from '../shared/warn-once';\n\n/**\n * @typedef {import('../types/annotator').FeatureFlags} IFeatureFlags\n */\n\n/**\n * List of feature flags that annotator code tests for.\n *\n * @type {string[]}\n */\nconst annotatorFlags = ['html_side_by_side'];\n\n/**\n * An observable container of feature flags.\n *\n * @implements {IFeatureFlags}\n */\nexport class FeatureFlags extends TinyEmitter {\n /**\n * @param {string[]} knownFlags - Test seam. This is a list of known flags\n * used to catch mistakes where code checks for an obsolete feature flag.\n */\n constructor(knownFlags = annotatorFlags) {\n super();\n\n /**\n * Map of flag name to enabled state.\n *\n * @type {Map<string, boolean>}\n */\n this._flags = new Map();\n this._knownFlags = knownFlags;\n }\n\n /**\n * Update the stored flags and notify observers via a \"flagsChanged\" event.\n *\n * @param {Record<string, boolean>} flags\n */\n update(flags) {\n this._flags.clear();\n for (let [flag, on] of Object.entries(flags)) {\n this._flags.set(flag, on);\n }\n this.emit('flagsChanged');\n }\n\n /**\n * Test if a feature flag is enabled.\n *\n * This will return false if the feature flags have not yet been received from\n * the backend. Code that uses a feature flag should handle subsequent changes\n * to the flag's state by listening for the \"flagsChanged\" event.\n *\n * @param {string} flag\n * @return {boolean}\n */\n flagEnabled(flag) {\n if (!this._knownFlags.includes(flag)) {\n warnOnce('Looked up unknown feature', flag);\n return false;\n }\n return this._flags.get(flag) ?? false;\n }\n\n /**\n * Return the state of all feature flags.\n *\n * To test whether an individual flag is enabled, use {@link flagEnabled}\n * instead.\n */\n allFlags() {\n return Object.fromEntries(this._flags);\n }\n}\n","/**\n * Implementation of Myers' online approximate string matching algorithm [1],\n * with additional optimizations suggested by [2].\n *\n * This has O((k/w) * n) expected-time where `n` is the length of the\n * text, `k` is the maximum number of errors allowed (always <= the pattern\n * length) and `w` is the word size. Because JS only supports bitwise operations\n * on 32 bit integers, `w` is 32.\n *\n * As far as I am aware, there aren't any online algorithms which are\n * significantly better for a wide range of input parameters. The problem can be\n * solved faster using \"filter then verify\" approaches which first filter out\n * regions of the text that cannot match using a \"cheap\" check and then verify\n * the remaining potential matches. The verify step requires an algorithm such\n * as this one however.\n *\n * The algorithm's approach is essentially to optimize the classic dynamic\n * programming solution to the problem by computing columns of the matrix in\n * word-sized chunks (ie. dealing with 32 chars of the pattern at a time) and\n * avoiding calculating regions of the matrix where the minimum error count is\n * guaranteed to exceed the input threshold.\n *\n * The paper consists of two parts, the first describes the core algorithm for\n * matching patterns <= the size of a word (implemented by `advanceBlock` here).\n * The second uses the core algorithm as part of a larger block-based algorithm\n * to handle longer patterns.\n *\n * [1] G. Myers, “A Fast Bit-Vector Algorithm for Approximate String Matching\n * Based on Dynamic Programming,” vol. 46, no. 3, pp. 395–415, 1999.\n *\n * [2] Šošić, M. (2014). An simd dynamic programming c/c++ library (Doctoral\n * dissertation, Fakultet Elektrotehnike i računarstva, Sveučilište u Zagrebu).\n */\nfunction reverse(s) {\n return s.split(\"\").reverse().join(\"\");\n}\n/**\n * Given the ends of approximate matches for `pattern` in `text`, find\n * the start of the matches.\n *\n * @param findEndFn - Function for finding the end of matches in\n * text.\n * @return Matches with the `start` property set.\n */\nfunction findMatchStarts(text, pattern, matches) {\n const patRev = reverse(pattern);\n return matches.map((m) => {\n // Find start of each match by reversing the pattern and matching segment\n // of text and searching for an approx match with the same number of\n // errors.\n const minStart = Math.max(0, m.end - pattern.length - m.errors);\n const textRev = reverse(text.slice(minStart, m.end));\n // If there are multiple possible start points, choose the one that\n // maximizes the length of the match.\n const start = findMatchEnds(textRev, patRev, m.errors).reduce((min, rm) => {\n if (m.end - rm.end < min) {\n return m.end - rm.end;\n }\n return min;\n }, m.end);\n return {\n start,\n end: m.end,\n errors: m.errors,\n };\n });\n}\n/**\n * Return 1 if a number is non-zero or zero otherwise, without using\n * conditional operators.\n *\n * This should get inlined into `advanceBlock` below by the JIT.\n *\n * Adapted from https://stackoverflow.com/a/3912218/434243\n */\nfunction oneIfNotZero(n) {\n return ((n | -n) >> 31) & 1;\n}\n/**\n * Block calculation step of the algorithm.\n *\n * From Fig 8. on p. 408 of [1], additionally optimized to replace conditional\n * checks with bitwise operations as per Section 4.2.3 of [2].\n *\n * @param ctx - The pattern context object\n * @param peq - The `peq` array for the current character (`ctx.peq.get(ch)`)\n * @param b - The block level\n * @param hIn - Horizontal input delta ∈ {1,0,-1}\n * @return Horizontal output delta ∈ {1,0,-1}\n */\nfunction advanceBlock(ctx, peq, b, hIn) {\n let pV = ctx.P[b];\n let mV = ctx.M[b];\n const hInIsNegative = hIn >>> 31; // 1 if hIn < 0 or 0 otherwise.\n const eq = peq[b] | hInIsNegative;\n // Step 1: Compute horizontal deltas.\n const xV = eq | mV;\n const xH = (((eq & pV) + pV) ^ pV) | eq;\n let pH = mV | ~(xH | pV);\n let mH = pV & xH;\n // Step 2: Update score (value of last row of this block).\n const hOut = oneIfNotZero(pH & ctx.lastRowMask[b]) -\n oneIfNotZero(mH & ctx.lastRowMask[b]);\n // Step 3: Update vertical deltas for use when processing next char.\n pH <<= 1;\n mH <<= 1;\n mH |= hInIsNegative;\n pH |= oneIfNotZero(hIn) - hInIsNegative; // set pH[0] if hIn > 0\n pV = mH | ~(xV | pH);\n mV = pH & xV;\n ctx.P[b] = pV;\n ctx.M[b] = mV;\n return hOut;\n}\n/**\n * Find the ends and error counts for matches of `pattern` in `text`.\n *\n * Only the matches with the lowest error count are reported. Other matches\n * with error counts <= maxErrors are discarded.\n *\n * This is the block-based search algorithm from Fig. 9 on p.410 of [1].\n */\nfunction findMatchEnds(text, pattern, maxErrors) {\n if (pattern.length === 0) {\n return [];\n }\n // Clamp error count so we can rely on the `maxErrors` and `pattern.length`\n // rows being in the same block below.\n maxErrors = Math.min(maxErrors, pattern.length);\n const matches = [];\n // Word size.\n const w = 32;\n // Index of maximum block level.\n const bMax = Math.ceil(pattern.length / w) - 1;\n // Context used across block calculations.\n const ctx = {\n P: new Uint32Array(bMax + 1),\n M: new Uint32Array(bMax + 1),\n lastRowMask: new Uint32Array(bMax + 1),\n };\n ctx.lastRowMask.fill(1 << 31);\n ctx.lastRowMask[bMax] = 1 << (pattern.length - 1) % w;\n // Dummy \"peq\" array for chars in the text which do not occur in the pattern.\n const emptyPeq = new Uint32Array(bMax + 1);\n // Map of UTF-16 character code to bit vector indicating positions in the\n // pattern that equal that character.\n const peq = new Map();\n // Version of `peq` that only stores mappings for small characters. This\n // allows faster lookups when iterating through the text because a simple\n // array lookup can be done instead of a hash table lookup.\n const asciiPeq = [];\n for (let i = 0; i < 256; i++) {\n asciiPeq.push(emptyPeq);\n }\n // Calculate `ctx.peq` - a map of character values to bitmasks indicating\n // positions of that character within the pattern, where each bit represents\n // a position in the pattern.\n for (let c = 0; c < pattern.length; c += 1) {\n const val = pattern.charCodeAt(c);\n if (peq.has(val)) {\n // Duplicate char in pattern.\n continue;\n }\n const charPeq = new Uint32Array(bMax + 1);\n peq.set(val, charPeq);\n if (val < asciiPeq.length) {\n asciiPeq[val] = charPeq;\n }\n for (let b = 0; b <= bMax; b += 1) {\n charPeq[b] = 0;\n // Set all the bits where the pattern matches the current char (ch).\n // For indexes beyond the end of the pattern, always set the bit as if the\n // pattern contained a wildcard char in that position.\n for (let r = 0; r < w; r += 1) {\n const idx = b * w + r;\n if (idx >= pattern.length) {\n continue;\n }\n const match = pattern.charCodeAt(idx) === val;\n if (match) {\n charPeq[b] |= 1 << r;\n }\n }\n }\n }\n // Index of last-active block level in the column.\n let y = Math.max(0, Math.ceil(maxErrors / w) - 1);\n // Initialize maximum error count at bottom of each block.\n const score = new Uint32Array(bMax + 1);\n for (let b = 0; b <= y; b += 1) {\n score[b] = (b + 1) * w;\n }\n score[bMax] = pattern.length;\n // Initialize vertical deltas for each block.\n for (let b = 0; b <= y; b += 1) {\n ctx.P[b] = ~0;\n ctx.M[b] = 0;\n }\n // Process each char of the text, computing the error count for `w` chars of\n // the pattern at a time.\n for (let j = 0; j < text.length; j += 1) {\n // Lookup the bitmask representing the positions of the current char from\n // the text within the pattern.\n const charCode = text.charCodeAt(j);\n let charPeq;\n if (charCode < asciiPeq.length) {\n // Fast array lookup.\n charPeq = asciiPeq[charCode];\n }\n else {\n // Slower hash table lookup.\n charPeq = peq.get(charCode);\n if (typeof charPeq === \"undefined\") {\n charPeq = emptyPeq;\n }\n }\n // Calculate error count for blocks that we definitely have to process for\n // this column.\n let carry = 0;\n for (let b = 0; b <= y; b += 1) {\n carry = advanceBlock(ctx, charPeq, b, carry);\n score[b] += carry;\n }\n // Check if we also need to compute an additional block, or if we can reduce\n // the number of blocks processed for the next column.\n if (score[y] - carry <= maxErrors &&\n y < bMax &&\n (charPeq[y + 1] & 1 || carry < 0)) {\n // Error count for bottom block is under threshold, increase the number of\n // blocks processed for this column & next by 1.\n y += 1;\n ctx.P[y] = ~0;\n ctx.M[y] = 0;\n let maxBlockScore;\n if (y === bMax) {\n const remainder = pattern.length % w;\n maxBlockScore = remainder === 0 ? w : remainder;\n }\n else {\n maxBlockScore = w;\n }\n score[y] =\n score[y - 1] +\n maxBlockScore -\n carry +\n advanceBlock(ctx, charPeq, y, carry);\n }\n else {\n // Error count for bottom block exceeds threshold, reduce the number of\n // blocks processed for the next column.\n while (y > 0 && score[y] >= maxErrors + w) {\n y -= 1;\n }\n }\n // If error count is under threshold, report a match.\n if (y === bMax && score[y] <= maxErrors) {\n if (score[y] < maxErrors) {\n // Discard any earlier, worse matches.\n matches.splice(0, matches.length);\n }\n matches.push({\n start: -1,\n end: j + 1,\n errors: score[y],\n });\n // Because `search` only reports the matches with the lowest error count,\n // we can \"ratchet down\" the max error threshold whenever a match is\n // encountered and thereby save a small amount of work for the remainder\n // of the text.\n maxErrors = score[y];\n }\n }\n return matches;\n}\n/**\n * Search for matches for `pattern` in `text` allowing up to `maxErrors` errors.\n *\n * Returns the start, and end positions and error counts for each lowest-cost\n * match. Only the \"best\" matches are returned.\n */\nexport default function search(text, pattern, maxErrors) {\n const matches = findMatchEnds(text, pattern, maxErrors);\n return findMatchStarts(text, pattern, matches);\n}\n","import approxSearch from 'approx-string-match';\n\n/**\n * @typedef {import('approx-string-match').Match} StringMatch\n */\n\n/**\n * @typedef Match\n * @prop {number} start - Start offset of match in text\n * @prop {number} end - End offset of match in text\n * @prop {number} score -\n * Score for the match between 0 and 1.0, where 1.0 indicates a perfect match\n * for the quote and context.\n */\n\n/**\n * Find the best approximate matches for `str` in `text` allowing up to `maxErrors` errors.\n *\n * @param {string} text\n * @param {string} str\n * @param {number} maxErrors\n * @return {StringMatch[]}\n */\nfunction search(text, str, maxErrors) {\n // Do a fast search for exact matches. The `approx-string-match` library\n // doesn't currently incorporate this optimization itself.\n let matchPos = 0;\n let exactMatches = [];\n while (matchPos !== -1) {\n matchPos = text.indexOf(str, matchPos);\n if (matchPos !== -1) {\n exactMatches.push({\n start: matchPos,\n end: matchPos + str.length,\n errors: 0,\n });\n matchPos += 1;\n }\n }\n if (exactMatches.length > 0) {\n return exactMatches;\n }\n\n // If there are no exact matches, do a more expensive search for matches\n // with errors.\n return approxSearch(text, str, maxErrors);\n}\n\n/**\n * Compute a score between 0 and 1.0 for the similarity between `text` and `str`.\n *\n * @param {string} text\n * @param {string} str\n */\nfunction textMatchScore(text, str) {\n // `search` will return no matches if either the text or pattern is empty,\n // otherwise it will return at least one match if the max allowed error count\n // is at least `str.length`.\n if (str.length === 0 || text.length === 0) {\n return 0.0;\n }\n\n const matches = search(text, str, str.length);\n\n // prettier-ignore\n return 1 - (matches[0].errors / str.length);\n}\n\n/**\n * Find the best approximate match for `quote` in `text`.\n *\n * Returns `null` if no match exceeding the minimum quality threshold was found.\n *\n * @param {string} text - Document text to search\n * @param {string} quote - String to find within `text`\n * @param {object} context -\n * Context in which the quote originally appeared. This is used to choose the\n * best match.\n * @param {string} [context.prefix] - Expected text before the quote\n * @param {string} [context.suffix] - Expected text after the quote\n * @param {number} [context.hint] - Expected offset of match within text\n * @return {Match|null}\n */\nexport function matchQuote(text, quote, context = {}) {\n if (quote.length === 0) {\n return null;\n }\n\n // Choose the maximum number of errors to allow for the initial search.\n // This choice involves a tradeoff between:\n //\n // - Recall (proportion of \"good\" matches found)\n // - Precision (proportion of matches found which are \"good\")\n // - Cost of the initial search and of processing the candidate matches [1]\n //\n // [1] Specifically, the expected-time complexity of the initial search is\n // `O((maxErrors / 32) * text.length)`. See `approx-string-match` docs.\n const maxErrors = Math.min(256, quote.length / 2);\n\n // Find closest matches for `quote` in `text` based on edit distance.\n const matches = search(text, quote, maxErrors);\n\n if (matches.length === 0) {\n return null;\n }\n\n /**\n * Compute a score between 0 and 1.0 for a match candidate.\n *\n * @param {StringMatch} match\n */\n const scoreMatch = match => {\n const quoteWeight = 50; // Similarity of matched text to quote.\n const prefixWeight = 20; // Similarity of text before matched text to `context.prefix`.\n const suffixWeight = 20; // Similarity of text after matched text to `context.suffix`.\n const posWeight = 2; // Proximity to expected location. Used as a tie-breaker.\n\n const quoteScore = 1 - match.errors / quote.length;\n\n const prefixScore = context.prefix\n ? textMatchScore(\n text.slice(\n Math.max(0, match.start - context.prefix.length),\n match.start\n ),\n context.prefix\n )\n : 1.0;\n const suffixScore = context.suffix\n ? textMatchScore(\n text.slice(match.end, match.end + context.suffix.length),\n context.suffix\n )\n : 1.0;\n\n let posScore = 1.0;\n if (typeof context.hint === 'number') {\n const offset = Math.abs(match.start - context.hint);\n posScore = 1.0 - offset / text.length;\n }\n\n const rawScore =\n quoteWeight * quoteScore +\n prefixWeight * prefixScore +\n suffixWeight * suffixScore +\n posWeight * posScore;\n const maxScore = quoteWeight + prefixWeight + suffixWeight + posWeight;\n const normalizedScore = rawScore / maxScore;\n\n return normalizedScore;\n };\n\n // Rank matches based on similarity of actual and expected surrounding text\n // and actual/expected offset in the document text.\n const scoredMatches = matches.map(m => ({\n start: m.start,\n end: m.end,\n score: scoreMatch(m),\n }));\n\n // Choose match with highest score.\n scoredMatches.sort((a, b) => b.score - a.score);\n return scoredMatches[0];\n}\n","/**\n * Get the node name for use in generating an xpath expression.\n *\n * @param {Node} node\n */\nfunction getNodeName(node) {\n const nodeName = node.nodeName.toLowerCase();\n let result = nodeName;\n if (nodeName === '#text') {\n result = 'text()';\n }\n return result;\n}\n\n/**\n * Get the index of the node as it appears in its parent's child list\n *\n * @param {Node} node\n */\nfunction getNodePosition(node) {\n let pos = 0;\n /** @type {Node|null} */\n let tmp = node;\n while (tmp) {\n if (tmp.nodeName === node.nodeName) {\n pos += 1;\n }\n tmp = tmp.previousSibling;\n }\n return pos;\n}\n\n/** @param {Node} node */\nfunction getPathSegment(node) {\n const name = getNodeName(node);\n const pos = getNodePosition(node);\n return `${name}[${pos}]`;\n}\n\n/**\n * A simple XPath generator which can generate XPaths of the form\n * /tag[index]/tag[index].\n *\n * @param {Node} node - The node to generate a path to\n * @param {Node} root - Root node to which the returned path is relative\n */\nexport function xpathFromNode(node, root) {\n let xpath = '';\n\n /** @type {Node|null} */\n let elem = node;\n while (elem !== root) {\n if (!elem) {\n throw new Error('Node is not a descendant of root');\n }\n xpath = getPathSegment(elem) + '/' + xpath;\n elem = elem.parentNode;\n }\n xpath = '/' + xpath;\n xpath = xpath.replace(/\\/$/, ''); // Remove trailing slash\n\n return xpath;\n}\n\n/**\n * Return the `index`'th immediate child of `element` whose tag name is\n * `nodeName` (case insensitive).\n *\n * @param {Element} element\n * @param {string} nodeName\n * @param {number} index\n */\nfunction nthChildOfType(element, nodeName, index) {\n nodeName = nodeName.toUpperCase();\n\n let matchIndex = -1;\n for (let i = 0; i < element.children.length; i++) {\n const child = element.children[i];\n if (child.nodeName.toUpperCase() === nodeName) {\n ++matchIndex;\n if (matchIndex === index) {\n return child;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Evaluate a _simple XPath_ relative to a `root` element and return the\n * matching element.\n *\n * A _simple XPath_ is a sequence of one or more `/tagName[index]` strings.\n *\n * Unlike `document.evaluate` this function:\n *\n * - Only supports simple XPaths\n * - Is not affected by the document's _type_ (HTML or XML/XHTML)\n * - Ignores element namespaces when matching element names in the XPath against\n * elements in the DOM tree\n * - Is case insensitive for all elements, not just HTML elements\n *\n * The matching element is returned or `null` if no such element is found.\n * An error is thrown if `xpath` is not a simple XPath.\n *\n * @param {string} xpath\n * @param {Element} root\n * @return {Element|null}\n */\nfunction evaluateSimpleXPath(xpath, root) {\n const isSimpleXPath =\n xpath.match(/^(\\/[A-Za-z0-9-]+(\\[[0-9]+\\])?)+$/) !== null;\n if (!isSimpleXPath) {\n throw new Error('Expression is not a simple XPath');\n }\n\n const segments = xpath.split('/');\n let element = root;\n\n // Remove leading empty segment. The regex above validates that the XPath\n // has at least two segments, with the first being empty and the others non-empty.\n segments.shift();\n\n for (let segment of segments) {\n let elementName;\n let elementIndex;\n\n const separatorPos = segment.indexOf('[');\n if (separatorPos !== -1) {\n elementName = segment.slice(0, separatorPos);\n\n const indexStr = segment.slice(separatorPos + 1, segment.indexOf(']'));\n elementIndex = parseInt(indexStr) - 1;\n if (elementIndex < 0) {\n return null;\n }\n } else {\n elementName = segment;\n elementIndex = 0;\n }\n\n const child = nthChildOfType(element, elementName, elementIndex);\n if (!child) {\n return null;\n }\n\n element = child;\n }\n\n return element;\n}\n\n/**\n * Finds an element node using an XPath relative to `root`\n *\n * Example:\n * node = nodeFromXPath('/main/article[1]/p[3]', document.body)\n *\n * @param {string} xpath\n * @param {Element} [root]\n * @return {Node|null}\n */\nexport function nodeFromXPath(xpath, root = document.body) {\n try {\n return evaluateSimpleXPath(xpath, root);\n } catch (err) {\n return document.evaluate(\n '.' + xpath,\n root,\n\n // nb. The `namespaceResolver` and `result` arguments are optional in the spec\n // but required in Edge Legacy.\n null /* namespaceResolver */,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null /* result */\n ).singleNodeValue;\n }\n}\n","/**\n * This module exports a set of classes for converting between DOM `Range`\n * objects and different types of selectors. It is mostly a thin wrapper around a\n * set of anchoring libraries. It serves two main purposes:\n *\n * 1. Providing a consistent interface across different types of anchors.\n * 2. Insulating the rest of the code from API changes in the underlying anchoring\n * libraries.\n */\n\nimport { matchQuote } from './match-quote';\nimport { TextRange, TextPosition } from './text-range';\nimport { nodeFromXPath, xpathFromNode } from './xpath';\n\n/**\n * @typedef {import('../../types/api').RangeSelector} RangeSelector\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n */\n\n/**\n * Converts between `RangeSelector` selectors and `Range` objects.\n */\nexport class RangeAnchor {\n /**\n * @param {Node} root - A root element from which to anchor.\n * @param {Range} range - A range describing the anchor.\n */\n constructor(root, range) {\n this.root = root;\n this.range = range;\n }\n\n /**\n * @param {Node} root - A root element from which to anchor.\n * @param {Range} range - A range describing the anchor.\n */\n static fromRange(root, range) {\n return new RangeAnchor(root, range);\n }\n\n /**\n * Create an anchor from a serialized `RangeSelector` selector.\n *\n * @param {Element} root - A root element from which to anchor.\n * @param {RangeSelector} selector\n */\n static fromSelector(root, selector) {\n const startContainer = nodeFromXPath(selector.startContainer, root);\n if (!startContainer) {\n throw new Error('Failed to resolve startContainer XPath');\n }\n\n const endContainer = nodeFromXPath(selector.endContainer, root);\n if (!endContainer) {\n throw new Error('Failed to resolve endContainer XPath');\n }\n\n const startPos = TextPosition.fromCharOffset(\n startContainer,\n selector.startOffset\n );\n const endPos = TextPosition.fromCharOffset(\n endContainer,\n selector.endOffset\n );\n\n const range = new TextRange(startPos, endPos).toRange();\n return new RangeAnchor(root, range);\n }\n\n toRange() {\n return this.range;\n }\n\n /**\n * @return {RangeSelector}\n */\n toSelector() {\n // \"Shrink\" the range so that it tightly wraps its text. This ensures more\n // predictable output for a given text selection.\n const normalizedRange = TextRange.fromRange(this.range).toRange();\n\n const textRange = TextRange.fromRange(normalizedRange);\n const startContainer = xpathFromNode(textRange.start.element, this.root);\n const endContainer = xpathFromNode(textRange.end.element, this.root);\n\n return {\n type: 'RangeSelector',\n startContainer,\n startOffset: textRange.start.offset,\n endContainer,\n endOffset: textRange.end.offset,\n };\n }\n}\n\n/**\n * Converts between `TextPositionSelector` selectors and `Range` objects.\n */\nexport class TextPositionAnchor {\n /**\n * @param {Element} root\n * @param {number} start\n * @param {number} end\n */\n constructor(root, start, end) {\n this.root = root;\n this.start = start;\n this.end = end;\n }\n\n /**\n * @param {Element} root\n * @param {Range} range\n */\n static fromRange(root, range) {\n const textRange = TextRange.fromRange(range).relativeTo(root);\n return new TextPositionAnchor(\n root,\n textRange.start.offset,\n textRange.end.offset\n );\n }\n /**\n * @param {Element} root\n * @param {TextPositionSelector} selector\n */\n static fromSelector(root, selector) {\n return new TextPositionAnchor(root, selector.start, selector.end);\n }\n\n /**\n * @return {TextPositionSelector}\n */\n toSelector() {\n return {\n type: 'TextPositionSelector',\n start: this.start,\n end: this.end,\n };\n }\n\n toRange() {\n return TextRange.fromOffsets(this.root, this.start, this.end).toRange();\n }\n}\n\n/**\n * @typedef QuoteMatchOptions\n * @prop {number} [hint] - Expected position of match in text. See `matchQuote`.\n */\n\n/**\n * Converts between `TextQuoteSelector` selectors and `Range` objects.\n */\nexport class TextQuoteAnchor {\n /**\n * @param {Element} root - A root element from which to anchor.\n * @param {string} exact\n * @param {object} context\n * @param {string} [context.prefix]\n * @param {string} [context.suffix]\n */\n constructor(root, exact, context = {}) {\n this.root = root;\n this.exact = exact;\n this.context = context;\n }\n\n /**\n * Create a `TextQuoteAnchor` from a range.\n *\n * Will throw if `range` does not contain any text nodes.\n *\n * @param {Element} root\n * @param {Range} range\n */\n static fromRange(root, range) {\n const text = /** @type {string} */ (root.textContent);\n const textRange = TextRange.fromRange(range).relativeTo(root);\n\n const start = textRange.start.offset;\n const end = textRange.end.offset;\n\n // Number of characters around the quote to capture as context. We currently\n // always use a fixed amount, but it would be better if this code was aware\n // of logical boundaries in the document (paragraph, article etc.) to avoid\n // capturing text unrelated to the quote.\n //\n // In regular prose the ideal content would often be the surrounding sentence.\n // This is a natural unit of meaning which enables displaying quotes in\n // context even when the document is not available. We could use `Intl.Segmenter`\n // for this when available.\n const contextLen = 32;\n\n return new TextQuoteAnchor(root, text.slice(start, end), {\n prefix: text.slice(Math.max(0, start - contextLen), start),\n suffix: text.slice(end, Math.min(text.length, end + contextLen)),\n });\n }\n\n /**\n * @param {Element} root\n * @param {TextQuoteSelector} selector\n */\n static fromSelector(root, selector) {\n const { prefix, suffix } = selector;\n return new TextQuoteAnchor(root, selector.exact, { prefix, suffix });\n }\n\n /**\n * @return {TextQuoteSelector}\n */\n toSelector() {\n return {\n type: 'TextQuoteSelector',\n exact: this.exact,\n prefix: this.context.prefix,\n suffix: this.context.suffix,\n };\n }\n\n /**\n * @param {QuoteMatchOptions} [options]\n */\n toRange(options = {}) {\n return this.toPositionAnchor(options).toRange();\n }\n\n /**\n * @param {QuoteMatchOptions} [options]\n */\n toPositionAnchor(options = {}) {\n const text = /** @type {string} */ (this.root.textContent);\n const match = matchQuote(text, this.exact, {\n ...this.context,\n hint: options.hint,\n });\n if (!match) {\n throw new Error('Quote not found');\n }\n return new TextPositionAnchor(this.root, match.start, match.end);\n }\n}\n","import { RangeAnchor, TextPositionAnchor, TextQuoteAnchor } from './types';\n\n/**\n * @typedef {import('../../types/api').RangeSelector} RangeSelector\n * @typedef {import('../../types/api').Selector} Selector\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n */\n\n/**\n * @param {RangeAnchor|TextPositionAnchor|TextQuoteAnchor} anchor\n * @param {object} [options]\n * @param {number} [options.hint]\n */\nasync function querySelector(anchor, options = {}) {\n return anchor.toRange(options);\n}\n\n/**\n * Anchor a set of selectors.\n *\n * This function converts a set of selectors into a document range.\n * It encapsulates the core anchoring algorithm, using the selectors alone or\n * in combination to establish the best anchor within the document.\n *\n * @param {Element} root - The root element of the anchoring context.\n * @param {Selector[]} selectors - The selectors to try.\n * @param {object} [options]\n * @param {number} [options.hint]\n */\nexport function anchor(root, selectors, options = {}) {\n let position = /** @type {TextPositionSelector|null} */ (null);\n let quote = /** @type {TextQuoteSelector|null} */ (null);\n let range = /** @type {RangeSelector|null} */ (null);\n\n // Collect all the selectors\n for (let selector of selectors) {\n switch (selector.type) {\n case 'TextPositionSelector':\n position = selector;\n options.hint = position.start; // TextQuoteAnchor hint\n break;\n case 'TextQuoteSelector':\n quote = selector;\n break;\n case 'RangeSelector':\n range = selector;\n break;\n }\n }\n\n /**\n * Assert the quote matches the stored quote, if applicable\n * @param {Range} range\n */\n const maybeAssertQuote = range => {\n if (quote?.exact && range.toString() !== quote.exact) {\n throw new Error('quote mismatch');\n } else {\n return range;\n }\n };\n\n // From a default of failure, we build up catch clauses to try selectors in\n // order, from simple to complex.\n /** @type {Promise<Range>} */\n let promise = Promise.reject('unable to anchor');\n\n if (range) {\n // Const binding assures TS that it won't be re-assigned when callback runs.\n const range_ = range;\n promise = promise.catch(() => {\n let anchor = RangeAnchor.fromSelector(root, range_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (position) {\n const position_ = position;\n promise = promise.catch(() => {\n let anchor = TextPositionAnchor.fromSelector(root, position_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (quote) {\n const quote_ = quote;\n promise = promise.catch(() => {\n let anchor = TextQuoteAnchor.fromSelector(root, quote_);\n return querySelector(anchor, options);\n });\n }\n\n return promise;\n}\n\n/**\n * @param {Element} root\n * @param {Range} range\n */\nexport function describe(root, range) {\n const types = [RangeAnchor, TextPositionAnchor, TextQuoteAnchor];\n const result = [];\n for (let type of types) {\n try {\n const anchor = type.fromRange(root, range);\n result.push(anchor.toSelector());\n } catch (error) {\n continue;\n }\n }\n return result;\n}\n","/**\n * Return a normalized version of a URI.\n *\n * This makes it absolute and strips the fragment identifier.\n *\n * @param {string} uri - Relative or absolute URL\n * @param {string} [base] - Base URL to resolve relative to. Defaults to\n * the document's base URL.\n */\nexport function normalizeURI(uri, base = document.baseURI) {\n const absUrl = new URL(uri, base).href;\n\n // Remove the fragment identifier.\n // This is done on the serialized URL rather than modifying `url.hash` due to\n // a bug in Safari.\n // See https://github.com/hypothesis/h/issues/3471#issuecomment-226713750\n return absUrl.toString().replace(/#.*/, '');\n}\n","/*\n ** Adapted from:\n ** https://github.com/openannotation/annotator/blob/v1.2.x/src/plugin/document.coffee\n **\n ** Annotator v1.2.10\n ** https://github.com/openannotation/annotator\n **\n ** Copyright 2015, the Annotator project contributors.\n ** Dual licensed under the MIT and GPLv3 licenses.\n ** https://github.com/openannotation/annotator/blob/master/LICENSE\n */\n\n/**\n * nb. The `DocumentMetadata` type is renamed to avoid a conflict with the\n * `DocumentMetadata` class below.\n *\n * @typedef {import('../../types/annotator').DocumentMetadata} Metadata\n */\n\nimport { normalizeURI } from '../util/url';\n\n/**\n * @typedef Link\n * @prop {string} link.href\n * @prop {string} [link.rel]\n * @prop {string} [link.type]\n */\n\n/**\n * Extension of the `Metadata` type with non-optional fields for `dc`, `eprints` etc.\n *\n * @typedef HTMLDocumentMetadata\n * @prop {string} title\n * @prop {Link[]} link\n * @prop {Record<string, string[]>} dc\n * @prop {Record<string, string[]>} eprints\n * @prop {Record<string, string[]>} facebook\n * @prop {Record<string, string[]>} highwire\n * @prop {Record<string, string[]>} prism\n * @prop {Record<string, string[]>} twitter\n * @prop {string} [favicon]\n * @prop {string} [documentFingerprint]\n */\n\n/**\n * HTMLMetadata reads metadata/links from the current HTML document.\n */\nexport class HTMLMetadata {\n /**\n * @param {object} [options]\n * @param {Document} [options.document]\n */\n constructor(options = {}) {\n this.document = options.document || document;\n }\n\n /**\n * Returns the primary URI for the document being annotated\n *\n * @return {string}\n */\n uri() {\n let uri = decodeURIComponent(this._getDocumentHref());\n\n // Use the `link[rel=canonical]` element's href as the URL if present.\n const links = this._getLinks();\n for (let link of links) {\n if (link.rel === 'canonical') {\n uri = link.href;\n }\n }\n\n return uri;\n }\n\n /**\n * Return metadata for the current page.\n *\n * @return {HTMLDocumentMetadata}\n */\n getDocumentMetadata() {\n /** @type {HTMLDocumentMetadata} */\n const metadata = {\n title: document.title,\n link: [],\n\n dc: this._getMetaTags('name', 'dc.'),\n eprints: this._getMetaTags('name', 'eprints.'),\n facebook: this._getMetaTags('property', 'og:'),\n highwire: this._getMetaTags('name', 'citation_'),\n prism: this._getMetaTags('name', 'prism.'),\n twitter: this._getMetaTags('name', 'twitter:'),\n };\n\n const favicon = this._getFavicon();\n if (favicon) {\n metadata.favicon = favicon;\n }\n\n metadata.title = this._getTitle(metadata);\n metadata.link = this._getLinks(metadata);\n\n const dcLink = metadata.link.find(link => link.href.startsWith('urn:x-dc'));\n if (dcLink) {\n metadata.documentFingerprint = dcLink.href;\n }\n\n return metadata;\n }\n\n /**\n * Return an array of all the `content` values of `<meta>` tags on the page\n * where the value of the attribute begins with `<prefix>`.\n *\n * @param {string} attribute\n * @param {string} prefix - it is interpreted as a regex\n * @return {Record<string,string[]>}\n */\n _getMetaTags(attribute, prefix) {\n /** @type {Record<string,string[]>} */\n const tags = {};\n for (let meta of Array.from(this.document.querySelectorAll('meta'))) {\n const name = meta.getAttribute(attribute);\n const { content } = meta;\n if (name && content) {\n const match = name.match(RegExp(`^${prefix}(.+)$`, 'i'));\n if (match) {\n const key = match[1].toLowerCase();\n if (tags[key]) {\n tags[key].push(content);\n } else {\n tags[key] = [content];\n }\n }\n }\n }\n return tags;\n }\n\n /** @param {HTMLDocumentMetadata} metadata */\n _getTitle(metadata) {\n if (metadata.highwire.title) {\n return metadata.highwire.title[0];\n } else if (metadata.eprints.title) {\n return metadata.eprints.title[0];\n } else if (metadata.prism.title) {\n return metadata.prism.title[0];\n } else if (metadata.facebook.title) {\n return metadata.facebook.title[0];\n } else if (metadata.twitter.title) {\n return metadata.twitter.title[0];\n } else if (metadata.dc.title) {\n return metadata.dc.title[0];\n } else {\n return this.document.title;\n }\n }\n\n /**\n * Get document URIs from `<link>` and `<meta>` elements on the page.\n *\n * @param {Pick<HTMLDocumentMetadata, 'highwire'|'dc'>} [metadata] -\n * Dublin Core and Highwire metadata parsed from `<meta>` tags.\n * @return {Link[]}\n */\n _getLinks(metadata = { dc: {}, highwire: {} }) {\n /** @type {Link[]} */\n const links = [{ href: this._getDocumentHref() }];\n\n // Extract links from `<link>` tags with certain `rel` values.\n const linkElements = Array.from(this.document.querySelectorAll('link'));\n for (let link of linkElements) {\n if (\n !['alternate', 'canonical', 'bookmark', 'shortlink'].includes(link.rel)\n ) {\n continue;\n }\n\n if (link.rel === 'alternate') {\n // Ignore RSS feed links.\n if (link.type && link.type.match(/^application\\/(rss|atom)\\+xml/)) {\n continue;\n }\n // Ignore alternate languages.\n if (link.hreflang) {\n continue;\n }\n }\n\n try {\n const href = this._absoluteUrl(link.href);\n links.push({ href, rel: link.rel, type: link.type });\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n\n // Look for links in scholar metadata\n for (let name of Object.keys(metadata.highwire)) {\n const values = metadata.highwire[name];\n if (name === 'pdf_url') {\n for (let url of values) {\n try {\n links.push({\n href: this._absoluteUrl(url),\n type: 'application/pdf',\n });\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n\n // Kind of a hack to express DOI identifiers as links but it's a\n // convenient place to look them up later, and somewhat sane since\n // they don't have a type.\n if (name === 'doi') {\n for (let doi of values) {\n if (doi.slice(0, 4) !== 'doi:') {\n doi = `doi:${doi}`;\n }\n links.push({ href: doi });\n }\n }\n }\n\n // Look for links in Dublin Core data\n for (let name of Object.keys(metadata.dc)) {\n const values = metadata.dc[name];\n if (name === 'identifier') {\n for (let id of values) {\n if (id.slice(0, 4) === 'doi:') {\n links.push({ href: id });\n }\n }\n }\n }\n\n // Look for a link to identify the resource in Dublin Core metadata\n const dcRelationValues = metadata.dc['relation.ispartof'];\n const dcIdentifierValues = metadata.dc.identifier;\n if (dcRelationValues && dcIdentifierValues) {\n const dcUrnRelationComponent =\n dcRelationValues[dcRelationValues.length - 1];\n const dcUrnIdentifierComponent =\n dcIdentifierValues[dcIdentifierValues.length - 1];\n const dcUrn =\n 'urn:x-dc:' +\n encodeURIComponent(dcUrnRelationComponent) +\n '/' +\n encodeURIComponent(dcUrnIdentifierComponent);\n links.push({ href: dcUrn });\n }\n\n return links;\n }\n\n _getFavicon() {\n let favicon = null;\n for (let link of Array.from(this.document.querySelectorAll('link'))) {\n if (['shortcut icon', 'icon'].includes(link.rel)) {\n try {\n favicon = this._absoluteUrl(link.href);\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n return favicon;\n }\n\n /**\n * Convert a possibly relative URI to an absolute one. This will throw an\n * exception if the URL cannot be parsed.\n *\n * @param {string} url\n */\n _absoluteUrl(url) {\n return normalizeURI(url, this.document.baseURI);\n }\n\n // Get the true URI record when it's masked via a different protocol.\n // This happens when an href is set with a uri using the 'blob:' protocol\n // but the document can set a different uri through a <base> tag.\n _getDocumentHref() {\n const { href } = this.document.location;\n const allowedSchemes = ['http:', 'https:', 'file:'];\n\n // Use the current document location if it has a recognized scheme.\n const scheme = new URL(href).protocol;\n if (allowedSchemes.includes(scheme)) {\n return href;\n }\n\n // Otherwise, try using the location specified by the <base> element.\n if (\n this.document.baseURI &&\n allowedSchemes.includes(new URL(this.document.baseURI).protocol)\n ) {\n return this.document.baseURI;\n }\n\n // Fall back to returning the document URI, even though the scheme is not\n // in the allowed list.\n return href;\n }\n}\n","/**\n * Return the intersection of two rects.\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function intersectRects(rectA, rectB) {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return `true` if a rect is _empty_.\n *\n * An empty rect is defined as one with zero or negative width/height, eg.\n * as returned by `new DOMRect()` or `Element.getBoundingClientRect()` for a\n * hidden element.\n *\n * @param {DOMRect} rect\n */\nexport function rectIsEmpty(rect) {\n return rect.width <= 0 || rect.height <= 0;\n}\n\n/**\n * Return true if the 1D lines a-b and c-d overlap (ie. the length of their\n * intersection is non-zero).\n *\n * For example, the following lines overlap:\n *\n * a----b\n * c------d\n *\n * The inputs must be normalized such that b >= a and d >= c.\n *\n * @param {number} a\n * @param {number} b\n * @param {number} c\n * @param {number} d\n */\nfunction linesOverlap(a, b, c, d) {\n const maxStart = Math.max(a, c);\n const minEnd = Math.min(b, d);\n return maxStart < minEnd;\n}\n\n/**\n * Return true if the intersection of `rectB` and `rectA` is non-empty.\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function rectIntersects(rectA, rectB) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n linesOverlap(rectA.left, rectA.right, rectB.left, rectB.right) &&\n linesOverlap(rectA.top, rectA.bottom, rectB.top, rectB.bottom)\n );\n}\n\n/**\n * Return true if `rectB` is fully contained within `rectA`\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function rectContains(rectA, rectB) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n rectB.left >= rectA.left &&\n rectB.right <= rectA.right &&\n rectB.top >= rectA.top &&\n rectB.bottom <= rectA.bottom\n );\n}\n\n/**\n * Return true if two rects overlap vertically.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function rectsOverlapVertically(a, b) {\n return linesOverlap(a.top, a.bottom, b.top, b.bottom);\n}\n\n/**\n * Return true if two rects overlap horizontally.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function rectsOverlapHorizontally(a, b) {\n return linesOverlap(a.left, a.right, b.left, b.right);\n}\n\n/**\n * Return the union of two rects.\n *\n * The union of an empty rect (see {@link rectIsEmpty}) with a non-empty rect is\n * defined to be the non-empty rect. The union of two empty rects is an empty\n * rect.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function unionRects(a, b) {\n if (rectIsEmpty(a)) {\n return b;\n } else if (rectIsEmpty(b)) {\n return a;\n }\n\n const left = Math.min(a.left, b.left);\n const top = Math.min(a.top, b.top);\n const right = Math.max(a.right, b.right);\n const bottom = Math.max(a.bottom, b.bottom);\n\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return the point at the center of a rect.\n *\n * @param {DOMRect} rect\n */\nexport function rectCenter(rect) {\n return new DOMPoint(\n (rect.left + rect.right) / 2,\n (rect.top + rect.bottom) / 2\n );\n}\n","import { rectContains, rectIntersects } from '../util/geometry';\n\n/**\n * CSS selectors used to find elements that are considered potentially part\n * of the main content of a page.\n */\nconst contentSelectors = [\n 'p',\n\n // Paragraphs in VitalSource \"Great Book\" format ebooks.\n '.para',\n];\n\n/**\n * Attempt to guess the region of the page that contains the main content.\n *\n * @param {Element} root\n * @return {{ left: number, right: number }|null} -\n * The left/right content margins or `null` if they could not be determined\n */\nexport function guessMainContentArea(root) {\n // Maps of (margin X coord, votes) for margin positions.\n\n /** @type {Map<number,number>} */\n const leftMarginVotes = new Map();\n\n /** @type {Map<number,number>} */\n const rightMarginVotes = new Map();\n\n // Gather data about the paragraphs of text in the document.\n //\n // In future we might want to expand this to consider other text containers,\n // since some pages, especially eg. in ebooks, may not have any paragraphs\n // (eg. instead they may only contain tables or lists or headings).\n const contentSelector = contentSelectors.join(',');\n const paragraphs = Array.from(root.querySelectorAll(contentSelector))\n .map(p => {\n // Gather some data about them.\n const rect = p.getBoundingClientRect();\n const textLength = /** @type {string} */ (p.textContent).length;\n return { rect, textLength };\n })\n .filter(({ rect }) => {\n // Filter out hidden paragraphs\n return rect.width > 0 && rect.height > 0;\n })\n // Select the paragraphs containing the most text.\n .sort((a, b) => b.textLength - a.textLength)\n .slice(0, 15);\n\n // Let these paragraphs \"vote\" for what the left and right margins of the\n // main content area in the document are.\n paragraphs.forEach(({ rect }) => {\n let leftVotes = leftMarginVotes.get(rect.left) ?? 0;\n leftVotes += 1;\n leftMarginVotes.set(rect.left, leftVotes);\n\n let rightVotes = rightMarginVotes.get(rect.right) ?? 0;\n rightVotes += 1;\n rightMarginVotes.set(rect.right, rightVotes);\n });\n\n // Find the margin values with the most votes.\n if (leftMarginVotes.size === 0 || rightMarginVotes.size === 0) {\n return null;\n }\n\n const leftMargin = [...leftMarginVotes.entries()].sort((a, b) => b[1] - a[1]);\n const rightMargin = [...rightMarginVotes.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n\n const [leftPos] = leftMargin[0];\n const [rightPos] = rightMargin[0];\n\n return { left: leftPos, right: rightPos };\n}\n\n/** @type {Range} */\nlet textRectRange;\n\n/**\n * Return the viewport-relative rect occupied by part of a text node.\n *\n * @param {Text} text\n * @param {number} start\n * @param {number} end\n */\nfunction textRect(text, start = 0, end = text.data.length) {\n if (!textRectRange) {\n // Allocate a range only on the first call to avoid the overhead of\n // constructing and maintaining a large number of live ranges.\n textRectRange = document.createRange();\n }\n textRectRange.setStart(text, start);\n textRectRange.setEnd(text, end);\n return textRectRange.getBoundingClientRect();\n}\n\n/** @param {Element} element */\nfunction hasFixedPosition(element) {\n switch (getComputedStyle(element).position) {\n case 'fixed':\n case 'sticky':\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Return the bounding rect that contains the element's content. Unlike\n * `Element.getBoundingClientRect`, this includes content which overflows\n * the element's specified size.\n *\n * @param {Element} element\n */\nfunction elementContentRect(element) {\n const rect = element.getBoundingClientRect();\n rect.x -= element.scrollLeft;\n rect.y -= element.scrollTop;\n rect.height = Math.max(rect.height, element.scrollHeight);\n rect.width = Math.max(rect.width, element.scrollWidth);\n return rect;\n}\n\n/**\n * Yield all the text node descendants of `root` that intersect `rect`.\n *\n * @param {Element} root\n * @param {DOMRect} rect\n * @param {(el: Element) => boolean} shouldVisit - Optional filter that determines\n * whether to visit a subtree\n * @return {Generator<Text>}\n */\nfunction* textNodesInRect(root, rect, shouldVisit = () => true) {\n /** @type {Node|null} */\n let node = root.firstChild;\n while (node) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = /** @type {Element} */ (node);\n const contentIntersectsRect = rectIntersects(\n elementContentRect(element),\n rect\n );\n\n // Only examine subtrees which are visible.\n if (shouldVisit(element) && contentIntersectsRect) {\n yield* textNodesInRect(element, rect, shouldVisit);\n }\n } else if (node.nodeType === Node.TEXT_NODE) {\n const text = /** @type {Text} */ (node);\n\n // Skip over text nodes which are entirely outside the viewport or empty.\n if (rectIntersects(textRect(text), rect)) {\n yield text;\n }\n }\n node = node.nextSibling;\n }\n}\n\n/**\n * Find content within an element to use as an anchor when applying a layout\n * change to the document.\n *\n * @param {Element} root\n * @param {DOMRect} viewport\n * @return {Range|null} - Range to use as an anchor or `null` if a suitable\n * range could not be found\n */\nfunction getScrollAnchor(root, viewport) {\n // Range representing the content whose position within the viewport we will\n // try to maintain after running the callback.\n let anchorRange = /** @type {Range|null} */ (null);\n\n // Find the first word (non-whitespace substring of a text node) that is fully\n // visible in the viewport.\n\n // Text inside fixed-position elements is ignored because its position won't\n // be affected by a layout change and so it makes a poor scroll anchor.\n /** @param {Element} el */\n const shouldVisit = el => !hasFixedPosition(el);\n\n textNodeLoop: for (let textNode of textNodesInRect(\n root,\n viewport,\n shouldVisit\n )) {\n let textLen = 0;\n\n // Visit all the non-whitespace substrings of the text node.\n for (let word of textNode.data.split(/\\b/)) {\n if (/\\S/.test(word)) {\n const start = textLen;\n const end = textLen + word.length;\n const wordBox = textRect(textNode, start, end);\n if (rectContains(viewport, wordBox)) {\n anchorRange = document.createRange();\n anchorRange.setStart(textNode, start);\n anchorRange.setEnd(textNode, end);\n break textNodeLoop;\n }\n }\n\n textLen += word.length;\n }\n }\n\n return anchorRange;\n}\n\n/**\n * Apply a layout change to the document and preserve the scroll position.\n *\n * This utility selects part of the content in the viewport as an _anchor_\n * and tries to preserve the position of this content within the viewport\n * after the callback is invoked.\n *\n * @param {() => void} callback - Callback that will apply the layout change\n * @param {Element} [scrollRoot]\n * @param {DOMRect} [viewport] - Area to consider \"in the viewport\". Defaults to\n * the viewport of the current window.\n * @return {number} - Amount by which the scroll position was adjusted to keep\n * the anchored content in view\n */\nexport function preserveScrollPosition(\n callback,\n /* istanbul ignore next */\n scrollRoot = document.documentElement,\n /* istanbul ignore next */\n viewport = new DOMRect(0, 0, window.innerWidth, window.innerHeight)\n) {\n const anchor = getScrollAnchor(scrollRoot, viewport);\n if (!anchor) {\n callback();\n return 0;\n }\n\n const anchorTop = anchor.getBoundingClientRect().top;\n callback();\n const newAnchorTop = anchor.getBoundingClientRect().top;\n\n // Determine how far we scrolled as a result of the layout change.\n // This will be positive if the anchor element moved down or negative if it moved up.\n const scrollDelta = newAnchorTop - anchorTop;\n scrollRoot.scrollTop += scrollDelta;\n\n return scrollDelta;\n}\n","var COMPLETE = 'complete',\r\n CANCELED = 'canceled';\r\n\r\nfunction raf(task){\r\n if('requestAnimationFrame' in window){\r\n return window.requestAnimationFrame(task);\r\n }\r\n\r\n setTimeout(task, 16);\r\n}\r\n\r\nfunction setElementScroll(element, x, y){\r\n Math.max(0, x);\r\n Math.max(0, y);\r\n\r\n if(element.self === element){\r\n element.scrollTo(x, y);\r\n }else{\r\n element.scrollLeft = x;\r\n element.scrollTop = y;\r\n }\r\n}\r\n\r\nfunction getTargetScrollLocation(scrollSettings, parent){\r\n var align = scrollSettings.align,\r\n target = scrollSettings.target,\r\n targetPosition = target.getBoundingClientRect(),\r\n parentPosition,\r\n x,\r\n y,\r\n differenceX,\r\n differenceY,\r\n targetWidth,\r\n targetHeight,\r\n leftAlign = align && align.left != null ? align.left : 0.5,\r\n topAlign = align && align.top != null ? align.top : 0.5,\r\n leftOffset = align && align.leftOffset != null ? align.leftOffset : 0,\r\n topOffset = align && align.topOffset != null ? align.topOffset : 0,\r\n leftScalar = leftAlign,\r\n topScalar = topAlign;\r\n\r\n if(scrollSettings.isWindow(parent)){\r\n targetWidth = Math.min(targetPosition.width, parent.innerWidth);\r\n targetHeight = Math.min(targetPosition.height, parent.innerHeight);\r\n x = targetPosition.left + parent.pageXOffset - parent.innerWidth * leftScalar + targetWidth * leftScalar;\r\n y = targetPosition.top + parent.pageYOffset - parent.innerHeight * topScalar + targetHeight * topScalar;\r\n x -= leftOffset;\r\n y -= topOffset;\r\n x = scrollSettings.align.lockX ? parent.pageXOffset : x;\r\n y = scrollSettings.align.lockY ? parent.pageYOffset : y;\r\n differenceX = x - parent.pageXOffset;\r\n differenceY = y - parent.pageYOffset;\r\n }else{\r\n targetWidth = targetPosition.width;\r\n targetHeight = targetPosition.height;\r\n parentPosition = parent.getBoundingClientRect();\r\n var offsetLeft = targetPosition.left - (parentPosition.left - parent.scrollLeft);\r\n var offsetTop = targetPosition.top - (parentPosition.top - parent.scrollTop);\r\n x = offsetLeft + (targetWidth * leftScalar) - parent.clientWidth * leftScalar;\r\n y = offsetTop + (targetHeight * topScalar) - parent.clientHeight * topScalar;\r\n x -= leftOffset;\r\n y -= topOffset;\r\n x = Math.max(Math.min(x, parent.scrollWidth - parent.clientWidth), 0);\r\n y = Math.max(Math.min(y, parent.scrollHeight - parent.clientHeight), 0);\r\n x = scrollSettings.align.lockX ? parent.scrollLeft : x;\r\n y = scrollSettings.align.lockY ? parent.scrollTop : y;\r\n differenceX = x - parent.scrollLeft;\r\n differenceY = y - parent.scrollTop;\r\n }\r\n\r\n return {\r\n x: x,\r\n y: y,\r\n differenceX: differenceX,\r\n differenceY: differenceY\r\n };\r\n}\r\n\r\nfunction animate(parent){\r\n var scrollSettings = parent._scrollSettings;\r\n\r\n if(!scrollSettings){\r\n return;\r\n }\r\n\r\n var maxSynchronousAlignments = scrollSettings.maxSynchronousAlignments;\r\n\r\n var location = getTargetScrollLocation(scrollSettings, parent),\r\n time = Date.now() - scrollSettings.startTime,\r\n timeValue = Math.min(1 / scrollSettings.time * time, 1);\r\n\r\n if(scrollSettings.endIterations >= maxSynchronousAlignments){\r\n setElementScroll(parent, location.x, location.y);\r\n parent._scrollSettings = null;\r\n return scrollSettings.end(COMPLETE);\r\n }\r\n\r\n var easeValue = 1 - scrollSettings.ease(timeValue);\r\n\r\n setElementScroll(parent,\r\n location.x - location.differenceX * easeValue,\r\n location.y - location.differenceY * easeValue\r\n );\r\n\r\n if(time >= scrollSettings.time){\r\n scrollSettings.endIterations++;\r\n // Align ancestor synchronously\r\n scrollSettings.scrollAncestor && animate(scrollSettings.scrollAncestor);\r\n animate(parent);\r\n return;\r\n }\r\n\r\n raf(animate.bind(null, parent));\r\n}\r\n\r\nfunction defaultIsWindow(target){\r\n return target.self === target\r\n}\r\n\r\nfunction transitionScrollTo(target, parent, settings, scrollAncestor, callback){\r\n var idle = !parent._scrollSettings,\r\n lastSettings = parent._scrollSettings,\r\n now = Date.now(),\r\n cancelHandler,\r\n passiveOptions = { passive: true };\r\n\r\n if(lastSettings){\r\n lastSettings.end(CANCELED);\r\n }\r\n\r\n function end(endType){\r\n parent._scrollSettings = null;\r\n\r\n if(parent.parentElement && parent.parentElement._scrollSettings){\r\n parent.parentElement._scrollSettings.end(endType);\r\n }\r\n\r\n if(settings.debug){\r\n console.log('Scrolling ended with type', endType, 'for', parent)\r\n }\r\n\r\n callback(endType);\r\n if(cancelHandler){\r\n parent.removeEventListener('touchstart', cancelHandler, passiveOptions);\r\n parent.removeEventListener('wheel', cancelHandler, passiveOptions);\r\n }\r\n }\r\n\r\n var maxSynchronousAlignments = settings.maxSynchronousAlignments;\r\n\r\n if(maxSynchronousAlignments == null){\r\n maxSynchronousAlignments = 3;\r\n }\r\n\r\n parent._scrollSettings = {\r\n startTime: now,\r\n endIterations: 0,\r\n target: target,\r\n time: settings.time,\r\n ease: settings.ease,\r\n align: settings.align,\r\n isWindow: settings.isWindow || defaultIsWindow,\r\n maxSynchronousAlignments: maxSynchronousAlignments,\r\n end: end,\r\n scrollAncestor\r\n };\r\n\r\n if(!('cancellable' in settings) || settings.cancellable){\r\n cancelHandler = end.bind(null, CANCELED);\r\n parent.addEventListener('touchstart', cancelHandler, passiveOptions);\r\n parent.addEventListener('wheel', cancelHandler, passiveOptions);\r\n }\r\n\r\n if(idle){\r\n animate(parent);\r\n }\r\n\r\n return cancelHandler\r\n}\r\n\r\nfunction defaultIsScrollable(element){\r\n return (\r\n 'pageXOffset' in element ||\r\n (\r\n element.scrollHeight !== element.clientHeight ||\r\n element.scrollWidth !== element.clientWidth\r\n ) &&\r\n getComputedStyle(element).overflow !== 'hidden'\r\n );\r\n}\r\n\r\nfunction defaultValidTarget(){\r\n return true;\r\n}\r\n\r\nfunction findParentElement(el){\r\n if (el.assignedSlot) {\r\n return findParentElement(el.assignedSlot);\r\n }\r\n\r\n if (el.parentElement) {\r\n if(el.parentElement.tagName === 'BODY'){\r\n return el.parentElement.ownerDocument.defaultView || el.parentElement.ownerDocument.ownerWindow;\r\n }\r\n return el.parentElement;\r\n }\r\n\r\n if (el.getRootNode){\r\n var parent = el.getRootNode()\r\n if(parent.nodeType === 11) {\r\n return parent.host;\r\n }\r\n }\r\n}\r\n\r\nmodule.exports = function(target, settings, callback){\r\n if(!target){\r\n return;\r\n }\r\n\r\n if(typeof settings === 'function'){\r\n callback = settings;\r\n settings = null;\r\n }\r\n\r\n if(!settings){\r\n settings = {};\r\n }\r\n\r\n settings.time = isNaN(settings.time) ? 1000 : settings.time;\r\n settings.ease = settings.ease || function(v){return 1 - Math.pow(1 - v, v / 2);};\r\n settings.align = settings.align || {};\r\n\r\n var parent = findParentElement(target),\r\n parents = 1;\r\n\r\n function done(endType){\r\n parents--;\r\n if(!parents){\r\n callback && callback(endType);\r\n }\r\n }\r\n\r\n var validTarget = settings.validTarget || defaultValidTarget;\r\n var isScrollable = settings.isScrollable;\r\n\r\n if(settings.debug){\r\n console.log('About to scroll to', target)\r\n\r\n if(!parent){\r\n console.error('Target did not have a parent, is it mounted in the DOM?')\r\n }\r\n }\r\n\r\n var scrollingElements = [];\r\n\r\n while(parent){\r\n if(settings.debug){\r\n console.log('Scrolling parent node', parent)\r\n }\r\n\r\n if(validTarget(parent, parents) && (isScrollable ? isScrollable(parent, defaultIsScrollable) : defaultIsScrollable(parent))){\r\n parents++;\r\n scrollingElements.push(parent);\r\n }\r\n\r\n parent = findParentElement(parent);\r\n\r\n if(!parent){\r\n done(COMPLETE)\r\n break;\r\n }\r\n }\r\n\r\n return scrollingElements.reduce((cancel, parent, index) => transitionScrollTo(target, parent, settings, scrollingElements[index + 1], done), null);\r\n};\r\n","import scrollIntoView from 'scroll-into-view';\n\n/**\n * Return a promise that resolves on the next animation frame.\n */\nfunction nextAnimationFrame() {\n return new Promise(resolve => {\n requestAnimationFrame(resolve);\n });\n}\n\n/**\n * Linearly interpolate between two values.\n *\n * @param {number} a\n * @param {number} b\n * @param {number} fraction - Value in [0, 1]\n */\nfunction interpolate(a, b, fraction) {\n return a + fraction * (b - a);\n}\n\n/**\n * Return the offset of `element` from the top of a positioned ancestor `parent`.\n *\n * @param {HTMLElement} element\n * @param {HTMLElement} parent - Positioned ancestor of `element`\n * @return {number}\n */\nexport function offsetRelativeTo(element, parent) {\n let offset = 0;\n while (element !== parent && parent.contains(element)) {\n offset += element.offsetTop;\n element = /** @type {HTMLElement} */ (element.offsetParent);\n }\n return offset;\n}\n\n/**\n * Scroll `element` until its `scrollTop` offset reaches a target value.\n *\n * @param {Element} element - Container element to scroll\n * @param {number} offset - Target value for the scroll offset\n * @param {object} options\n * @param {number} [options.maxDuration]\n * @return {Promise<void>} - A promise that resolves once the scroll animation\n * is complete\n */\nexport async function scrollElement(\n element,\n offset,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 } = {}\n) {\n const startOffset = element.scrollTop;\n const endOffset = offset;\n const scrollStart = Date.now();\n\n // Choose a scroll duration proportional to the scroll distance, but capped\n // to avoid it being too slow.\n const pixelsPerMs = 3;\n const scrollDuration = Math.min(\n Math.abs(endOffset - startOffset) / pixelsPerMs,\n maxDuration\n );\n\n let scrollFraction = 0.0;\n while (scrollFraction < 1.0) {\n await nextAnimationFrame();\n scrollFraction = Math.min(1.0, (Date.now() - scrollStart) / scrollDuration);\n element.scrollTop = interpolate(startOffset, endOffset, scrollFraction);\n }\n}\n\n/**\n * Smoothly scroll an element into view.\n *\n * @param {HTMLElement} element\n * @param {object} options\n * @param {number} [options.maxDuration]\n */\nexport async function scrollElementIntoView(\n element,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 } = {}\n) {\n // Make the body's `tagName` return an upper-case string in XHTML documents\n // like it does in HTML documents. This is a workaround for\n // `scrollIntoView`'s detection of the <body> element. See\n // https://github.com/KoryNunn/scroll-into-view/issues/101.\n const body = element.closest('body');\n if (body && body.tagName !== 'BODY') {\n Object.defineProperty(body, 'tagName', {\n value: 'BODY',\n configurable: true,\n });\n }\n\n await new Promise(resolve =>\n scrollIntoView(element, { time: maxDuration }, resolve)\n );\n}\n","import { anchor, describe } from '../anchoring/html';\n\nimport { HTMLMetadata } from './html-metadata';\nimport {\n guessMainContentArea,\n preserveScrollPosition,\n} from './html-side-by-side';\nimport { scrollElementIntoView } from '../util/scroll';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').FeatureFlags} FeatureFlags\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n */\n\n// When activating side-by-side mode, make sure there is at least this amount\n// of space (in pixels) left for the document's content. Any narrower and the\n// content line lengths and scale are too short to be readable.\nconst MIN_HTML_WIDTH = 480;\n\n/**\n * Document type integration for ordinary web pages.\n *\n * This integration is used for web pages and applications that are not handled\n * by a more specific integration (eg. for PDFs).\n *\n * @implements {Integration}\n */\nexport class HTMLIntegration {\n /**\n * @param {object} options\n * @param {FeatureFlags} options.features\n * @param {HTMLElement} [options.container]\n */\n constructor({ features, container = document.body }) {\n this.features = features;\n this.container = container;\n this.anchor = anchor;\n this.describe = describe;\n\n this._htmlMeta = new HTMLMetadata();\n\n /** Whether to attempt to resize the document to fit alongside sidebar. */\n this._sideBySideEnabled = this.features.flagEnabled('html_side_by_side');\n\n /**\n * Whether the document is currently being resized to fit alongside an\n * open sidebar.\n */\n this._sideBySideActive = false;\n\n /** @type {SidebarLayout|null} */\n this._lastLayout = null;\n\n this._flagsChanged = () => {\n const sideBySide = features.flagEnabled('html_side_by_side');\n if (sideBySide !== this._sideBySideEnabled) {\n this._sideBySideEnabled = sideBySide;\n\n // `fitSideBySide` is normally called by Guest when the sidebar layout\n // changes. When the feature flag changes, we need to re-run the method.\n if (this._lastLayout) {\n this.fitSideBySide(this._lastLayout);\n }\n }\n };\n this.features.on('flagsChanged', this._flagsChanged);\n }\n\n canAnnotate() {\n return true;\n }\n\n destroy() {\n this.features.off('flagsChanged', this._flagsChanged);\n }\n\n contentContainer() {\n return this.container;\n }\n\n /**\n * @param {SidebarLayout} layout\n */\n fitSideBySide(layout) {\n this._lastLayout = layout;\n\n const maximumWidthToFit = window.innerWidth - layout.width;\n const active =\n this._sideBySideEnabled &&\n layout.expanded &&\n maximumWidthToFit >= MIN_HTML_WIDTH;\n\n if (active) {\n // nb. We call `_activateSideBySide` regardless of whether side-by-side\n // is already active because the sidebar width might be different.\n this._activateSideBySide(layout.width);\n } else if (this._sideBySideActive) {\n this._deactivateSideBySide();\n }\n this._sideBySideActive = active;\n return active;\n }\n\n /**\n * Resize the document content after side-by-side mode is activated.\n *\n * @param {number} sidebarWidth\n */\n _activateSideBySide(sidebarWidth) {\n // When side-by-side mode is activated, what we want to achieve is that the\n // main content of the page is fully visible alongside the sidebar, with\n // as much space given to the main content as possible. A challenge is that\n // we don't know how the page will respond to reducing the width of the body.\n //\n // - The content might have margins which automatically get reduced as the\n // available width is reduced. For example a blog post with a fixed-width\n // article in the middle and `margin: auto` for both margins.\n //\n // In this scenario we'd want to reduce the document width by the full\n // width of the sidebar.\n //\n // - There might be sidebars to the left and/or right of the main content\n // which cause the main content to be squashed when the width is reduced.\n // For example a news website with a column of ads on the right.\n //\n // In this scenario we'd want to not reduce the document width or reduce\n // it by a smaller amount and let the Hypothesis sidebar cover up the\n // document's sidebar, leaving as much space as possible to the content.\n //\n // Therefore what we do is to initially reduce the width of the document by\n // the full width of the sidebar, then we use heuristics to analyze the\n // resulting page layout and determine whether there is significant \"free space\"\n // (ie. anything that is not the main content of the document, such as ads or\n // links to related stories) to the right of the main content. If there is,\n // we make the document wider again to allow more space for the main content.\n //\n // These heuristics assume a typical \"article\" page with one central block\n // of content. If we can't find the \"main content\" then we just assume that\n // everything on the page is potentially content that the user might want\n // to annotate and so try to keep it all visible.\n\n // nb. 12px padding is a multiple of the 4px grid unit in our design system.\n const padding = 12;\n const rightMargin = sidebarWidth + padding;\n\n /** @param {HTMLElement} element */\n const computeLeftMargin = element =>\n parseInt(window.getComputedStyle(element).marginLeft, 10);\n\n preserveScrollPosition(() => {\n // nb. Adjusting the body size this way relies on the page not setting a\n // width on the body. For sites that do this won't work.\n\n // Remove any margins we've previously set\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n\n // Keep track of what left margin would be naturally without right margin set\n const beforeBodyLeft = computeLeftMargin(document.body);\n\n document.body.style.marginRight = `${rightMargin}px`;\n\n const contentArea = guessMainContentArea(document.body);\n if (contentArea) {\n // Check if we can give the main content more space by letting the\n // sidebar overlap stuff in the document to the right of the main content.\n const freeSpace = Math.max(\n 0,\n window.innerWidth - rightMargin - contentArea.right\n );\n if (freeSpace > 0) {\n const adjustedMargin = Math.max(0, rightMargin - freeSpace);\n document.body.style.marginRight = `${adjustedMargin}px`;\n }\n\n // Changes to right margin can affect left margin in cases where body\n // has `margin:auto`. It's OK to move the body to the left to make\n // space, but avoid moving it to the right.\n // See https://github.com/hypothesis/client/issues/4280\n const afterBodyLeft = computeLeftMargin(document.body);\n if (afterBodyLeft > beforeBodyLeft) {\n document.body.style.marginLeft = `${beforeBodyLeft}px`;\n }\n\n // If the main content appears to be right up against the edge of the\n // window, add padding for readability.\n if (contentArea.left < padding) {\n document.body.style.marginLeft = `${padding}px`;\n }\n } else {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n }\n });\n }\n\n /**\n * Undo the effects of `activateSideBySide`.\n */\n _deactivateSideBySide() {\n preserveScrollPosition(() => {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n });\n }\n\n async getMetadata() {\n return this._htmlMeta.getDocumentMetadata();\n }\n\n async uri() {\n return this._htmlMeta.uri();\n }\n\n /**\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n const highlight = anchor.highlights?.[0];\n if (!highlight) {\n return;\n }\n await scrollElementIntoView(highlight);\n }\n}\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n result = wait - timeSinceLastCall;\n\n return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","/**\n * Find the smallest offset in `str` which contains at least `count` chars\n * that match `filter` before it.\n *\n * @param {string} str\n * @param {number} count\n * @param {(char: string) => boolean} filter\n * @param {number} [startPos]\n */\nfunction advance(str, count, filter, startPos = 0) {\n let pos = startPos;\n while (pos < str.length && count > 0) {\n if (filter(str[pos])) {\n --count;\n }\n ++pos;\n }\n return pos;\n}\n\n/**\n * Count characters which match `filter` in `str`.\n *\n * @param {string} str\n * @param {(char: string) => boolean} filter\n * @param {number} startPos\n * @param {number} endPos\n */\nfunction countChars(str, filter, startPos, endPos) {\n let count = 0;\n for (let pos = startPos; pos < endPos; pos++) {\n if (filter(str[pos])) {\n ++count;\n }\n }\n return count;\n}\n\n/**\n * Translate a (start, end) pair of offsets for an \"input\" string into\n * corresponding offsets in an \"output\" string.\n *\n * Positions in the input and output strings are related by counting\n * the number of \"important\" characters before them, as determined by a\n * filter function.\n *\n * An example usage would be to find equivalent positions in two strings which\n * contain the same text content except for the addition or removal of\n * whitespace at arbitrary locations in the output string.\n *\n * Where there are multiple possible offsets in the output string that\n * correspond to the input offsets, the largest start offset and smallest end\n * offset are chosen. In other words, leading and trailing ignored characters\n * are trimmed from the output.\n *\n * @example\n * // The input offsets (1, 3) select the substring \"bc\" in the \"input\" argument.\n * // The returned offsets select the substring \"b c\" in the \"output\" argument.\n * translateOffsets('abcd', ' a b c d ', 1, 3, char => char !== ' ')\n *\n * @param {string} input\n * @param {string} output\n * @param {number} start - Start offset in `input`\n * @param {number} end - End offset in `input`\n * @param {(ch: string) => boolean} filter - Filter function that returns true\n * if a character should be counted when relating positions between `input`\n * and `output`.\n * @return {[number, number]} - Start and end offsets in `output`\n */\nexport function translateOffsets(input, output, start, end, filter) {\n const beforeStartCount = countChars(input, filter, 0, start);\n const startToEndCount = countChars(input, filter, start, end);\n\n // Find smallest offset in `output` with same number of non-ignored characters\n // before it as before `start` in the input. This offset might correspond to\n // an ignored character.\n let outputStart = advance(output, beforeStartCount, filter);\n\n // Increment this offset until it points to a non-ignored character. This\n // \"trims\" leading ignored characters from the result.\n while (outputStart < output.length && !filter(output[outputStart])) {\n ++outputStart;\n }\n\n // Find smallest offset in `output` with same number of non-ignored characters\n // before it as before `end` in the input.\n const outputEnd = advance(output, startToEndCount, filter, outputStart);\n\n return [outputStart, outputEnd];\n}\n","/* global PDFViewerApplication */\n\nimport { warnOnce } from '../../shared/warn-once';\nimport { translateOffsets } from '../util/normalize';\nimport { matchQuote } from './match-quote';\nimport { createPlaceholder } from './placeholder';\nimport { TextPosition, TextRange } from './text-range';\nimport { TextQuoteAnchor } from './types';\n\n/**\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n * @typedef {import('../../types/api').Selector} Selector\n *\n * @typedef {import('../../types/pdfjs').PDFPageView} PDFPageView\n * @typedef {import('../../types/pdfjs').PDFViewer} PDFViewer\n */\n\n/**\n * @typedef PDFTextRange\n * @prop {number} pageIndex\n * @prop {object} anchor\n * @prop {number} anchor.start - Start character offset within the page's text\n * @prop {number} anchor.end - End character offset within the page's text\n */\n\n/**\n * Enum values for page rendering states (IRenderableView#renderingState)\n * in PDF.js. Taken from web/pdf_rendering_queue.js in the PDF.js library.\n *\n * Reproduced here because this enum is not exported consistently across\n * different versions of PDF.js\n */\nexport const RenderingStates = {\n INITIAL: 0,\n RUNNING: 1,\n PAUSED: 2,\n FINISHED: 3,\n};\n\n// Caches for performance.\n\n/**\n * Map of page index to page text content.\n *\n * @type {Map<number, Promise<string>>}\n */\nconst pageTextCache = new Map();\n\n/**\n * A cache that maps a `{quote}:{offset}` key to a specific\n * location in the document.\n *\n * The components of the key come from an annotation's selectors. This is used\n * to speed up re-anchoring an annotation that was previously anchored in the\n * current session.\n *\n * @type {Map<string, PDFTextRange>}\n */\nconst quotePositionCache = new Map();\n\n/**\n * Return a cache key for lookups in `quotePositionCache`.\n *\n * @param {string} quote\n * @param {number} [pos] - Offset in document text\n */\nfunction quotePositionCacheKey(quote, pos) {\n return `${quote}:${pos}`;\n}\n\n/**\n * Return offset of `node` among its siblings\n *\n * @param {Node} node\n */\nfunction getSiblingIndex(node) {\n let index = 0;\n while (node.previousSibling) {\n ++index;\n node = node.previousSibling;\n }\n return index;\n}\n\n/**\n * Return the text layer element of the PDF page containing `node`.\n *\n * @param {Node|Element} node\n * @return {Element|null}\n */\nfunction getNodeTextLayer(node) {\n const el = 'closest' in node ? node : node.parentElement;\n return el?.closest('.textLayer') ?? null;\n}\n\n/**\n * Get the PDF.js viewer application.\n *\n * @return {PDFViewer}\n */\nfunction getPDFViewer() {\n // @ts-ignore - TS doesn't know about PDFViewerApplication global.\n return PDFViewerApplication.pdfViewer;\n}\n\n/**\n * Returns the view into which a PDF page is drawn.\n *\n * If called while the PDF document is still loading, this will delay until\n * the document loading has progressed far enough for a `PDFPageView` and its\n * associated `PDFPage` to be ready.\n *\n * @param {number} pageIndex\n * @return {Promise<PDFPageView>}\n */\nasync function getPageView(pageIndex) {\n const pdfViewer = getPDFViewer();\n let pageView = pdfViewer.getPageView(pageIndex);\n\n if (!pageView || !pageView.pdfPage) {\n // If the document is still loading, wait for the `pagesloaded` event.\n //\n // Note that loading happens in several stages. Initially the page view\n // objects do not exist (`pageView` will be nullish), then after the\n // \"pagesinit\" event, the page view exists but it does not have a `pdfPage`\n // property set, then finally after the \"pagesloaded\" event, it will have\n // a \"pdfPage\" property.\n pageView = await new Promise(resolve => {\n const onPagesLoaded = () => {\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.off('pagesloaded', onPagesLoaded);\n } else {\n document.removeEventListener('pagesloaded', onPagesLoaded);\n }\n\n resolve(pdfViewer.getPageView(pageIndex));\n };\n\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.on('pagesloaded', onPagesLoaded);\n } else {\n // Old PDF.js versions (< 1.6.210) use DOM events.\n document.addEventListener('pagesloaded', onPagesLoaded);\n }\n });\n }\n\n return /** @type {PDFPageView} */ (pageView);\n}\n\n/**\n * Return true if the document has selectable text.\n */\nexport async function documentHasText() {\n const viewer = getPDFViewer();\n let hasText = false;\n for (let i = 0; i < viewer.pagesCount; i++) {\n const pageText = await getPageTextContent(i);\n if (pageText.trim().length > 0) {\n hasText = true;\n break;\n }\n }\n return hasText;\n}\n\n/**\n * Return the text of a given PDF page.\n *\n * The text returned by this function should match the `textContent` of the text\n * layer element that PDF.js creates for rendered pages, with the exception\n * that differences in whitespace are tolerated.\n *\n * @param {number} pageIndex\n * @return {Promise<string>}\n */\nfunction getPageTextContent(pageIndex) {\n // If we already have or are fetching the text for this page, return the\n // existing result.\n const cachedText = pageTextCache.get(pageIndex);\n if (cachedText) {\n return cachedText;\n }\n\n const getPageText = async () => {\n const pageView = await getPageView(pageIndex);\n const textContent = await pageView.pdfPage.getTextContent({\n // Deprecated option, set for compatibility with older PDF.js releases.\n normalizeWhitespace: true,\n });\n return textContent.items.map(it => it.str).join('');\n };\n\n // This function synchronously populates the cache with a promise so that\n // multiple calls don't call `PDFPageProxy.getTextContent` twice.\n const pageText = getPageText();\n pageTextCache.set(pageIndex, pageText);\n return pageText;\n}\n\n/**\n * Find the offset within the document's text at which a page begins.\n *\n * @param {number} pageIndex\n * @return {Promise<number>} - Offset of page's text within document text\n */\nasync function getPageOffset(pageIndex) {\n const viewer = getPDFViewer();\n if (pageIndex >= viewer.pagesCount) {\n /* istanbul ignore next - This should never be triggered */\n throw new Error('Invalid page index');\n }\n let offset = 0;\n for (let i = 0; i < pageIndex; i++) {\n const text = await getPageTextContent(i);\n offset += text.length;\n }\n return offset;\n}\n\n/**\n * @typedef PageOffset\n * @prop {number} index - Page index\n * @prop {number} offset - Character offset of start of page within document text\n * @prop {string} text - Text of page\n */\n\n/**\n * Find the page containing a text offset within the document.\n *\n * If the offset is invalid (less than 0 or greater than the length of the document)\n * then the nearest (first or last) page is returned.\n *\n * @param {number} offset\n * @return {Promise<PageOffset>}\n */\nasync function findPageByOffset(offset) {\n const viewer = getPDFViewer();\n\n let pageStartOffset = 0;\n let pageEndOffset = 0;\n let text = '';\n\n for (let i = 0; i < viewer.pagesCount; i++) {\n text = await getPageTextContent(i);\n pageStartOffset = pageEndOffset;\n pageEndOffset += text.length;\n\n if (pageEndOffset >= offset) {\n return { index: i, offset: pageStartOffset, text };\n }\n }\n\n // If the offset is beyond the end of the document, just pretend it was on\n // the last page.\n return { index: viewer.pagesCount - 1, offset: pageStartOffset, text };\n}\n\n/**\n * Return true if `char` is an ASCII space.\n *\n * This is more efficient than `/\\s/.test(char)` but does not handle Unicode\n * spaces.\n *\n * @param {string} char\n */\nfunction isSpace(char) {\n switch (char) {\n case ' ':\n case '\\f':\n case '\\n':\n case '\\r':\n case '\\t':\n case '\\v':\n case '\\u00a0': // nbsp\n return true;\n default:\n return false;\n }\n}\n\n/** @param {string} char */\nconst isNotSpace = char => !isSpace(char);\n\n/**\n * Locate the DOM Range which a position selector refers to.\n *\n * If the page is off-screen it may be in an unrendered state, in which case\n * the text layer will not have been created. In that case a placeholder\n * DOM element is created and the returned range refers to that placeholder.\n * In that case, the selector will need to be re-anchored when the page is\n * scrolled into view.\n *\n * @param {number} pageIndex - The PDF page index\n * @param {number} start - Character offset within the page's text\n * @param {number} end - Character offset within the page's text\n * @return {Promise<Range>}\n */\nasync function anchorByPosition(pageIndex, start, end) {\n const [page, pageText] = await Promise.all([\n getPageView(pageIndex),\n getPageTextContent(pageIndex),\n ]);\n\n if (\n page.renderingState === RenderingStates.FINISHED &&\n page.textLayer &&\n page.textLayer.renderingDone\n ) {\n // The page has been rendered. Locate the position in the text layer.\n //\n // We allow for differences in whitespace between the text returned by\n // `getPageTextContent` and the text layer content. Any other differences\n // will cause mis-anchoring.\n\n const root = page.textLayer.textLayerDiv;\n const textLayerStr = /** @type {string} */ (root.textContent);\n\n const [textLayerStart, textLayerEnd] = translateOffsets(\n pageText,\n textLayerStr,\n start,\n end,\n isNotSpace\n );\n\n const textLayerQuote = stripSpaces(\n textLayerStr.slice(textLayerStart, textLayerEnd)\n );\n const pageTextQuote = stripSpaces(pageText.slice(start, end));\n if (textLayerQuote !== pageTextQuote) {\n warnOnce(\n 'Text layer text does not match page text. Highlights will be mis-aligned.'\n );\n }\n\n const startPos = new TextPosition(root, textLayerStart);\n const endPos = new TextPosition(root, textLayerEnd);\n return new TextRange(startPos, endPos).toRange();\n }\n\n // The page has not been rendered yet. Create a placeholder element and\n // anchor to that instead.\n const placeholder = createPlaceholder(page.div);\n const range = document.createRange();\n range.setStartBefore(placeholder);\n range.setEndAfter(placeholder);\n return range;\n}\n\n/**\n * Return a string with spaces stripped.\n *\n * This function optimizes for performance of stripping the main space chars\n * that PDF.js generates over handling all kinds of whitespace that could\n * occur in a string.\n *\n * @param {string} str\n */\nfunction stripSpaces(str) {\n let stripped = '';\n for (let i = 0; i < str.length; i++) {\n const char = str[i];\n if (isSpace(char)) {\n continue;\n }\n stripped += char;\n }\n return stripped;\n}\n\n/**\n * Search for a quote in the given pages.\n *\n * When comparing quote selectors to document text, ASCII whitespace characters\n * are ignored. This is because text extracted from a PDF by different PDF\n * viewers, including different versions of PDF.js, can often differ in the\n * whitespace between characters and words. For a long time PDF.js in particular\n * had issues where it would often produce extra spaces between characters that\n * should not be there or omit spaces between words.\n *\n * @param {TextQuoteSelector} quoteSelector\n * @param {number} [positionHint] - Expected start offset of quote\n * @return {Promise<Range>} Location of quote\n */\nasync function anchorQuote(quoteSelector, positionHint) {\n // Determine which pages to search and in what order. If we have a position\n // hint we'll try to use that. Otherwise we'll just search all pages in order.\n const pageCount = getPDFViewer().pagesCount;\n const pageIndexes = Array(pageCount)\n .fill(0)\n .map((_, i) => i);\n\n let expectedPageIndex;\n let expectedOffsetInPage;\n\n if (positionHint) {\n const { index, offset } = await findPageByOffset(positionHint);\n expectedPageIndex = index;\n expectedOffsetInPage = positionHint - offset;\n\n // Sort pages by distance from the page where we expect to find the quote,\n // based on the position hint.\n pageIndexes.sort((a, b) => {\n const distA = Math.abs(a - index);\n const distB = Math.abs(b - index);\n return distA - distB;\n });\n }\n\n // Search pages for the best match, ignoring whitespace differences.\n const strippedPrefix =\n quoteSelector.prefix !== undefined\n ? stripSpaces(quoteSelector.prefix)\n : undefined;\n const strippedSuffix =\n quoteSelector.suffix !== undefined\n ? stripSpaces(quoteSelector.suffix)\n : undefined;\n const strippedQuote = stripSpaces(quoteSelector.exact);\n\n let bestMatch;\n for (let page of pageIndexes) {\n const text = await getPageTextContent(page);\n const strippedText = stripSpaces(text);\n\n // Determine expected offset of quote in current page based on position hint.\n let strippedHint;\n if (expectedPageIndex !== undefined && expectedOffsetInPage !== undefined) {\n if (page < expectedPageIndex) {\n strippedHint = strippedText.length; // Prefer matches closer to end of page.\n } else if (page === expectedPageIndex) {\n // Translate expected offset in whitespace-inclusive version of page\n // text into offset in whitespace-stripped version of page text.\n [strippedHint] = translateOffsets(\n text,\n strippedText,\n expectedOffsetInPage,\n expectedOffsetInPage,\n isNotSpace\n );\n } else {\n strippedHint = 0; // Prefer matches closer to start of page.\n }\n }\n\n const match = matchQuote(strippedText, strippedQuote, {\n prefix: strippedPrefix,\n suffix: strippedSuffix,\n hint: strippedHint,\n });\n\n if (!match) {\n continue;\n }\n\n if (!bestMatch || match.score > bestMatch.match.score) {\n // Translate match offset from whitespace-stripped version of page text\n // back to original text.\n const [start, end] = translateOffsets(\n strippedText,\n text,\n match.start,\n match.end,\n isNotSpace\n );\n bestMatch = {\n page,\n match: {\n start,\n end,\n score: match.score,\n },\n };\n\n // If we find a very good match, stop early.\n //\n // There is a tradeoff here between optimizing search performance and\n // ensuring that we have found the best match in the document.\n //\n // The current heuristics are that we require an exact match for the quote\n // and either the preceding or following context. The context matching\n // helps to avoid incorrectly stopping the search early if the quote is\n // a word or phrase that is common in the document.\n const exactQuoteMatch =\n strippedText.slice(match.start, match.end) === strippedQuote;\n\n const exactPrefixMatch =\n strippedPrefix !== undefined &&\n strippedText.slice(\n Math.max(0, match.start - strippedPrefix.length),\n match.start\n ) === strippedPrefix;\n\n const exactSuffixMatch =\n strippedSuffix !== undefined &&\n strippedText.slice(match.end, strippedSuffix.length) === strippedSuffix;\n\n const hasContext =\n strippedPrefix !== undefined || strippedSuffix !== undefined;\n\n if (\n exactQuoteMatch &&\n (exactPrefixMatch || exactSuffixMatch || !hasContext)\n ) {\n break;\n }\n }\n }\n\n if (bestMatch) {\n const { page, match } = bestMatch;\n\n // If we found a match, optimize future anchoring of this selector in the\n // same session by caching the match location.\n if (positionHint) {\n const cacheKey = quotePositionCacheKey(quoteSelector.exact, positionHint);\n quotePositionCache.set(cacheKey, {\n pageIndex: page,\n anchor: match,\n });\n }\n\n // Convert the (start, end) position match into a DOM range.\n return anchorByPosition(page, match.start, match.end);\n }\n\n throw new Error('Quote not found');\n}\n\n/**\n * Anchor a set of selectors to a DOM Range.\n *\n * `selectors` must include a `TextQuoteSelector` and may include other selector\n * types.\n *\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n * @return {Promise<Range>}\n */\nexport async function anchor(root, selectors) {\n const quote = /** @type {TextQuoteSelector|undefined} */ (\n selectors.find(s => s.type === 'TextQuoteSelector')\n );\n\n // The quote selector is required in order to check that text position\n // selector results are still valid.\n if (!quote) {\n throw new Error('No quote selector found');\n }\n\n const position = /** @type {TextPositionSelector|undefined} */ (\n selectors.find(s => s.type === 'TextPositionSelector')\n );\n\n if (position) {\n // If we have a position selector, try using that first as it is the fastest\n // anchoring method.\n try {\n const { index, offset, text } = await findPageByOffset(position.start);\n const start = position.start - offset;\n const end = position.end - offset;\n\n const matchedText = text.substring(start, end);\n if (quote.exact !== matchedText) {\n throw new Error('quote mismatch');\n }\n\n const range = await anchorByPosition(index, start, end);\n return range;\n } catch {\n // Fall back to quote selector\n }\n\n // If anchoring with the position failed, check for a cached quote-based\n // match using the quote + position as a cache key.\n try {\n const cacheKey = quotePositionCacheKey(quote.exact, position.start);\n const cachedPos = quotePositionCache.get(cacheKey);\n if (cachedPos) {\n const { pageIndex, anchor } = cachedPos;\n const range = await anchorByPosition(\n pageIndex,\n anchor.start,\n anchor.end\n );\n return range;\n }\n } catch {\n // Fall back to uncached quote selector match\n }\n }\n\n return anchorQuote(quote, position?.start);\n}\n\n/**\n * Prepare a DOM range for generating selectors and find the containing text layer.\n *\n * @param {Range} range\n * @return {[Range, Element]}\n * @throws If the range cannot be annotated\n */\nfunction getTextLayerForRange(range) {\n // \"Shrink\" the range so that the start and endpoints are at offsets within\n // text nodes rather than any containing nodes.\n try {\n range = TextRange.fromRange(range).toRange();\n } catch {\n throw new Error('Selection does not contain text');\n }\n\n const startTextLayer = getNodeTextLayer(range.startContainer);\n const endTextLayer = getNodeTextLayer(range.endContainer);\n\n if (!startTextLayer || !endTextLayer) {\n throw new Error('Selection is outside page text');\n }\n\n if (startTextLayer !== endTextLayer) {\n throw new Error('Selecting across page breaks is not supported');\n }\n\n return [range, startTextLayer];\n}\n\n/**\n * Return true if selectors can be generated for a range using `describe`.\n *\n * This function is faster than calling `describe` if the selectors are not\n * required.\n *\n * @param {Range} range\n */\nexport function canDescribe(range) {\n try {\n getTextLayerForRange(range);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert a DOM Range object into a set of selectors.\n *\n * Converts a DOM `Range` object into a `[position, quote]` tuple of selectors\n * which can be saved with an annotation and later passed to `anchor` to\n * convert the selectors back to a `Range`.\n *\n * @param {HTMLElement} root - The root element\n * @param {Range} range\n * @return {Promise<Selector[]>}\n */\nexport async function describe(root, range) {\n const [textRange, textLayer] = getTextLayerForRange(range);\n\n const startPos = TextPosition.fromPoint(\n textRange.startContainer,\n textRange.startOffset\n ).relativeTo(textLayer);\n\n const endPos = TextPosition.fromPoint(\n textRange.endContainer,\n textRange.endOffset\n ).relativeTo(textLayer);\n\n const startPageIndex = getSiblingIndex(\n /** @type {Node} */ (textLayer.parentNode)\n );\n const pageOffset = await getPageOffset(startPageIndex);\n\n /** @type {TextPositionSelector} */\n const position = {\n type: 'TextPositionSelector',\n start: pageOffset + startPos.offset,\n end: pageOffset + endPos.offset,\n };\n\n const quote = TextQuoteAnchor.fromRange(root, textRange).toSelector();\n\n return [position, quote];\n}\n\n/**\n * Clear this module's internal caches.\n *\n * This exists mainly as a helper for use in tests.\n */\nexport function purgeCache() {\n pageTextCache.clear();\n quotePositionCache.clear();\n}\n","/**\n * Render banners at the top of a document in a stacked column.\n *\n * @param {object} props\n * @param {import(\"preact\").ComponentChildren} props.children\n */\nexport default function Banners({ children }) {\n return <div className=\"flex flex-col\">{children}</div>;\n}\n","import { Icon, LabeledButton, Link } from '@hypothesis/frontend-shared';\n\n/**\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n */\n\n/**\n * A banner that informs the user about the provider of the document.\n *\n * @param {object} props\n * @param {ContentPartner} props.provider\n * @param {() => void} props.onClose\n */\nexport default function ContentPartnerBanner({ provider, onClose }) {\n return (\n <div className=\"flex items-center border-b gap-x-4 px-2 py-1 bg-white text-annotator-lg\">\n {provider === 'jstor' && (\n <>\n <Link href=\"https://jstor.org\" target=\"_blank\">\n <Icon\n classes=\"w-[97px] h-[25px]\"\n name=\"jstor\"\n title=\"Document hosted by JSTOR\"\n />\n </Link>\n <div className=\"grow\">\n Document hosted by <b>JSTOR</b>\n </div>\n </>\n )}\n <div className=\"text-annotator-base\">\n <LabeledButton onClick={onClose} data-testid=\"close-button\">\n Close\n </LabeledButton>\n </div>\n </div>\n );\n}\n","import { Icon, Link } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * A banner shown at the top of the PDF viewer if the PDF cannot be annotated\n * by Hypothesis.\n */\nexport default function WarningBanner() {\n return (\n <div className=\"bg-white\">\n <div\n className={classnames(\n 'flex items-center gap-x-2',\n 'border border-yellow-notice bg-yellow-notice/10 text-annotator-base'\n )}\n >\n <div className=\"bg-yellow-notice text-white p-2\">\n <Icon name=\"caution\" classes=\"text-annotator-xl\" />\n </div>\n <div>\n <strong>This PDF does not contain selectable text:</strong>{' '}\n <Link\n target=\"_blank\"\n href=\"https://web.hypothes.is/help/how-to-ocr-optimize-pdfs/\"\n >\n Learn how to fix this\n </Link>{' '}\n in order to annotate with Hypothesis.\n </div>\n </div>\n </div>\n );\n}\n","import { normalizeURI } from '../util/url';\n\n/**\n * @typedef {import('../../types/pdfjs').PDFViewerApplication} PDFViewerApplication\n */\n\n/**\n * @typedef Link\n * @prop {string} href\n */\n\n/**\n * @typedef Metadata\n * @prop {string} title - The document title\n * @prop {Link[]} link - Array of URIs associated with this document\n * @prop {string} documentFingerprint - The fingerprint of this PDF. This is\n * referred to as the \"File Identifier\" in the PDF spec. It may be a hash of\n * part of the content if the PDF file does not have a File Identifier.\n *\n * PDFs may have two file identifiers. The first is the \"original\" identifier\n * which is not supposed to change if the file is updated and the second\n * one is the \"last modified\" identifier. This property is the original\n * identifier.\n */\n\n/**\n * Wait for a PDFViewerApplication to be initialized.\n *\n * @param {PDFViewerApplication} app\n * @return {Promise<void>}\n */\nfunction pdfViewerInitialized(app) {\n // `initializedPromise` was added in PDF.js v2.4.456.\n // See https://github.com/mozilla/pdf.js/pull/11607. In earlier versions the\n // `initialized` property can be queried.\n if (app.initializedPromise) {\n return app.initializedPromise;\n } else if (app.initialized) {\n return Promise.resolve();\n } else {\n // PDF.js < v2.4.456. The recommended approach is to listen for a `localized`\n // DOM event, but this assumes that PDF.js has been configured to publish\n // events to the DOM. Here we simply poll `app.initialized` because it is\n // easier.\n return new Promise(resolve => {\n const timeout = setInterval(() => {\n if (app.initialized) {\n clearTimeout(timeout);\n resolve();\n }\n }, 5);\n });\n }\n}\n\n/**\n * PDFMetadata extracts metadata about a loading/loaded PDF document from a\n * PDF.js PDFViewerApplication object.\n *\n * @example\n * // Invoke in a PDF.js viewer, before or after the PDF has finished loading.\n * const meta = new PDFMetadata(window.PDFViewerApplication)\n * meta.getUri().then(uri => {\n * // Do something with the URL of the PDF.\n * })\n */\nexport class PDFMetadata {\n /**\n * Construct a `PDFMetadata` that returns URIs/metadata associated with a\n * given PDF viewer.\n *\n * @param {PDFViewerApplication} app - The `PDFViewerApplication` global from PDF.js\n */\n constructor(app) {\n /** @type {Promise<PDFViewerApplication>} */\n this._loaded = pdfViewerInitialized(app).then(() => {\n // Check if document has already loaded.\n if (app.downloadComplete) {\n return app;\n }\n\n return new Promise(resolve => {\n const finish = () => {\n if (app.eventBus) {\n app.eventBus.off('documentload', finish);\n app.eventBus.off('documentloaded', finish);\n } else {\n window.removeEventListener('documentload', finish);\n }\n resolve(app);\n };\n\n // Listen for \"documentloaded\" event which signals that the document\n // has been downloaded and the first page has been rendered.\n if (app.eventBus) {\n // PDF.js >= v1.6.210 dispatch events via an internal event bus.\n // PDF.js < v2.5.207 also dispatches events to the DOM.\n\n // `documentloaded` is the preferred event in PDF.js >= v2.0.943.\n // See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2\n app.eventBus.on('documentloaded', finish);\n\n // `documentload` is dispatched by PDF.js < v2.1.266.\n app.eventBus.on('documentload', finish);\n } else {\n // PDF.js < v1.6.210 dispatches events only to the DOM.\n window.addEventListener('documentload', finish);\n }\n });\n });\n }\n\n /**\n * Return the URI of the PDF.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n *\n * @return {Promise<string>}\n */\n getUri() {\n return this._loaded.then(app => {\n let uri = getPDFURL(app);\n if (!uri) {\n uri = fingerprintToURN(getFingerprint(app));\n }\n return uri;\n });\n }\n\n /**\n * Returns metadata about the document.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n *\n * @return {Promise<Metadata>}\n */\n async getMetadata() {\n const app = await this._loaded;\n const {\n info: documentInfo,\n contentDispositionFilename,\n metadata,\n } = await app.pdfDocument.getMetadata();\n\n const documentFingerprint = getFingerprint(app);\n const url = getPDFURL(app);\n\n // Return the title metadata embedded in the PDF if available, otherwise\n // fall back to values from the `Content-Disposition` header or URL.\n //\n // PDFs contain two embedded metadata sources, the metadata stream and\n // the document info dictionary. Per the specification, the metadata stream\n // is preferred if available.\n //\n // This logic is similar to how PDF.js sets `document.title`.\n let title;\n if (metadata?.has('dc:title') && metadata.get('dc:title') !== 'Untitled') {\n title = /** @type {string} */ (metadata.get('dc:title'));\n } else if (documentInfo?.Title) {\n title = documentInfo.Title;\n } else if (contentDispositionFilename) {\n title = contentDispositionFilename;\n } else if (url) {\n title = filenameFromURL(url);\n } else {\n title = '';\n }\n\n const link = [{ href: fingerprintToURN(documentFingerprint) }];\n if (url) {\n link.push({ href: url });\n }\n\n return {\n title,\n link,\n documentFingerprint,\n };\n }\n}\n\n/**\n * Get the fingerprint/file identifier of the currently loaded PDF.\n *\n * @param {PDFViewerApplication} app\n */\nfunction getFingerprint(app) {\n if (Array.isArray(app.pdfDocument.fingerprints)) {\n return app.pdfDocument.fingerprints[0];\n } else {\n return /** @type {string} */ (app.pdfDocument.fingerprint);\n }\n}\n\n/**\n * Generate a URI from a PDF fingerprint suitable for storing as the main\n * or associated URI of an annotation.\n *\n * @param {string} fingerprint\n */\nfunction fingerprintToURN(fingerprint) {\n return `urn:x-pdf:${fingerprint}`;\n}\n\n/**\n * @param {PDFViewerApplication} app\n * @return {string|null} - Valid URL string or `null`\n */\nfunction getPDFURL(app) {\n if (!app.url) {\n return null;\n }\n\n const url = normalizeURI(app.url);\n\n // Local file:// URLs should not be saved in document metadata.\n // Entries in document.link should be URIs. In the case of\n // local files, omit the URL.\n if (url.indexOf('file://') !== 0) {\n return url;\n }\n\n return null;\n}\n\n/**\n * Return the last component of the path part of a URL.\n *\n * @param {string} url - A valid URL string\n * @return {string}\n */\nfunction filenameFromURL(url) {\n const parsed = new URL(url);\n const pathSegments = parsed.pathname.split('/');\n return pathSegments[pathSegments.length - 1];\n}\n","import debounce from 'lodash.debounce';\nimport { render } from 'preact';\n\nimport { ListenerCollection } from '../../shared/listener-collection';\nimport {\n RenderingStates,\n anchor,\n canDescribe,\n describe,\n documentHasText,\n} from '../anchoring/pdf';\nimport { isInPlaceholder, removePlaceholder } from '../anchoring/placeholder';\nimport Banners from '../components/Banners';\nimport ContentPartnerBanner from '../components/ContentPartnerBanner';\nimport WarningBanner from '../components/WarningBanner';\nimport { createShadowRoot } from '../util/shadow-root';\nimport { offsetRelativeTo, scrollElement } from '../util/scroll';\n\nimport { PDFMetadata } from './pdf-metadata';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').AnnotationData} AnnotationData\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n * @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../../types/api').Selector} Selector\n * @typedef {import('../../types/pdfjs').PDFViewerApplication} PDFViewerApplication\n */\n\n// The viewport and controls for PDF.js start breaking down below about 670px\n// of available space, so only render PDF and sidebar side-by-side if there\n// is enough room. Otherwise, allow sidebar to overlap PDF\nconst MIN_PDF_WIDTH = 680;\n\n/**\n * Return true if `anchor` is in an un-rendered page.\n *\n * @param {Anchor} anchor\n */\nfunction anchorIsInPlaceholder(anchor) {\n const highlight = anchor.highlights?.[0];\n return highlight && isInPlaceholder(highlight);\n}\n\n/** @param {number} ms */\nfunction delay(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Is the current document the PDF.js viewer application?\n */\nexport function isPDF() {\n // @ts-ignore - TS doesn't know about PDFViewerApplication global.\n return typeof PDFViewerApplication !== 'undefined';\n}\n\n/**\n * Integration that works with PDF.js\n * @implements {Integration}\n */\nexport class PDFIntegration {\n /**\n * @param {Annotator} annotator\n * @param {object} options\n * @param {ContentPartner} [options.contentPartner] - If set, show branding\n * for the given content partner in a banner above the PDF viewer.\n * @param {number} [options.reanchoringMaxWait] - Max time to wait for\n * re-anchoring to complete when scrolling to an un-rendered page.\n */\n constructor(annotator, options = {}) {\n this.annotator = annotator;\n\n const window_ = /** @type {HypothesisWindow} */ (window);\n\n // Assume this class is only used if we're in the PDF.js viewer.\n const pdfViewerApp = /** @type {PDFViewerApplication} */ (\n window_.PDFViewerApplication\n );\n\n this.pdfViewer = pdfViewerApp.pdfViewer;\n this.pdfViewer.viewer.classList.add('has-transparent-text-layer');\n\n // Get the element that contains all of the PDF.js UI. This is typically\n // `document.body`.\n this.pdfContainer = pdfViewerApp.appConfig?.appContainer ?? document.body;\n\n this.pdfMetadata = new PDFMetadata(pdfViewerApp);\n\n this.observer = new MutationObserver(debounce(() => this._update(), 100));\n this.observer.observe(this.pdfViewer.viewer, {\n attributes: true,\n attributeFilter: ['data-loaded'],\n childList: true,\n subtree: true,\n });\n\n /**\n * Amount of time to wait for re-anchoring to complete when scrolling to\n * an anchor in a not-yet-rendered page.\n */\n this._reanchoringMaxWait = options.reanchoringMaxWait ?? 3000;\n\n /**\n * Banners shown at the top of the PDF viewer.\n *\n * @type {HTMLElement|null}\n */\n this._banner = null;\n\n /** State indicating which banners to show above the PDF viewer. */\n this._bannerState = {\n /**\n * Branding for a content provider.\n *\n * @type {ContentPartner|null}\n */\n contentPartner: options.contentPartner ?? null,\n /** Warning that the current PDF does not have selectable text. */\n noTextWarning: false,\n };\n this._updateBannerState(this._bannerState);\n this._checkForSelectableText();\n\n // Hide annotation layer when the user is making a selection. The annotation\n // layer appears above the invisible text layer and can interfere with text\n // selection. See https://github.com/hypothesis/client/issues/1464.\n this._updateAnnotationLayerVisibility = () => {\n const selection = /** @type {Selection} */ (window_.getSelection());\n\n // Add CSS class to indicate whether there is a selection. Annotation\n // layers are then hidden by a CSS rule in `pdfjs-overrides.scss`.\n this.pdfViewer.viewer.classList.toggle(\n 'is-selecting',\n !selection.isCollapsed\n );\n };\n\n this._listeners = new ListenerCollection();\n this._listeners.add(\n document,\n 'selectionchange',\n this._updateAnnotationLayerVisibility\n );\n\n // A flag that indicates whether `destroy` has been called. Used to handle\n // `destroy` being called during async code elsewhere in the class.\n this._destroyed = false;\n }\n\n destroy() {\n this._listeners.removeAll();\n this.pdfViewer.viewer.classList.remove('has-transparent-text-layer');\n this.observer.disconnect();\n this._banner?.remove();\n this._destroyed = true;\n }\n\n /**\n * Return the URL of the currently loaded PDF document.\n */\n uri() {\n return this.pdfMetadata.getUri();\n }\n\n /**\n * Return the metadata (eg. title) for the currently loaded PDF document.\n */\n getMetadata() {\n return this.pdfMetadata.getMetadata();\n }\n\n /**\n * Resolve serialized `selectors` from an annotation to a range.\n *\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n * @return {Promise<Range>}\n */\n anchor(root, selectors) {\n // nb. The `root` argument is not really used by `anchor`. It existed for\n // consistency between HTML and PDF anchoring and could be removed.\n return anchor(root, selectors);\n }\n\n /**\n * Return true if the text in a range lies within the text layer of a PDF.\n *\n * @param {Range} range\n */\n canAnnotate(range) {\n return canDescribe(range);\n }\n\n /**\n * Generate selectors for the text in `range`.\n *\n * @param {HTMLElement} root\n * @param {Range} range\n * @return {Promise<Selector[]>}\n */\n describe(root, range) {\n // nb. The `root` argument is not really used by `anchor`. It existed for\n // consistency between HTML and PDF anchoring and could be removed.\n return describe(root, range);\n }\n\n /**\n * Check whether the PDF has selectable text and show a warning if not.\n */\n async _checkForSelectableText() {\n // Wait for PDF to load.\n try {\n await this.uri();\n } catch (e) {\n return;\n }\n\n // Handle `PDF` instance being destroyed while URI is fetched. This is only\n // expected to happen in synchronous tests.\n if (this._destroyed) {\n return;\n }\n\n try {\n const hasText = await documentHasText();\n this._updateBannerState({ noTextWarning: !hasText });\n } catch (err) {\n /* istanbul ignore next */\n console.warn('Unable to check for text in PDF:', err);\n }\n }\n\n /**\n * Update banners shown above the PDF viewer.\n *\n * @param {Partial<typeof PDFIntegration.prototype._bannerState>} state\n */\n _updateBannerState(state) {\n this._bannerState = { ...this._bannerState, ...state };\n\n // Get a reference to the top-level DOM element associated with the PDF.js\n // viewer.\n const outerContainer = /** @type {HTMLElement} */ (\n document.querySelector('#outerContainer')\n );\n\n const showBanner =\n this._bannerState.contentPartner || this._bannerState.noTextWarning;\n\n if (!showBanner) {\n this._banner?.remove();\n this._banner = null;\n\n // Undo inline styles applied when the banner is shown. The banner will\n // then gets its normal 100% height set by PDF.js's CSS.\n outerContainer.style.height = '';\n\n return;\n }\n\n if (!this._banner) {\n this._banner = document.createElement('hypothesis-banner');\n document.body.prepend(this._banner);\n createShadowRoot(this._banner);\n }\n\n render(\n <Banners>\n {this._bannerState.contentPartner && (\n <ContentPartnerBanner\n provider={this._bannerState.contentPartner}\n onClose={() => this._updateBannerState({ contentPartner: null })}\n />\n )}\n {this._bannerState.noTextWarning && <WarningBanner />}\n </Banners>,\n /** @type {ShadowRoot} */ (this._banner.shadowRoot)\n );\n\n const bannerHeight = this._banner.getBoundingClientRect().height;\n\n // The `#outerContainer` element normally has height set to 100% of the body.\n //\n // Reduce this by the height of the banner so that it doesn't extend beyond\n // the bottom of the viewport.\n //\n // We don't currently handle the height of the banner changing here.\n outerContainer.style.height = `calc(100% - ${bannerHeight}px)`;\n }\n\n // This method (re-)anchors annotations when pages are rendered and destroyed.\n _update() {\n // A list of annotations that need to be refreshed.\n const refreshAnnotations = /** @type {AnnotationData[]} */ ([]);\n\n const pageCount = this.pdfViewer.pagesCount;\n for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {\n const page = this.pdfViewer.getPageView(pageIndex);\n if (!page?.textLayer?.renderingDone) {\n continue;\n }\n\n // Detect what needs to be done by checking the rendering state.\n switch (page.renderingState) {\n case RenderingStates.INITIAL:\n // This page has been reset to its initial state so its text layer\n // is no longer valid. Null it out so that we don't process it again.\n page.textLayer = null;\n break;\n case RenderingStates.FINISHED:\n // This page is still rendered. If it has a placeholder node that\n // means the PDF anchoring module anchored annotations before it was\n // rendered. Remove this, which will cause the annotations to anchor\n // again, below.\n removePlaceholder(page.div);\n break;\n }\n }\n\n // Find all the anchors that have been invalidated by page state changes.\n for (let anchor of this.annotator.anchors) {\n // Skip any we already know about.\n if (anchor.highlights) {\n if (refreshAnnotations.includes(anchor.annotation)) {\n continue;\n }\n\n // If the highlights are no longer in the document it means that either\n // the page was destroyed by PDF.js or the placeholder was removed above.\n // The annotations for these anchors need to be refreshed.\n for (let index = 0; index < anchor.highlights.length; index++) {\n const hl = anchor.highlights[index];\n if (!document.body.contains(hl)) {\n anchor.highlights.splice(index, 1);\n delete anchor.range;\n refreshAnnotations.push(anchor.annotation);\n break;\n }\n }\n }\n }\n\n refreshAnnotations.map(annotation => this.annotator.anchor(annotation));\n }\n\n /**\n * Return the scrollable element which contains the document content.\n *\n * @return {HTMLElement}\n */\n contentContainer() {\n return /** @type {HTMLElement} */ (\n document.querySelector('#viewerContainer')\n );\n }\n\n /**\n * Attempt to make the PDF viewer and the sidebar fit side-by-side without\n * overlap if there is enough room in the viewport to do so reasonably.\n * Resize the PDF viewer container element to leave the right amount of room\n * for the sidebar, and prompt PDF.js to re-render the PDF pages to scale\n * within that resized container.\n *\n * @param {SidebarLayout} sidebarLayout\n * @return {boolean} - True if side-by-side mode was activated\n */\n fitSideBySide(sidebarLayout) {\n const maximumWidthToFit = window.innerWidth - sidebarLayout.width;\n const active = sidebarLayout.expanded && maximumWidthToFit >= MIN_PDF_WIDTH;\n\n // If the sidebar is closed, we reserve enough space for the toolbar controls\n // so that they don't overlap a) the chevron-menu on the right side of\n // PDF.js's top toolbar and b) the document's scrollbar.\n //\n // If the sidebar is open, we reserve space for the whole sidebar if there is\n // room, otherwise we reserve the same space as in the closed state to\n // prevent the PDF content shifting when opening and closing the sidebar.\n const reservedSpace = active\n ? sidebarLayout.width\n : sidebarLayout.toolbarWidth;\n this.pdfContainer.style.width = `calc(100% - ${reservedSpace}px)`;\n\n // The following logic is pulled from PDF.js `webViewerResize`\n const currentScaleValue = this.pdfViewer.currentScaleValue;\n if (\n currentScaleValue === 'auto' ||\n currentScaleValue === 'page-fit' ||\n currentScaleValue === 'page-width'\n ) {\n // NB: There is logic within the setter for `currentScaleValue`\n // Setting this scale value will prompt PDF.js to recalculate viewport\n this.pdfViewer.currentScaleValue = currentScaleValue;\n }\n // This will cause PDF pages to re-render if their scaling has changed\n this.pdfViewer.update();\n\n return active;\n }\n\n /**\n * Scroll to the location of an anchor in the PDF.\n *\n * If the anchor refers to a location that is an un-rendered page far from\n * the viewport, then scrolling happens in three phases. First the document\n * scrolls to the approximate location indicated by the placeholder anchor,\n * then `scrollToAnchor` waits until the page's text layer is rendered and\n * the annotation is re-anchored in the fully rendered page. Then it scrolls\n * again to the final location.\n *\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n const annotation = anchor.annotation;\n const inPlaceholder = anchorIsInPlaceholder(anchor);\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n\n // nb. We only compute the scroll offset once at the start of scrolling.\n // This is important as the highlight may be removed from the document during\n // the scroll due to a page transitioning from rendered <-> un-rendered.\n await scrollElement(this.contentContainer(), offset);\n\n if (inPlaceholder) {\n const anchor = await this._waitForAnnotationToBeAnchored(\n annotation,\n this._reanchoringMaxWait\n );\n if (!anchor) {\n return;\n }\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n await scrollElement(this.contentContainer(), offset);\n }\n }\n\n /**\n * Wait for an annotation to be anchored in a rendered page.\n *\n * @param {AnnotationData} annotation\n * @param {number} maxWait\n * @return {Promise<Anchor|null>}\n */\n async _waitForAnnotationToBeAnchored(annotation, maxWait) {\n const start = Date.now();\n let anchor;\n do {\n // nb. Re-anchoring might result in a different anchor object for the\n // same annotation.\n anchor = this.annotator.anchors.find(a => a.annotation === annotation);\n if (!anchor || anchorIsInPlaceholder(anchor)) {\n anchor = null;\n\n // If no anchor was found, wait a bit longer and check again to see if\n // re-anchoring completed.\n await delay(20);\n }\n } while (!anchor && Date.now() - start < maxWait);\n return anchor ?? null;\n }\n\n /**\n * Return the offset that the PDF content container would need to be scrolled\n * to, in order to make an anchor visible.\n *\n * @param {Anchor} anchor\n * @return {number|null} - Target offset or `null` if this anchor was not resolved\n */\n _anchorOffset(anchor) {\n if (!anchor.highlights) {\n // This anchor was not resolved to a location in the document.\n return null;\n }\n const highlight = anchor.highlights[0];\n return offsetRelativeTo(highlight, this.contentContainer());\n }\n}\n","import debounce from 'lodash.debounce';\n\nexport const DEBOUNCE_WAIT = 40;\n\n/** @typedef {(frame: HTMLIFrameElement) => void} FrameCallback */\n\n/**\n * FrameObserver detects iframes added and deleted from the document.\n *\n * To enable annotation, an iframe must be opted-in by adding the\n * `enable-annotation` attribute.\n *\n * We require the `enable-annotation` attribute to avoid the overhead of loading\n * the client into frames which are not useful to annotate. See\n * https://github.com/hypothesis/client/issues/530\n */\nexport class FrameObserver {\n /**\n * @param {Element} element - root of the DOM subtree to watch for the addition\n * and removal of annotatable iframes\n * @param {FrameCallback} onFrameAdded - callback fired when an annotatable iframe is added\n * @param {FrameCallback} onFrameRemoved - callback triggered when the annotatable iframe is removed\n */\n constructor(element, onFrameAdded, onFrameRemoved) {\n this._element = element;\n this._onFrameAdded = onFrameAdded;\n this._onFrameRemoved = onFrameRemoved;\n /** @type {Set<HTMLIFrameElement>} */\n this._annotatableFrames = new Set();\n this._isDisconnected = false;\n\n this._mutationObserver = new MutationObserver(\n debounce(() => {\n this._discoverFrames();\n }, DEBOUNCE_WAIT)\n );\n this._discoverFrames();\n this._mutationObserver.observe(this._element, {\n childList: true,\n subtree: true,\n attributeFilter: ['enable-annotation'],\n });\n }\n\n disconnect() {\n this._isDisconnected = true;\n this._mutationObserver.disconnect();\n }\n\n /**\n * @param {HTMLIFrameElement} frame\n */\n async _addFrame(frame) {\n this._annotatableFrames.add(frame);\n try {\n await onNextDocumentReady(frame);\n if (this._isDisconnected) {\n return;\n }\n const frameWindow = frame.contentWindow;\n // @ts-expect-error\n // This line raises an exception if the iframe is from a different origin\n frameWindow.addEventListener('unload', () => {\n this._removeFrame(frame);\n });\n this._onFrameAdded(frame);\n } catch (e) {\n console.warn(\n `Unable to inject the Hypothesis client (from '${document.location.href}' into a cross-origin frame '${frame.src}')`\n );\n }\n }\n\n /**\n * @param {HTMLIFrameElement} frame\n */\n _removeFrame(frame) {\n this._annotatableFrames.delete(frame);\n this._onFrameRemoved(frame);\n }\n\n _discoverFrames() {\n const frames = new Set(\n /** @type {NodeListOf<HTMLIFrameElement> } */ (\n this._element.querySelectorAll('iframe[enable-annotation]')\n )\n );\n\n for (let frame of frames) {\n if (!this._annotatableFrames.has(frame)) {\n this._addFrame(frame);\n }\n }\n\n for (let frame of this._annotatableFrames) {\n if (!frames.has(frame)) {\n this._removeFrame(frame);\n }\n }\n }\n}\n\n/**\n * Test if this is the empty document that a new iframe has before the URL\n * specified by its `src` attribute loads.\n *\n * @param {HTMLIFrameElement} frame\n */\nfunction hasBlankDocumentThatWillNavigate(frame) {\n return (\n frame.contentDocument?.location.href === 'about:blank' &&\n // Do we expect the frame to navigate away from about:blank?\n frame.hasAttribute('src') &&\n frame.src !== 'about:blank'\n );\n}\n\n/**\n * Wrapper around {@link onDocumentReady} which returns a promise that resolves\n * the first time that a document in `frame` becomes ready.\n *\n * See {@link onDocumentReady} for the definition of _ready_.\n *\n * @param {HTMLIFrameElement} frame\n * @return {Promise<Document>}\n */\nexport function onNextDocumentReady(frame) {\n return new Promise((resolve, reject) => {\n const unsubscribe = onDocumentReady(frame, (err, doc) => {\n unsubscribe();\n if (doc) {\n resolve(doc);\n } else {\n reject(err);\n }\n });\n });\n}\n\n/**\n * Register a callback that is invoked when the content document\n * (`frame.contentDocument`) in a same-origin iframe becomes _ready_.\n *\n * A document is _ready_ when its `readyState` is either \"interactive\" or\n * \"complete\". It must also not be the empty document with URL \"about:blank\"\n * that iframes have before they navigate to the URL specified by their \"src\"\n * attribute.\n *\n * The callback is fired both for the document that is in the frame when\n * `onDocumentReady` is called, as well as for new documents that are\n * subsequently loaded into the same frame.\n *\n * If at any time the frame navigates to an iframe that is cross-origin,\n * the callback will fire with an error. It will fire again for subsequent\n * navigations, but due to platform limitations, it will only fire after the\n * next document fully loads (ie. when the frame's `load` event fires).\n *\n * @param {HTMLIFrameElement} frame\n * @param {(...args: [Error]|[null, Document]) => void} callback\n * @param {object} options\n * @param {number} [options.pollInterval]\n * @return {() => void} Callback that unsubscribes from future changes\n */\nexport function onDocumentReady(frame, callback, { pollInterval = 10 } = {}) {\n /** @type {number|undefined} */\n let pollTimer;\n /** @type {() => void} */\n let pollForDocumentChange;\n\n // Visited documents for which we have fired the callback or are waiting\n // to become ready.\n const documents = new WeakSet();\n\n const cancelPoll = () => {\n clearTimeout(pollTimer);\n pollTimer = undefined;\n };\n\n // Begin polling for a document change when the current document is about\n // to go away.\n const pollOnUnload = () => {\n if (frame.contentDocument) {\n frame.contentWindow?.addEventListener('unload', pollForDocumentChange);\n }\n };\n\n const checkForDocumentChange = () => {\n const currentDocument = frame.contentDocument;\n if (!currentDocument) {\n callback(new Error('Frame is cross-origin'));\n return;\n }\n\n if (documents.has(currentDocument)) {\n return;\n }\n documents.add(currentDocument);\n cancelPoll();\n\n if (!hasBlankDocumentThatWillNavigate(frame)) {\n const isReady =\n currentDocument.readyState === 'interactive' ||\n currentDocument.readyState === 'complete';\n\n if (isReady) {\n callback(null, currentDocument);\n } else {\n currentDocument.addEventListener('DOMContentLoaded', () =>\n callback(null, currentDocument)\n );\n }\n }\n\n // Poll for the next document change.\n pollOnUnload();\n };\n\n let canceled = false;\n pollForDocumentChange = () => {\n cancelPoll();\n if (!canceled) {\n pollTimer = setInterval(checkForDocumentChange, pollInterval);\n }\n };\n\n // Set up observers for signals that the document either has changed or will\n // soon change. There are two signals with different trade-offs:\n //\n // - Polling after the current document is about to be unloaded. This allows\n // us to detect the new document quickly, but may not fire in some\n // situations (exact circumstances unclear, but eg. MDN warns about this).\n //\n // This is set up in the first call to `checkForDocumentChange`.\n //\n // - The iframe's \"load\" event. This is guaranteed to fire but only after the\n // new document is fully loaded.\n frame.addEventListener('load', checkForDocumentChange);\n\n // Notify caller about the current document. This fires asynchronously so that\n // the caller will have received the unsubscribe callback first.\n const initialCheckTimer = setTimeout(checkForDocumentChange, 0);\n\n return () => {\n canceled = true;\n clearTimeout(initialCheckTimer);\n cancelPoll();\n frame.removeEventListener('load', checkForDocumentChange);\n };\n}\n","import debounce from 'lodash.debounce';\n\nimport { ListenerCollection } from '../../shared/listener-collection';\nimport {\n rectCenter,\n rectsOverlapHorizontally,\n rectsOverlapVertically,\n unionRects,\n} from '../util/geometry';\n\n/**\n * @typedef WordBox\n * @prop {string} text\n * @prop {DOMRect} rect - Bounding rectangle of all glyphs in word\n */\n\n/**\n * @typedef LineBox\n * @prop {WordBox[]} words\n * @prop {DOMRect} rect - Bounding rectangle of all words in line\n */\n\n/**\n * @typedef ColumnBox\n * @prop {LineBox[]} lines\n * @prop {DOMRect} rect - Bounding rectangle of all lines in column\n */\n\n/**\n * Group characters in a page into words, lines and columns.\n *\n * The input is assumed to be _roughly_ reading order, more so at the low (word,\n * line) level. When the input is not in reading order, the output may be\n * divided into more lines / columns than expected. Downstream code is expected\n * to tolerate over-segmentation. This function should try to avoid producing\n * lines or columns that significantly intersect, as this can impair text\n * selection.\n *\n * @param {DOMRect[]} charBoxes - Bounding rectangle associated with each character on the page\n * @param {string} text - Text that corresponds to `charBoxes`\n * @return {ColumnBox[]}\n */\nfunction analyzeLayout(charBoxes, text) {\n /** @type {WordBox[]} */\n const words = [];\n\n /** @type {WordBox} */\n let currentWord = { text: '', rect: new DOMRect() };\n\n // Group characters into words.\n const addWord = () => {\n if (currentWord.text.length > 0) {\n words.push(currentWord);\n currentWord = { text: '', rect: new DOMRect() };\n }\n };\n for (let [i, rect] of charBoxes.entries()) {\n const char = text[i];\n const isSpace = /\\s/.test(char);\n\n currentWord.rect = unionRects(currentWord.rect, rect);\n\n // To simplify downstream logic, normalize whitespace.\n currentWord.text += isSpace ? ' ' : char;\n\n if (isSpace) {\n addWord();\n }\n }\n addWord();\n\n /** @type {LineBox[]} */\n const lines = [];\n\n /** @type {LineBox} */\n let currentLine = { words: [], rect: new DOMRect() };\n\n // Group words into lines.\n const addLine = () => {\n if (currentLine.words.length > 0) {\n lines.push(currentLine);\n currentLine = { words: [], rect: new DOMRect() };\n }\n };\n for (let word of words) {\n const prevWord = currentLine.words[currentLine.words.length - 1];\n if (prevWord) {\n const prevCenter = rectCenter(prevWord.rect);\n const currentCenter = rectCenter(word.rect);\n const xDist = currentCenter.x - prevCenter.x;\n if (\n !rectsOverlapVertically(prevWord.rect, word.rect) ||\n // Break line if current word is left of previous word\n xDist < 0\n ) {\n addLine();\n }\n }\n currentLine.words.push(word);\n currentLine.rect = unionRects(currentLine.rect, word.rect);\n }\n addLine();\n\n /** @type {ColumnBox[]} */\n const columns = [];\n\n /** @type {ColumnBox} */\n let currentColumn = { lines: [], rect: new DOMRect() };\n\n // Group lines into columns.\n const addColumn = () => {\n if (currentColumn.lines.length > 0) {\n columns.push(currentColumn);\n currentColumn = { lines: [], rect: new DOMRect() };\n }\n };\n for (let line of lines) {\n const prevLine = currentColumn.lines[currentColumn.lines.length - 1];\n\n if (prevLine) {\n const prevCenter = rectCenter(prevLine.rect);\n const currentCenter = rectCenter(line.rect);\n const yDist = currentCenter.y - prevCenter.y;\n\n if (\n !rectsOverlapHorizontally(prevLine.rect, line.rect) ||\n rectsOverlapVertically(prevLine.rect, line.rect) ||\n // Break column if current line is above previous line.\n //\n // In the case of a two column layout for example, this happens when\n // moving from the bottom of one column to the top of the next.\n yDist < 0 ||\n // Break column if there is a large gap between the previous and current lines.\n //\n // This helps to avoid generating intersecting columns if there happens\n // to be other content between the lines that comes later in the input.\n yDist > line.rect.height * 4\n ) {\n addColumn();\n }\n }\n currentColumn.lines.push(line);\n currentColumn.rect = unionRects(currentColumn.rect, line.rect);\n }\n addColumn();\n\n return columns;\n}\n\n/**\n * ImageTextLayer maintains a transparent text layer on top of an image\n * which contains text. This enables the text in the image to be selected\n * and highlighted.\n *\n * This is similar to the one that PDF.js creates for us in our standard PDF\n * viewer.\n */\nexport class ImageTextLayer {\n /**\n * Create a text layer which is displayed on top of `image`.\n *\n * @param {Element} image - Rendered image on which to overlay the text layer.\n * The text layer will be inserted into the DOM as the next sibling of `image`.\n * @param {DOMRect[]} charBoxes - Bounding boxes for characters in the image.\n * Coordinates should be in the range [0-1], where 0 is the top/left corner\n * of the image and 1 is the bottom/right.\n * @param {string} text - Characters in the image corresponding to `charBoxes`\n */\n constructor(image, charBoxes, text) {\n if (charBoxes.length !== text.length) {\n throw new Error('Char boxes length does not match text length');\n }\n\n // Create container for text layer and position it above the image.\n const containerParent = /** @type {HTMLElement} */ (image.parentNode);\n const container = document.createElement('hypothesis-text-layer');\n containerParent.insertBefore(container, image.nextSibling);\n\n // Position text layer over image. We assume the image's top-left corner\n // aligns with the top-left corner of its container.\n containerParent.style.position = 'relative';\n container.style.position = 'absolute';\n container.style.top = '0';\n container.style.left = '0';\n container.style.color = 'transparent';\n\n // Prevent inherited text alignment from affecting positioning.\n // VitalSource sets `text-align: center` for example.\n container.style.textAlign = 'left';\n\n // Use multiply blending to make text in the image more readable when\n // the corresponding text in the text layer is selected or highlighted.\n // We apply a similar effect in PDF.js.\n container.style.mixBlendMode = 'multiply';\n\n // Set a fixed font style on the container and create a canvas using the same\n // font which we can use to measure the \"natural\" size of the text. This is\n // later used when scaling the text to fit the underlying part of the image.\n const fontSize = 16;\n const fontFamily = 'sans-serif';\n container.style.fontSize = fontSize + 'px';\n container.style.fontFamily = fontFamily;\n const canvas = document.createElement('canvas');\n const context = /** @type {CanvasRenderingContext2D} */ (\n canvas.getContext('2d')\n );\n context.font = `${fontSize}px ${fontFamily}`;\n\n /**\n * Generate a CSS value that scales with the `--x-scale` or `--y-scale` CSS variables.\n *\n * @param {'x'|'y'} dimension\n * @param {number} value\n * @param {string} unit\n */\n const scaledValue = (dimension, value, unit = 'px') =>\n `calc(var(--${dimension}-scale) * ${value}${unit})`;\n\n // Group characters into words, lines and columns. Then use the result to\n // create a hierarchical DOM structure in the text layer:\n //\n // 1. Columns are positioned absolutely\n // 2. Columns stack lines vertically using a block layout\n // 3. Lines arrange words horizontally using an inline layout\n //\n // This allows the browser to select the expected text when the cursor is\n // in-between lines or words.\n const columns = analyzeLayout(charBoxes, text);\n\n for (let column of columns) {\n const columnEl = document.createElement('hypothesis-text-column');\n columnEl.style.display = 'block';\n columnEl.style.position = 'absolute';\n columnEl.style.left = scaledValue('x', column.rect.left);\n columnEl.style.top = scaledValue('y', column.rect.top);\n\n let prevLine = null;\n for (let line of column.lines) {\n const lineEl = document.createElement('hypothesis-text-line');\n lineEl.style.display = 'block';\n lineEl.style.marginLeft = scaledValue(\n 'x',\n line.rect.left - column.rect.left\n );\n lineEl.style.height = scaledValue('y', line.rect.height);\n\n if (prevLine) {\n lineEl.style.marginTop = scaledValue(\n 'y',\n line.rect.top - prevLine.rect.bottom\n );\n }\n prevLine = line;\n\n // Prevent line breaks if the word elements don't quite fit the line.\n lineEl.style.whiteSpace = 'nowrap';\n\n let prevWord = null;\n for (let word of line.words) {\n const wordEl = document.createElement('hypothesis-text-word');\n wordEl.style.display = 'inline-block';\n wordEl.style.transformOrigin = 'top left';\n wordEl.textContent = word.text;\n\n if (prevWord) {\n wordEl.style.marginLeft = scaledValue(\n 'x',\n word.rect.left - prevWord.rect.right\n );\n }\n prevWord = word;\n\n // Set the size of this box used for layout. This does not affect the\n // rendered size of the content.\n wordEl.style.width = scaledValue('x', word.rect.width);\n wordEl.style.height = scaledValue('y', word.rect.height);\n\n // Don't collapse whitespace at end of words, so it remains visible\n // in selected text. Also prevent line breaks due to overflows.\n wordEl.style.whiteSpace = 'pre';\n\n // Scale content using a transform. This affects the rendered size\n // of the text, used by text selection and\n // `Element.getBoundingClientRect`, but not layout.\n const metrics = context.measureText(word.text);\n const xScale = scaledValue('x', word.rect.width / metrics.width, '');\n const yScale = scaledValue('y', word.rect.height / fontSize, '');\n wordEl.style.transform = `scale(${xScale}, ${yScale})`;\n\n lineEl.append(wordEl);\n }\n\n columnEl.append(lineEl);\n }\n\n container.append(columnEl);\n }\n\n const updateTextLayerSize = () => {\n const { width: imageWidth, height: imageHeight } =\n image.getBoundingClientRect();\n container.style.width = imageWidth + 'px';\n container.style.height = imageHeight + 'px';\n\n container.style.setProperty('--x-scale', `${imageWidth}`);\n container.style.setProperty('--y-scale', `${imageHeight}`);\n };\n\n updateTextLayerSize();\n\n /**\n * Container element for the text layer.\n *\n * This is exposed so that callers can tweak the style if needed (eg.\n * to set a z-index value).\n */\n this.container = container;\n\n this._updateTextLayerSize = debounce(updateTextLayerSize, { maxWait: 50 });\n this._listeners = new ListenerCollection();\n\n if (typeof ResizeObserver !== 'undefined') {\n this._imageSizeObserver = new ResizeObserver(() => {\n this._updateTextLayerSize();\n });\n this._imageSizeObserver.observe(image);\n }\n\n // Fallback for browsers that don't support ResizeObserver (Safari < 13.4).\n // Due to the debouncing, we can register this listener in all browsers for\n // simplicity, without downsides.\n this._listeners.add(window, 'resize', this._updateTextLayerSize);\n }\n\n /**\n * Synchronously update the text layer to match the size and position of\n * the image.\n *\n * Normally the text layer is resized automatically but asynchronously when\n * the image size changes (eg. due to the window being resized) and updates\n * are debounced. This method can be used to force an immediate update if\n * needed.\n */\n updateSync() {\n this._updateTextLayerSize();\n this._updateTextLayerSize.flush();\n }\n\n destroy() {\n this.container.remove();\n this._listeners.removeAll();\n this._updateTextLayerSize.cancel();\n this._imageSizeObserver?.disconnect();\n }\n}\n","import { parseJsonConfig } from '../boot/parse-json-config';\nimport { generateHexString } from '../shared/random';\nimport { onNextDocumentReady, FrameObserver } from './frame-observer';\n\n/**\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Options for injecting the client into child frames.\n *\n * This includes the URL of the client's boot script, plus configuration\n * for the client when it loads in the child frame.\n *\n * @typedef {{ clientUrl: string } & Record<string, unknown>} InjectConfig\n */\n\n/**\n * HypothesisInjector injects the Hypothesis client into same-origin iframes.\n *\n * The client will be injected automatically into frames that have the\n * `enable-annotation` attribute set (see {@link FrameObserver}) and can be\n * manually injected into other frames using {@link injectClient}.\n *\n * @implements {Destroyable}\n */\nexport class HypothesisInjector {\n /**\n * @param {Element} element - root of the DOM subtree to watch for the\n * addition and removal of annotatable iframes\n * @param {InjectConfig} config\n */\n constructor(element, config) {\n this._config = config;\n this._frameObserver = new FrameObserver(\n element,\n frame => injectClient(frame, config), // Frame added callback\n () => {} // Frame removed callback\n );\n }\n\n /**\n * Disables the injection of the Hypothesis client into child iframes.\n */\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\n/**\n * Check if the client was added to a frame by {@link injectHypothesis}.\n *\n * @param {HTMLIFrameElement} iframe\n */\nfunction hasHypothesis(iframe) {\n const iframeDocument = /** @type {Document} */ (iframe.contentDocument);\n return iframeDocument.querySelector('script.js-hypothesis-config') !== null;\n}\n\n/**\n * Inject Hypothesis client into a frame.\n *\n * IMPORTANT: This method requires that the iframe is same-origin\n * (frame.contentDocument|contentWindow is not null).\n *\n * This waits for the frame to finish loading before injecting the client.\n * See {@link onDocumentReady}.\n *\n * @param {HTMLIFrameElement} frame\n * @param {InjectConfig} config -\n */\nexport async function injectClient(frame, config) {\n if (hasHypothesis(frame)) {\n return;\n }\n\n await onNextDocumentReady(frame);\n\n // Propagate the client resource locations from the current frame.\n //\n // These settings are set only in the browser extension and not by the\n // embedded client (served by h).\n //\n // We could potentially do this by allowing these settings to be part of\n // the \"annotator\" config (see `annotator/config/index.js`) which gets passed\n // to the constructor.\n const { assetRoot, notebookAppUrl, sidebarAppUrl } =\n parseJsonConfig(document);\n\n const injectedConfig = {\n ...config,\n\n assetRoot,\n notebookAppUrl,\n sidebarAppUrl,\n\n // Generate a random string to use as a frame ID. The format is not important.\n subFrameIdentifier: generateHexString(10),\n };\n\n const configElement = document.createElement('script');\n configElement.className = 'js-hypothesis-config';\n configElement.type = 'application/json';\n configElement.innerText = JSON.stringify(injectedConfig);\n\n const bootScript = document.createElement('script');\n bootScript.async = true;\n bootScript.src = config.clientUrl;\n\n const iframeDocument = /** @type {Document} */ (frame.contentDocument);\n iframeDocument.body.appendChild(configElement);\n iframeDocument.body.appendChild(bootScript);\n}\n","import { ListenerCollection } from '../../shared/listener-collection';\nimport { FeatureFlags } from '../features';\nimport { onDocumentReady } from '../frame-observer';\nimport { HTMLIntegration } from './html';\nimport { preserveScrollPosition } from './html-side-by-side';\nimport { ImageTextLayer } from './image-text-layer';\nimport { injectClient } from '../hypothesis-injector';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').Selector} Selector\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../hypothesis-injector').InjectConfig} InjectConfig\n */\n\n// When activating side-by-side mode for VitalSource PDF documents, make sure\n// at least this much space (in pixels) is left for the PDF document. Any\n// smaller and it feels unreadable or too-zoomed-out\nconst MIN_CONTENT_WIDTH = 480;\n\n/**\n * Return the custom DOM element that contains the book content iframe.\n */\nfunction findBookElement(document_ = document) {\n return document_.querySelector('mosaic-book');\n}\n\n/**\n * Return the role of the current frame in the VitalSource Bookshelf reader or\n * `null` if the frame is not part of Bookshelf.\n *\n * @return {'container'|'content'|null} - `container` if this is the parent of\n * the content frame, `content` if this is the frame that contains the book\n * content or `null` if the document is not part of the Bookshelf reader.\n */\nexport function vitalSourceFrameRole(window_ = window) {\n if (findBookElement(window_.document)) {\n return 'container';\n }\n\n const parentDoc = window_.frameElement?.ownerDocument;\n if (parentDoc && findBookElement(parentDoc)) {\n return 'content';\n }\n\n return null;\n}\n\n/**\n * VitalSourceInjector runs in the book container frame and loads the client into\n * book content frames.\n *\n * The frame structure of the VitalSource book reader looks like this:\n *\n * [VitalSource top frame - bookshelf.vitalsource.com]\n * |\n * [Book container frame - jigsaw.vitalsource.com]\n * |\n * [Book content frame - jigsaw.vitalsource.com]\n *\n * The Hypothesis client can be initially loaded in the container frame or the\n * content frame. As the user navigates around the book, the container frame\n * remains the same but the content frame is swapped out. When used in the\n * container frame, this class handles initial injection of the client as a\n * guest in the current content frame, and re-injecting the client into new\n * content frames when they are created.\n */\nexport class VitalSourceInjector {\n /**\n * @param {InjectConfig} config - Configuration for injecting the client into\n * book content frames\n */\n constructor(config) {\n const bookElement = findBookElement();\n if (!bookElement) {\n throw new Error('Book container element not found');\n }\n\n /** @type {WeakSet<HTMLIFrameElement>} */\n const contentFrames = new WeakSet();\n\n const shadowRoot = /** @type {ShadowRoot} */ (bookElement.shadowRoot);\n const injectClientIntoContentFrame = () => {\n const frame = shadowRoot.querySelector('iframe');\n if (!frame || contentFrames.has(frame)) {\n // Either there is no content frame or we are already watching it.\n return;\n }\n contentFrames.add(frame);\n onDocumentReady(frame, (err, document_) => {\n const body = document_?.body;\n const isBookContent =\n body &&\n // Check that this is not the temporary page containing encrypted and\n // invisible book content, which is replaced with the real content after\n // a form submission. These pages look something like:\n //\n // ```\n // <html>\n // <title>content</title>\n // <body><div id=\"page-content\">{ Base64 encoded data }</div></body>\n // </html>\n // ```\n !body.querySelector('#page-content');\n\n if (isBookContent) {\n injectClient(frame, config);\n }\n });\n };\n\n injectClientIntoContentFrame();\n\n // Re-inject client into content frame after a chapter navigation.\n this._frameObserver = new MutationObserver(injectClientIntoContentFrame);\n this._frameObserver.observe(shadowRoot, { childList: true, subtree: true });\n }\n\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\n/**\n * Bounding box of a single character in the page.\n *\n * Coordinates are expressed in percentage distance from the top-left corner\n * of the rendered page.\n *\n * @typedef GlyphBox\n * @prop {number} l\n * @prop {number} r\n * @prop {number} t\n * @prop {number} b\n */\n\n/**\n * @typedef PDFGlyphData\n * @prop {GlyphBox[]} glyphs\n */\n\n/**\n * Data that the VitalSource book reader renders into the page about the\n * content and location of text in the image.\n *\n * @typedef PDFTextData\n * @prop {PDFGlyphData} glyphs - Locations of each text character in the page\n * @prop {string} words - The text in the page\n */\n\nfunction getPDFPageImage() {\n return /** @type {HTMLImageElement|null} */ (\n document.querySelector('img#pbk-page')\n );\n}\n\n/**\n * Fix how a VitalSource book content frame scrolls, so that various related\n * Hypothesis behaviors (the bucket bar, scrolling annotations into view) work\n * as intended.\n *\n * Some VitalSource books (PDFs) make content scrolling work by making the\n * content iframe really tall and having the parent frame scroll. This stops the\n * Hypothesis bucket bar and scrolling annotations into view from working.\n *\n * @param {HTMLIFrameElement} frame\n */\nfunction makeContentFrameScrollable(frame) {\n if (frame.getAttribute('scrolling') !== 'no') {\n // This is a book (eg. EPUB) where the workaround is not required.\n return;\n }\n\n // Override inline styles of iframe (hence `!important`). The iframe lives\n // in Shadow DOM, so the element styles won't affect the rest of the app.\n const style = document.createElement('style');\n style.textContent = `iframe { height: 100% !important; }`;\n frame.insertAdjacentElement('beforebegin', style);\n\n const removeScrollingAttr = () => frame.removeAttribute('scrolling');\n removeScrollingAttr();\n\n // Sometimes the attribute gets re-added by VS. Remove it if that\n // happens.\n const attrObserver = new MutationObserver(removeScrollingAttr);\n attrObserver.observe(frame, { attributes: true });\n}\n\n/**\n * Integration for the content frame in VitalSource's Bookshelf ebook reader.\n *\n * This integration delegates to the standard HTML integration for most\n * functionality, but it adds logic to:\n *\n * - Customize the document URI and metadata that is associated with annotations\n * - Prevent VitalSource's built-in selection menu from getting in the way\n * of the adder.\n * - Create a hidden text layer in PDF-based books, so the user can select text\n * in the PDF image. This is similar to what PDF.js does for us in PDFs.\n *\n * @implements {Integration}\n */\nexport class VitalSourceContentIntegration {\n /**\n * @param {HTMLElement} container\n */\n constructor(container = document.body) {\n const features = new FeatureFlags();\n\n // Forcibly enable the side-by-side feature for VS books. This feature is\n // only behind a flag for regular web pages, which are typically more\n // complex and varied than EPUB books.\n features.update({ html_side_by_side: true });\n\n this._htmlIntegration = new HTMLIntegration({ container, features });\n\n this._listeners = new ListenerCollection();\n\n // Prevent mouse events from reaching the window. This prevents VitalSource\n // from showing its native selection menu, which obscures the client's\n // annotation toolbar.\n //\n // To avoid interfering with the client's own selection handling, this\n // event blocking must happen at the same level or higher in the DOM tree\n // than where SelectionObserver listens.\n const stopEvents = ['mouseup', 'mousedown', 'mouseout'];\n for (let event of stopEvents) {\n this._listeners.add(document.documentElement, event, e => {\n e.stopPropagation();\n });\n }\n\n // Install scrolling workaround for PDFs. We do this in the content frame\n // so that it works whether Hypothesis is loaded directly into the content\n // frame or injected by VitalSourceInjector from the parent frame.\n const frame = /** @type {HTMLIFrameElement|null} */ (window.frameElement);\n if (frame) {\n makeContentFrameScrollable(frame);\n }\n\n // If this is a PDF, create the hidden text layer above the rendered PDF\n // image.\n const bookImage = getPDFPageImage();\n\n /** @type {PDFTextData|undefined} */\n const pageData = /** @type {any} */ (window).innerPageData;\n\n if (bookImage && pageData) {\n const charRects = pageData.glyphs.glyphs.map(glyph => {\n const left = glyph.l / 100;\n const right = glyph.r / 100;\n const top = glyph.t / 100;\n const bottom = glyph.b / 100;\n return new DOMRect(left, top, right - left, bottom - top);\n });\n\n this._textLayer = new ImageTextLayer(\n bookImage,\n charRects,\n pageData.words\n );\n\n // VitalSource has several DOM elements in the page which are raised\n // above the image using z-index. One of these is used to handle VS's\n // own text selection functionality.\n //\n // Set a z-index on our text layer to raise it above VS's own one.\n this._textLayer.container.style.zIndex = '100';\n }\n }\n\n canAnnotate() {\n return true;\n }\n\n destroy() {\n this._textLayer?.destroy();\n this._listeners.removeAll();\n this._htmlIntegration.destroy();\n }\n\n /**\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n */\n anchor(root, selectors) {\n return this._htmlIntegration.anchor(root, selectors);\n }\n\n /**\n * @param {HTMLElement} root\n * @param {Range} range\n */\n describe(root, range) {\n return this._htmlIntegration.describe(root, range);\n }\n\n contentContainer() {\n return this._htmlIntegration.contentContainer();\n }\n\n /**\n * @param {SidebarLayout} layout\n */\n fitSideBySide(layout) {\n // For PDF books, handle side-by-side mode in this integration. For EPUBs,\n // delegate to the HTML integration.\n const bookImage = getPDFPageImage();\n if (bookImage && this._textLayer) {\n const bookContainer = /** @type {HTMLElement} */ (\n bookImage.parentElement\n );\n const textLayer = this._textLayer;\n\n // Update the PDF image size and alignment to fit alongside the sidebar.\n // `ImageTextLayer` will handle adjusting the text layer to match.\n const newWidth = window.innerWidth - layout.width;\n\n preserveScrollPosition(() => {\n if (layout.expanded && newWidth > MIN_CONTENT_WIDTH) {\n // The VS book viewer sets `text-align: center` on the <body> element\n // by default, which centers the book image in the page. When the sidebar\n // is open we need the image to be left-aligned.\n bookContainer.style.textAlign = 'left';\n bookImage.style.width = `${newWidth}px`;\n } else {\n bookContainer.style.textAlign = '';\n bookImage.style.width = '';\n }\n\n // Update text layer to match new image dimensions immediately. This\n // is needed so that `preserveScrollPosition` can see how the content\n // has shifted when this callback returns.\n textLayer.updateSync();\n });\n\n return layout.expanded;\n } else {\n return this._htmlIntegration.fitSideBySide(layout);\n }\n }\n\n async getMetadata() {\n // Return minimal metadata which includes only the information we really\n // want to include.\n return {\n title: document.title,\n link: [],\n };\n }\n\n async uri() {\n // An example of a typical URL for the chapter content in the Bookshelf reader is:\n //\n // https://jigsaw.vitalsource.com/books/9781848317703/epub/OPS/xhtml/chapter_001.html#cfi=/6/10%5B;vnd.vst.idref=chap001%5D!/4\n //\n // Where \"9781848317703\" is the VitalSource book ID (\"vbid\"), \"chapter_001.html\"\n // is the location of the HTML page for the current chapter within the book\n // and the `#cfi` fragment identifies the scroll location.\n //\n // Note that this URL is typically different than what is displayed in the\n // iframe's `src` attribute.\n\n // Strip off search parameters and fragments.\n const uri = new URL(document.location.href);\n uri.search = '';\n return uri.toString();\n }\n\n /**\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n return this._htmlIntegration.scrollToAnchor(anchor);\n }\n}\n","import { HTMLIntegration } from './html';\nimport { PDFIntegration, isPDF } from './pdf';\nimport {\n VitalSourceContentIntegration,\n vitalSourceFrameRole,\n} from './vitalsource';\n\n/**\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n * @typedef {import('../../types/annotator').Integration} Integration\n */\n\n/**\n * Create the integration that handles document-type specific aspects of\n * guest functionality.\n *\n * @param {Annotator} annotator\n * @param {object} options\n * @param {ContentPartner} [options.contentPartner] - Content partner banner to show,\n * if supported by the integration.\n * @return {Integration}\n */\nexport function createIntegration(annotator, { contentPartner } = {}) {\n if (isPDF()) {\n return new PDFIntegration(annotator, { contentPartner });\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'content') {\n return new VitalSourceContentIntegration();\n }\n\n return new HTMLIntegration({ features: annotator.features });\n}\n","import { ListenerCollection } from '../shared/listener-collection';\n\n/**\n * Return the current selection or `null` if there is no selection or it is empty.\n *\n * @param {Document} document\n * @return {Range|null}\n */\nexport function selectedRange(document) {\n const selection = document.getSelection();\n if (!selection || selection.rangeCount === 0) {\n return null;\n }\n const range = selection.getRangeAt(0);\n if (range.collapsed) {\n return null;\n }\n return range;\n}\n\n/**\n * An observer that watches for and buffers changes to the document's current selection.\n */\nexport class SelectionObserver {\n /**\n * Start observing changes to the current selection in the document.\n *\n * @param {(range: Range|null) => void} callback -\n * Callback invoked with the selected region of the document when it has\n * changed.\n * @param {Document} document_ - Test seam\n */\n constructor(callback, document_ = document) {\n let isMouseDown = false;\n\n this._pendingCallback = null;\n\n const scheduleCallback = (delay = 10) => {\n this._pendingCallback = setTimeout(() => {\n callback(selectedRange(document_));\n }, delay);\n };\n\n /** @param {Event} event */\n const eventHandler = event => {\n if (event.type === 'mousedown') {\n isMouseDown = true;\n }\n if (event.type === 'mouseup') {\n isMouseDown = false;\n }\n\n // If the user makes a selection with the mouse, wait until they release\n // it before reporting a selection change.\n if (isMouseDown) {\n return;\n }\n\n this._cancelPendingCallback();\n\n // Schedule a notification after a short delay. The delay serves two\n // purposes:\n //\n // - If this handler was called as a result of a 'mouseup' event then the\n // selection will not be updated until the next tick of the event loop.\n // In this case we only need a short delay.\n //\n // - If the user is changing the selection with a non-mouse input (eg.\n // keyboard or selection handles on mobile) this buffers updates and\n // makes sure that we only report one when the update has stopped\n // changing. In this case we want a longer delay.\n\n const delay = event.type === 'mouseup' ? 10 : 100;\n scheduleCallback(delay);\n };\n\n this._document = document_;\n this._listeners = new ListenerCollection();\n\n this._listeners.add(document_, 'selectionchange', eventHandler);\n\n // Mouse events are handled on the body because propagation may be stopped\n // before they reach the document in some environments (eg. VitalSource).\n this._listeners.add(document_.body, 'mousedown', eventHandler);\n this._listeners.add(document_.body, 'mouseup', eventHandler);\n\n // Report the initial selection.\n scheduleCallback(1);\n }\n\n disconnect() {\n this._listeners.removeAll();\n this._cancelPendingCallback();\n }\n\n _cancelPendingCallback() {\n if (this._pendingCallback) {\n clearTimeout(this._pendingCallback);\n this._pendingCallback = null;\n }\n }\n}\n","import { ListenerCollection } from '../shared/listener-collection';\nimport { PortFinder, PortRPC } from '../shared/messaging';\nimport { generateHexString } from '../shared/random';\n\nimport { Adder } from './adder';\nimport { TextRange } from './anchoring/text-range';\nimport { BucketBarClient } from './bucket-bar-client';\nimport { FeatureFlags } from './features';\nimport {\n getHighlightsContainingNode,\n highlightRange,\n removeAllHighlights,\n removeHighlights,\n setHighlightsFocused,\n setHighlightsVisible,\n} from './highlighter';\nimport { createIntegration } from './integrations';\nimport * as rangeUtil from './range-util';\nimport { SelectionObserver, selectedRange } from './selection-observer';\nimport { findClosestOffscreenAnchor } from './util/buckets';\nimport { frameFillsAncestor } from './util/frame';\nimport { normalizeURI } from './util/url';\n\n/**\n * @typedef {import('../types/annotator').AnnotationData} AnnotationData\n * @typedef {import('../types/annotator').Annotator} Annotator\n * @typedef {import('../types/annotator').Anchor} Anchor\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../types/api').Target} Target\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n * @typedef {import('../types/port-rpc-events').GuestToSidebarEvent} GuestToSidebarEvent\n * @typedef {import('../types/port-rpc-events').SidebarToGuestEvent} SidebarToGuestEvent\n */\n\n/**\n * HTML element created by the highlighter with an associated annotation.\n *\n * @typedef {HTMLElement & { _annotation?: AnnotationData }} AnnotationHighlight\n */\n\n/**\n * Return all the annotations tags associated with the selected text.\n *\n * @return {string[]}\n */\nfunction annotationsForSelection() {\n const selection = /** @type {Selection} */ (window.getSelection());\n const range = selection.getRangeAt(0);\n const tags = rangeUtil.itemsForRange(\n range,\n node => /** @type {AnnotationHighlight} */ (node)._annotation?.$tag\n );\n return tags;\n}\n\n/**\n * Return the annotation tags associated with any highlights that contain a given\n * DOM node.\n *\n * @param {Node} node\n * @return {string[]}\n */\nfunction annotationsAt(node) {\n const items = getHighlightsContainingNode(node)\n .map(h => /** @type {AnnotationHighlight} */ (h)._annotation)\n .filter(ann => ann !== undefined)\n .map(ann => ann?.$tag);\n return /** @type {string[]} */ (items);\n}\n\n/**\n * Resolve an anchor's associated document region to a concrete `Range`.\n *\n * This may fail if anchoring failed or if the document has been mutated since\n * the anchor was created in a way that invalidates the anchor.\n *\n * @param {Anchor} anchor\n * @return {Range|null}\n */\nfunction resolveAnchor(anchor) {\n if (!anchor.range) {\n return null;\n }\n try {\n return anchor.range.toRange();\n } catch {\n return null;\n }\n}\n\nfunction removeTextSelection() {\n document.getSelection()?.removeAllRanges();\n}\n\n/**\n * Subset of the Hypothesis client configuration that is used by {@link Guest}.\n *\n * @typedef GuestConfig\n * @prop {string} [subFrameIdentifier] - An identifier used by this guest to\n * identify the current frame when communicating with the sidebar. This is\n * only set in non-host frames.\n * @prop {'jstor'} [contentPartner] - Configures a banner or other indicators\n * showing where the content has come from.\n */\n\n/**\n * `Guest` is the central class of the annotator that handles anchoring (locating)\n * annotations in the document when they are fetched by the sidebar, rendering\n * highlights for them and handling subsequent interactions with the highlights.\n *\n * It is also responsible for listening to changes in the current selection\n * and triggering the display of controls to create new annotations. When one\n * of these controls is clicked, it creates the new annotation and sends it to\n * the sidebar.\n *\n * Within a browser tab, there is typically one `Guest` instance per frame that\n * loads Hypothesis (not all frames will be annotation-enabled). In one frame,\n * usually the top-level one, there will also be an instance of the `Sidebar`\n * class that shows the sidebar app and surrounding UI. The `Guest` instance in\n * each frame connects to the sidebar and host frames as part of its\n * initialization.\n *\n * @implements {Annotator}\n * @implements {Destroyable}\n */\nexport class Guest {\n /**\n * @param {HTMLElement} element -\n * The root element in which the `Guest` instance should be able to anchor\n * or create annotations. In an ordinary web page this typically `document.body`.\n * @param {GuestConfig} [config]\n * @param {Window} [hostFrame] -\n * Host frame which this guest is associated with. This is expected to be\n * an ancestor of the guest frame. It may be same or cross origin.\n */\n constructor(element, config = {}, hostFrame = window) {\n this.element = element;\n this._hostFrame = hostFrame;\n this._highlightsVisible = false;\n this._isAdderVisible = false;\n this._informHostOnNextSelectionClear = true;\n /** @type {Range[]} - Ranges of the current text selection. */\n this.selectedRanges = [];\n\n this._adder = new Adder(this.element, {\n onAnnotate: () => this.createAnnotation(),\n onHighlight: () => this.createAnnotation({ highlight: true }),\n onShowAnnotations: tags => this.selectAnnotations(tags),\n });\n\n this._selectionObserver = new SelectionObserver(range => {\n if (range) {\n this._onSelection(range);\n } else {\n this._onClearSelection();\n }\n });\n\n /**\n * The anchors generated by resolving annotation selectors to locations in the\n * document. These are added by `anchor` and removed by `detach`.\n *\n * There is one anchor per annotation `Target`, which typically means one\n * anchor per annotation.\n *\n * @type {Anchor[]}\n */\n this.anchors = [];\n\n /**\n * Tags of annotations that are currently anchored or being anchored in\n * the guest.\n */\n this._annotations = /** @type {Set<string>} */ (new Set());\n\n // Set the frame identifier if it's available.\n // The \"top\" guest instance will have this as null since it's in a top frame not a sub frame\n /** @type {string|null} */\n this._frameIdentifier = config.subFrameIdentifier || null;\n\n this._portFinder = new PortFinder({\n hostFrame: this._hostFrame,\n source: 'guest',\n });\n\n this.features = new FeatureFlags();\n\n /**\n * Integration that handles document-type specific functionality in the\n * guest.\n */\n this._integration = createIntegration(this, {\n contentPartner: config.contentPartner,\n });\n\n /**\n * Channel for host-guest communication.\n *\n * @type {PortRPC<HostToGuestEvent, GuestToHostEvent>}\n */\n this._hostRPC = new PortRPC();\n this._connectHost(hostFrame);\n\n /**\n * Channel for guest-sidebar communication.\n *\n * @type {PortRPC<SidebarToGuestEvent, GuestToSidebarEvent>}\n */\n this._sidebarRPC = new PortRPC();\n this._connectSidebar();\n\n this._bucketBarClient = new BucketBarClient({\n contentContainer: this._integration.contentContainer(),\n hostRPC: this._hostRPC,\n });\n\n this._sideBySideActive = false;\n\n // Setup event handlers on the root element\n this._listeners = new ListenerCollection();\n this._setupElementEvents();\n\n /**\n * Tags of currently focused annotations. This is used to set the focused\n * state correctly for new highlights if the associated annotation is already\n * focused in the sidebar.\n *\n * @type {Set<string>}\n */\n this._focusedAnnotations = new Set();\n }\n\n // Add DOM event listeners for clicks, taps etc. on the document and\n // highlights.\n _setupElementEvents() {\n // Hide the sidebar in response to a document click or tap, so it doesn't obscure\n // the document content.\n /** @param {Element} element */\n const maybeCloseSidebar = element => {\n if (this._sideBySideActive) {\n // Don't hide the sidebar if event was disabled because the sidebar\n // doesn't overlap the content.\n return;\n }\n if (annotationsAt(element).length) {\n // Don't hide the sidebar if the event comes from an element that contains a highlight\n return;\n }\n this._sidebarRPC.call('closeSidebar');\n };\n\n this._listeners.add(this.element, 'mouseup', event => {\n const { target, metaKey, ctrlKey } = event;\n const tags = annotationsAt(/** @type {Element} */ (target));\n if (tags.length && this._highlightsVisible) {\n const toggle = metaKey || ctrlKey;\n this.selectAnnotations(tags, toggle);\n }\n });\n\n this._listeners.add(this.element, 'mousedown', ({ target }) => {\n maybeCloseSidebar(/** @type {Element} */ (target));\n });\n\n // Allow taps on the document to hide the sidebar as well as clicks.\n // On iOS < 13 (2019), elements like h2 or div don't emit 'click' events.\n this._listeners.add(this.element, 'touchstart', ({ target }) => {\n maybeCloseSidebar(/** @type {Element} */ (target));\n });\n\n this._listeners.add(this.element, 'mouseover', ({ target }) => {\n const tags = annotationsAt(/** @type {Element} */ (target));\n if (tags.length && this._highlightsVisible) {\n this._sidebarRPC.call('focusAnnotations', tags);\n }\n });\n\n this._listeners.add(this.element, 'mouseout', () => {\n if (this._highlightsVisible) {\n this._sidebarRPC.call('focusAnnotations', []);\n }\n });\n\n this._listeners.add(window, 'resize', () => this._repositionAdder());\n }\n\n /**\n * Retrieve metadata for the current document.\n */\n async getDocumentInfo() {\n const [uri, metadata] = await Promise.all([\n this._integration.uri(),\n this._integration.getMetadata(),\n ]);\n\n return {\n uri: normalizeURI(uri),\n metadata,\n frameIdentifier: this._frameIdentifier,\n };\n }\n\n /**\n * Shift the position of the adder on window 'resize' events\n */\n _repositionAdder() {\n if (this._isAdderVisible === false) {\n return;\n }\n const range = window.getSelection()?.getRangeAt(0);\n if (range) {\n this._onSelection(range);\n }\n }\n\n /** @param {Window} hostFrame */\n async _connectHost(hostFrame) {\n this._hostRPC.on('clearSelection', () => {\n if (selectedRange(document)) {\n this._informHostOnNextSelectionClear = false;\n removeTextSelection();\n }\n });\n\n this._hostRPC.on('createAnnotation', () => this.createAnnotation());\n\n this._hostRPC.on(\n 'focusAnnotations',\n /** @param {string[]} tags */\n tags => this._focusAnnotations(tags)\n );\n\n this._hostRPC.on(\n 'scrollToClosestOffScreenAnchor',\n /**\n * @param {string[]} tags\n * @param {'down'|'up'} direction\n */\n (tags, direction) => this._scrollToClosestOffScreenAnchor(tags, direction)\n );\n\n this._hostRPC.on(\n 'selectAnnotations',\n /**\n * @param {string[]} tags\n * @param {boolean} toggle\n */\n (tags, toggle) => this.selectAnnotations(tags, toggle)\n );\n\n this._hostRPC.on(\n 'sidebarLayoutChanged',\n /** @param {SidebarLayout} sidebarLayout */\n sidebarLayout => {\n if (frameFillsAncestor(window, hostFrame)) {\n this.fitSideBySide(sidebarLayout);\n }\n }\n );\n\n // Discover and connect to the host frame. All RPC events must be\n // registered before creating the channel.\n const hostPort = await this._portFinder.discover('host');\n this._hostRPC.connect(hostPort);\n }\n\n async _connectSidebar() {\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n /** @param {Record<string, boolean>} flags */ flags =>\n this.features.update(flags)\n );\n\n // Handlers for events sent when user hovers or clicks on an annotation card\n // in the sidebar.\n this._sidebarRPC.on(\n 'focusAnnotations',\n /** @param {string[]} tags */\n tags => this._focusAnnotations(tags)\n );\n\n this._sidebarRPC.on(\n 'scrollToAnnotation',\n /** @param {string} tag */\n tag => {\n const anchor = this.anchors.find(a => a.annotation.$tag === tag);\n if (!anchor?.highlights) {\n return;\n }\n const range = resolveAnchor(anchor);\n if (!range) {\n return;\n }\n\n // Emit a custom event that the host page can respond to. This is useful,\n // for example, if the highlighted content is contained in a collapsible\n // section of the page that needs to be un-collapsed.\n const event = new CustomEvent('scrolltorange', {\n bubbles: true,\n cancelable: true,\n detail: range,\n });\n const defaultNotPrevented = this.element.dispatchEvent(event);\n\n if (defaultNotPrevented) {\n this._integration.scrollToAnchor(anchor);\n }\n }\n );\n\n // Handler for controls on the sidebar\n this._sidebarRPC.on(\n 'setHighlightsVisible',\n /** @param {boolean} showHighlights */ showHighlights => {\n this.setHighlightsVisible(showHighlights);\n }\n );\n\n this._sidebarRPC.on(\n 'deleteAnnotation',\n /** @param {string} tag */\n tag => this.detach(tag)\n );\n\n this._sidebarRPC.on(\n 'loadAnnotations',\n /** @param {AnnotationData[]} annotations */\n annotations => annotations.forEach(annotation => this.anchor(annotation))\n );\n\n // Connect to sidebar and send document info/URIs to it.\n //\n // RPC calls are deferred until a connection is made, so these steps can\n // complete in either order.\n this._portFinder.discover('sidebar').then(port => {\n this._sidebarRPC.connect(port);\n });\n this.getDocumentInfo().then(metadata =>\n this._sidebarRPC.call('documentInfoChanged', metadata)\n );\n }\n\n destroy() {\n this._portFinder.destroy();\n this._hostRPC.destroy();\n this._sidebarRPC.destroy();\n\n this._listeners.removeAll();\n\n this._selectionObserver.disconnect();\n this._adder.destroy();\n this._bucketBarClient.destroy();\n\n removeAllHighlights(this.element);\n\n this._integration.destroy();\n }\n\n /**\n * Anchor an annotation's selectors in the document.\n *\n * _Anchoring_ resolves a set of selectors to a concrete region of the document\n * which is then highlighted.\n *\n * Any existing anchors associated with `annotation` will be removed before\n * re-anchoring the annotation.\n *\n * @param {AnnotationData} annotation\n * @return {Promise<Anchor[]>}\n */\n async anchor(annotation) {\n /**\n * Resolve an annotation's selectors to a concrete range.\n *\n * @param {Target} target\n * @return {Promise<Anchor>}\n */\n const locate = async target => {\n // Only annotations with an associated quote can currently be anchored.\n // This is because the quote is used to verify anchoring with other selector\n // types.\n if (\n !target.selector ||\n !target.selector.some(s => s.type === 'TextQuoteSelector')\n ) {\n return { annotation, target };\n }\n\n /** @type {Anchor} */\n let anchor;\n try {\n const range = await this._integration.anchor(\n this.element,\n target.selector\n );\n // Convert the `Range` to a `TextRange` which can be converted back to\n // a `Range` later. The `TextRange` representation allows for highlights\n // to be inserted during anchoring other annotations without \"breaking\"\n // this anchor.\n const textRange = TextRange.fromRange(range);\n anchor = { annotation, target, range: textRange };\n } catch (err) {\n anchor = { annotation, target };\n }\n return anchor;\n };\n\n /**\n * Highlight the text range that `anchor` refers to.\n *\n * @param {Anchor} anchor\n */\n const highlight = anchor => {\n const range = resolveAnchor(anchor);\n if (!range) {\n return;\n }\n\n const highlights = /** @type {AnnotationHighlight[]} */ (\n highlightRange(range)\n );\n highlights.forEach(h => {\n h._annotation = anchor.annotation;\n });\n anchor.highlights = highlights;\n\n if (this._focusedAnnotations.has(anchor.annotation.$tag)) {\n setHighlightsFocused(highlights, true);\n }\n };\n\n // Remove existing anchors for this annotation.\n this.detach(annotation.$tag, false /* notify */);\n\n this._annotations.add(annotation.$tag);\n\n // Resolve selectors to ranges and insert highlights.\n if (!annotation.target) {\n annotation.target = [];\n }\n const anchors = await Promise.all(annotation.target.map(locate));\n\n // If the annotation was removed while anchoring, don't save the anchors.\n if (!this._annotations.has(annotation.$tag)) {\n return [];\n }\n\n for (let anchor of anchors) {\n highlight(anchor);\n }\n\n // Set flag indicating whether anchoring succeeded. For each target,\n // anchoring is successful either if there are no selectors (ie. this is a\n // Page Note) or we successfully resolved the selectors to a range.\n annotation.$orphan =\n anchors.length > 0 &&\n anchors.every(anchor => anchor.target.selector && !anchor.range);\n\n this._updateAnchors(this.anchors.concat(anchors), true /* notify */);\n\n // Let other frames (eg. the sidebar) know about the new annotation.\n this._sidebarRPC.call('syncAnchoringStatus', annotation);\n\n return anchors;\n }\n\n /**\n * Remove the anchors and associated highlights for an annotation from the document.\n *\n * @param {string} tag\n * @param {boolean} [notify] - For internal use. Whether to inform the host\n * frame about the removal of an anchor.\n */\n detach(tag, notify = true) {\n this._annotations.delete(tag);\n\n /** @type {Anchor[]} */\n const anchors = [];\n for (let anchor of this.anchors) {\n if (anchor.annotation.$tag !== tag) {\n anchors.push(anchor);\n } else if (anchor.highlights) {\n removeHighlights(anchor.highlights);\n }\n }\n this._updateAnchors(anchors, notify);\n }\n\n /**\n * @param {Anchor[]} anchors\n * @param {boolean} notify\n */\n _updateAnchors(anchors, notify) {\n this.anchors = anchors;\n if (notify) {\n this._bucketBarClient.update(this.anchors);\n }\n }\n\n /**\n * Create a new annotation that is associated with the selected region of\n * the current document.\n *\n * @param {object} options\n * @param {boolean} [options.highlight] - If true, the new annotation has\n * the `$highlight` flag set, causing it to be saved immediately without\n * prompting for a comment.\n * @return {Promise<AnnotationData>} - The new annotation\n */\n async createAnnotation({ highlight = false } = {}) {\n const ranges = this.selectedRanges;\n this.selectedRanges = [];\n\n const info = await this.getDocumentInfo();\n const root = this.element;\n const rangeSelectors = await Promise.all(\n ranges.map(range => this._integration.describe(root, range))\n );\n const target = rangeSelectors.map(selectors => ({\n source: info.uri,\n\n // In the Hypothesis API the field containing the selectors is called\n // `selector`, despite being a list.\n selector: selectors,\n }));\n\n /** @type {AnnotationData} */\n const annotation = {\n uri: info.uri,\n document: info.metadata,\n target,\n $highlight: highlight,\n $tag: 'a:' + generateHexString(8),\n };\n\n this._sidebarRPC.call('createAnnotation', annotation);\n this.anchor(annotation);\n\n // Removing the text selection triggers the `SelectionObserver` callback,\n // which causes the adder to be removed after some delay.\n removeTextSelection();\n\n return annotation;\n }\n\n /**\n * Indicate in the sidebar that certain annotations are focused (ie. the\n * associated document region(s) is hovered).\n *\n * @param {string[]} tags\n */\n _focusAnnotations(tags) {\n this._focusedAnnotations.clear();\n tags.forEach(tag => this._focusedAnnotations.add(tag));\n\n for (let anchor of this.anchors) {\n if (anchor.highlights) {\n const toggle = tags.includes(anchor.annotation.$tag);\n setHighlightsFocused(anchor.highlights, toggle);\n }\n }\n\n this._sidebarRPC.call('focusAnnotations', tags);\n }\n\n /**\n * Scroll to the closest off screen anchor.\n *\n * @param {string[]} tags\n * @param {'down'|'up'} direction\n */\n _scrollToClosestOffScreenAnchor(tags, direction) {\n const anchors = this.anchors.filter(({ annotation }) =>\n tags.includes(annotation.$tag)\n );\n const closest = findClosestOffscreenAnchor(anchors, direction);\n if (closest) {\n this._integration.scrollToAnchor(closest);\n }\n }\n\n /**\n * Show or hide the adder toolbar when the selection changes.\n *\n * @param {Range} range\n */\n _onSelection(range) {\n if (!this._integration.canAnnotate(range)) {\n this._onClearSelection();\n return;\n }\n\n const selection = /** @type {Selection} */ (document.getSelection());\n const isBackwards = rangeUtil.isSelectionBackwards(selection);\n const focusRect = rangeUtil.selectionFocusRect(selection);\n if (!focusRect) {\n // The selected range does not contain any text\n this._onClearSelection();\n return;\n }\n\n this.selectedRanges = [range];\n this._hostRPC.call('textSelected');\n\n this._adder.annotationsForSelection = annotationsForSelection();\n this._isAdderVisible = true;\n this._adder.show(focusRect, isBackwards);\n }\n\n _onClearSelection() {\n this._isAdderVisible = false;\n this._adder.hide();\n this.selectedRanges = [];\n if (this._informHostOnNextSelectionClear) {\n this._hostRPC.call('textUnselected');\n }\n this._informHostOnNextSelectionClear = true;\n }\n\n /**\n * Show the given annotations in the sidebar.\n *\n * This sets up a filter in the sidebar to show only the selected annotations\n * and opens the sidebar.\n *\n * @param {string[]} tags\n * @param {boolean} [toggle] - Toggle whether the annotations are selected\n * instead of showing them regardless of whether they are currently selected.\n */\n selectAnnotations(tags, toggle = false) {\n if (toggle) {\n this._sidebarRPC.call('toggleAnnotationSelection', tags);\n } else {\n this._sidebarRPC.call('showAnnotations', tags);\n }\n this._sidebarRPC.call('openSidebar');\n }\n\n /**\n * Set whether highlights are visible in the document or not.\n *\n * @param {boolean} visible\n */\n setHighlightsVisible(visible) {\n setHighlightsVisible(this.element, visible);\n this._highlightsVisible = visible;\n }\n\n /**\n * Attempt to fit the document content alongside the sidebar.\n *\n * @param {SidebarLayout} sidebarLayout\n */\n fitSideBySide(sidebarLayout) {\n this._sideBySideActive = this._integration.fitSideBySide(sidebarLayout);\n }\n\n /**\n * Return true if side-by-side mode is currently active.\n *\n * Side-by-side mode is activated or de-activated when `fitSideBySide` is called\n * depending on whether the sidebar is expanded and whether there is room for\n * the content alongside the sidebar.\n */\n get sideBySideActive() {\n return this._sideBySideActive;\n }\n\n /**\n * Return the tags of annotations that are currently displayed in a focused\n * state.\n *\n * @return {Set<string>}\n */\n get focusedAnnotationTags() {\n return this._focusedAnnotations;\n }\n}\n","/**\n * Test whether an iframe fills the viewport of an ancestor frame.\n *\n * @param {Window} frame\n * @param {Window} ancestor\n */\nexport function frameFillsAncestor(frame, ancestor) {\n if (frame === ancestor) {\n return true;\n }\n\n if (frame.parent !== ancestor) {\n // To keep things simple, we initially only support direct ancestors.\n return false;\n }\n\n if (!frame.frameElement) {\n // This is a cross-origin iframe. In this case we can't tell if it fills\n // the parent frame or not.\n return false;\n }\n\n const frameBox = frame.frameElement.getBoundingClientRect();\n\n // Threshold for deciding when a frame occupies enough of its parent's width\n // to count as filling the viewport.\n const fullWidthThreshold = 0.8;\n\n return frameBox.width / frame.parent.innerWidth >= fullWidthThreshold;\n}\n","/**\n * Encode app configuration in a URL fragment.\n *\n * This is used by the annotator to pass configuration to the sidebar and\n * notebook apps, which they can easily read on startup. The configuration is\n * passed in the fragment to avoid invalidating cache entries for the URL\n * or adding noise to server logs.\n *\n * @param {string} baseURL\n * @param {object} config\n * @return {string} URL with added fragment\n */\nexport function addConfigFragment(baseURL, config) {\n const url = new URL(baseURL);\n const params = new URLSearchParams();\n params.append('config', JSON.stringify(config));\n url.hash = params.toString();\n return url.toString();\n}\n\n/**\n * Parse configuration from a URL generated by {@link addConfigFragment}.\n *\n * @param {string} url\n * @return {Record<string, unknown>}\n */\nexport function parseConfigFragment(url) {\n const configStr = new URL(url).hash.slice(1);\n const configJSON = new URLSearchParams(configStr).get('config');\n return JSON.parse(configJSON || '{}');\n}\n","/**\n * Create the JSON-serializable subset of annotator configuration that should\n * be passed to the sidebar or notebook applications.\n *\n * @param {string} appURL - URL from which the application will be served\n * @param {Record<string, unknown>} config\n * @return {Record<string, unknown>}\n */\nexport function createAppConfig(appURL, config) {\n /** @type {Record<string, unknown>} */\n const appConfig = {};\n\n for (let [key, value] of Object.entries(config)) {\n // Remove several annotator-only properties.\n //\n // nb. We don't currently strip all the annotator-only properties here.\n // That's OK because validation / filtering happens in the sidebar app itself.\n // It just results in unnecessary content in the sidebar iframe's URL string.\n if (key === 'notebookAppUrl' || key === 'sidebarAppUrl') {\n continue;\n }\n\n // Strip nullish properties, as these are ignored by the application and\n // they add noise to logs etc.\n //\n // eslint-disable-next-line eqeqeq\n if (value == null) {\n continue;\n }\n\n appConfig[key] = value;\n }\n\n // Pass the expected origin of the app. This is used to detect when it is\n // served from a different location than expected, which may stop it working.\n appConfig.origin = new URL(appURL).origin;\n\n // Pass the version of the client, so we can check if it is the same as the\n // one used in the sidebar/notebook.\n appConfig.version = '__VERSION__';\n\n // Pass the URL of the page that embedded the client.\n const hostURL = new URL(window.location.href);\n hostURL.hash = '';\n appConfig.hostURL = hostURL.toString();\n\n // Some config settings are not JSON-stringifiable (e.g. JavaScript\n // functions) and will be omitted when the config is JSON-stringified.\n // Add a JSON-stringifiable option for each of these so that the sidebar can\n // at least know whether the callback functions were provided or not.\n if (Array.isArray(appConfig.services) && appConfig.services?.length > 0) {\n const service = appConfig.services[0];\n if (service.onLoginRequest) {\n service.onLoginRequestProvided = true;\n }\n if (service.onLogoutRequest) {\n service.onLogoutRequestProvided = true;\n }\n if (service.onSignupRequest) {\n service.onSignupRequestProvided = true;\n }\n if (service.onProfileRequest) {\n service.onProfileRequestProvided = true;\n }\n if (service.onHelpRequest) {\n service.onHelpRequestProvided = true;\n }\n }\n\n return appConfig;\n}\n","import { IconButton } from '@hypothesis/frontend-shared';\nimport { useEffect, useRef, useState } from 'preact/hooks';\nimport classnames from 'classnames';\n\nimport { addConfigFragment } from '../../shared/config-fragment';\nimport { createAppConfig } from '../config/app';\n\n/**\n * Configuration used to launch the notebook application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n *\n * @typedef {{ notebookAppUrl: string } & Record<string, unknown>} NotebookConfig\n */\n\n/**\n * @typedef NotebookIframeProps\n * @prop {NotebookConfig} config\n * @prop {string} groupId\n */\n\n/**\n * Create the iframe that will load the notebook application.\n *\n * @param {NotebookIframeProps} props\n */\nfunction NotebookIframe({ config, groupId }) {\n const notebookAppSrc = addConfigFragment(config.notebookAppUrl, {\n ...createAppConfig(config.notebookAppUrl, config),\n\n // Explicity set the \"focused\" group\n group: groupId,\n });\n\n return (\n <iframe\n title={'Hypothesis annotation notebook'}\n className=\"h-full w-full border-0\"\n // Enable media in annotations to be shown fullscreen\n allowFullScreen\n src={notebookAppSrc}\n />\n );\n}\n\n/** @typedef {import('../util/emitter').Emitter} Emitter */\n\n/**\n * @typedef NotebookModalProps\n * @prop {import('../util/emitter').EventBus} eventBus\n * @prop {NotebookConfig} config\n */\n\n/**\n * Create a modal component that hosts (1) the notebook iframe and (2) a button to close the modal.\n *\n * @param {NotebookModalProps} props\n */\nexport default function NotebookModal({ eventBus, config }) {\n // Temporary solution: while there is no mechanism to sync new annotations in\n // the notebook, we force re-rendering of the iframe on every 'openNotebook'\n // event, so that the new annotations are displayed.\n // https://github.com/hypothesis/client/issues/3182\n const [iframeKey, setIframeKey] = useState(0);\n const [isHidden, setIsHidden] = useState(true);\n const [groupId, setGroupId] = useState(/** @type {string|null} */ (null));\n const originalDocumentOverflowStyle = useRef('');\n const emitterRef = useRef(/** @type {Emitter|null} */ (null));\n\n // Stores the original overflow CSS property of document.body and reset it\n // when the component is destroyed\n useEffect(() => {\n originalDocumentOverflowStyle.current = document.body.style.overflow;\n\n return () => {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n };\n }, []);\n\n // The overflow CSS property is set to hidden to prevent scrolling of the host page,\n // while the notebook modal is open. It is restored when the modal is closed.\n useEffect(() => {\n if (isHidden) {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n } else {\n document.body.style.overflow = 'hidden';\n }\n }, [isHidden]);\n\n useEffect(() => {\n const emitter = eventBus.createEmitter();\n emitter.subscribe('openNotebook', (/** @type {string} */ groupId) => {\n setIsHidden(false);\n setIframeKey(iframeKey => iframeKey + 1);\n setGroupId(groupId);\n });\n emitterRef.current = emitter;\n\n return () => {\n emitter.destroy();\n };\n }, [eventBus]);\n\n const onClose = () => {\n setIsHidden(true);\n emitterRef.current?.publish('closeNotebook');\n };\n\n if (groupId === null) {\n return null;\n }\n\n return (\n <div\n className={classnames(\n 'fixed z-max top-0 left-0 right-0 bottom-0 p-3 bg-black/50',\n { hidden: isHidden }\n )}\n data-testid=\"notebook-outer\"\n >\n <div className=\"relative w-full h-full\" data-testid=\"notebook-inner\">\n <div className=\"absolute right-0 text-xl m-3\">\n <IconButton\n icon=\"cancel\"\n title=\"Close the Notebook\"\n onClick={onClose}\n variant=\"dark\"\n />\n </div>\n <NotebookIframe key={iframeKey} config={config} groupId={groupId} />\n </div>\n </div>\n );\n}\n","import { createShadowRoot } from './util/shadow-root';\nimport { render } from 'preact';\nimport NotebookModal from './components/NotebookModal';\n\n/**\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('./components/NotebookModal').NotebookConfig} NotebookConfig\n */\n\n/** @implements {Destroyable} */\nexport class Notebook {\n /**\n * @param {HTMLElement} element\n * @param {import('./util/emitter').EventBus} eventBus -\n * Enables communication between components sharing the same eventBus\n * @param {NotebookConfig} config\n */\n constructor(element, eventBus, config) {\n /**\n * Un-styled shadow host for the notebook content.\n * This isolates the notebook from the page's styles.\n */\n this._outerContainer = document.createElement('hypothesis-notebook');\n element.appendChild(this._outerContainer);\n this.shadowRoot = createShadowRoot(this._outerContainer);\n\n render(\n <NotebookModal eventBus={eventBus} config={config} />,\n this.shadowRoot\n );\n }\n\n destroy() {\n render(null, this.shadowRoot);\n this._outerContainer.remove();\n }\n}\n","/*! Hammer.JS - v2.0.7 - 2016-04-22\n * http://hammerjs.github.io/\n *\n * Copyright (c) 2016 Jorik Tangelder;\n * Licensed under the MIT license */\n(function(window, document, exportName, undefined) {\n 'use strict';\n\nvar VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];\nvar TEST_ELEMENT = document.createElement('div');\n\nvar TYPE_FUNCTION = 'function';\n\nvar round = Math.round;\nvar abs = Math.abs;\nvar now = Date.now;\n\n/**\n * set a timeout with a given scope\n * @param {Function} fn\n * @param {Number} timeout\n * @param {Object} context\n * @returns {number}\n */\nfunction setTimeoutContext(fn, timeout, context) {\n return setTimeout(bindFn(fn, context), timeout);\n}\n\n/**\n * if the argument is an array, we want to execute the fn on each entry\n * if it aint an array we don't want to do a thing.\n * this is used by all the methods that accept a single and array argument.\n * @param {*|Array} arg\n * @param {String} fn\n * @param {Object} [context]\n * @returns {Boolean}\n */\nfunction invokeArrayArg(arg, fn, context) {\n if (Array.isArray(arg)) {\n each(arg, context[fn], context);\n return true;\n }\n return false;\n}\n\n/**\n * walk objects and arrays\n * @param {Object} obj\n * @param {Function} iterator\n * @param {Object} context\n */\nfunction each(obj, iterator, context) {\n var i;\n\n if (!obj) {\n return;\n }\n\n if (obj.forEach) {\n obj.forEach(iterator, context);\n } else if (obj.length !== undefined) {\n i = 0;\n while (i < obj.length) {\n iterator.call(context, obj[i], i, obj);\n i++;\n }\n } else {\n for (i in obj) {\n obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);\n }\n }\n}\n\n/**\n * wrap a method with a deprecation warning and stack trace\n * @param {Function} method\n * @param {String} name\n * @param {String} message\n * @returns {Function} A new function wrapping the supplied method.\n */\nfunction deprecate(method, name, message) {\n var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\\n' + message + ' AT \\n';\n return function() {\n var e = new Error('get-stack-trace');\n var stack = e && e.stack ? e.stack.replace(/^[^\\(]+?[\\n$]/gm, '')\n .replace(/^\\s+at\\s+/gm, '')\n .replace(/^Object.<anonymous>\\s*\\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';\n\n var log = window.console && (window.console.warn || window.console.log);\n if (log) {\n log.call(window.console, deprecationMessage, stack);\n }\n return method.apply(this, arguments);\n };\n}\n\n/**\n * extend object.\n * means that properties in dest will be overwritten by the ones in src.\n * @param {Object} target\n * @param {...Object} objects_to_assign\n * @returns {Object} target\n */\nvar assign;\nif (typeof Object.assign !== 'function') {\n assign = function assign(target) {\n if (target === undefined || target === null) {\n throw new TypeError('Cannot convert undefined or null to object');\n }\n\n var output = Object(target);\n for (var index = 1; index < arguments.length; index++) {\n var source = arguments[index];\n if (source !== undefined && source !== null) {\n for (var nextKey in source) {\n if (source.hasOwnProperty(nextKey)) {\n output[nextKey] = source[nextKey];\n }\n }\n }\n }\n return output;\n };\n} else {\n assign = Object.assign;\n}\n\n/**\n * extend object.\n * means that properties in dest will be overwritten by the ones in src.\n * @param {Object} dest\n * @param {Object} src\n * @param {Boolean} [merge=false]\n * @returns {Object} dest\n */\nvar extend = deprecate(function extend(dest, src, merge) {\n var keys = Object.keys(src);\n var i = 0;\n while (i < keys.length) {\n if (!merge || (merge && dest[keys[i]] === undefined)) {\n dest[keys[i]] = src[keys[i]];\n }\n i++;\n }\n return dest;\n}, 'extend', 'Use `assign`.');\n\n/**\n * merge the values from src in the dest.\n * means that properties that exist in dest will not be overwritten by src\n * @param {Object} dest\n * @param {Object} src\n * @returns {Object} dest\n */\nvar merge = deprecate(function merge(dest, src) {\n return extend(dest, src, true);\n}, 'merge', 'Use `assign`.');\n\n/**\n * simple class inheritance\n * @param {Function} child\n * @param {Function} base\n * @param {Object} [properties]\n */\nfunction inherit(child, base, properties) {\n var baseP = base.prototype,\n childP;\n\n childP = child.prototype = Object.create(baseP);\n childP.constructor = child;\n childP._super = baseP;\n\n if (properties) {\n assign(childP, properties);\n }\n}\n\n/**\n * simple function bind\n * @param {Function} fn\n * @param {Object} context\n * @returns {Function}\n */\nfunction bindFn(fn, context) {\n return function boundFn() {\n return fn.apply(context, arguments);\n };\n}\n\n/**\n * let a boolean value also be a function that must return a boolean\n * this first item in args will be used as the context\n * @param {Boolean|Function} val\n * @param {Array} [args]\n * @returns {Boolean}\n */\nfunction boolOrFn(val, args) {\n if (typeof val == TYPE_FUNCTION) {\n return val.apply(args ? args[0] || undefined : undefined, args);\n }\n return val;\n}\n\n/**\n * use the val2 when val1 is undefined\n * @param {*} val1\n * @param {*} val2\n * @returns {*}\n */\nfunction ifUndefined(val1, val2) {\n return (val1 === undefined) ? val2 : val1;\n}\n\n/**\n * addEventListener with multiple events at once\n * @param {EventTarget} target\n * @param {String} types\n * @param {Function} handler\n */\nfunction addEventListeners(target, types, handler) {\n each(splitStr(types), function(type) {\n target.addEventListener(type, handler, false);\n });\n}\n\n/**\n * removeEventListener with multiple events at once\n * @param {EventTarget} target\n * @param {String} types\n * @param {Function} handler\n */\nfunction removeEventListeners(target, types, handler) {\n each(splitStr(types), function(type) {\n target.removeEventListener(type, handler, false);\n });\n}\n\n/**\n * find if a node is in the given parent\n * @method hasParent\n * @param {HTMLElement} node\n * @param {HTMLElement} parent\n * @return {Boolean} found\n */\nfunction hasParent(node, parent) {\n while (node) {\n if (node == parent) {\n return true;\n }\n node = node.parentNode;\n }\n return false;\n}\n\n/**\n * small indexOf wrapper\n * @param {String} str\n * @param {String} find\n * @returns {Boolean} found\n */\nfunction inStr(str, find) {\n return str.indexOf(find) > -1;\n}\n\n/**\n * split string on whitespace\n * @param {String} str\n * @returns {Array} words\n */\nfunction splitStr(str) {\n return str.trim().split(/\\s+/g);\n}\n\n/**\n * find if a array contains the object using indexOf or a simple polyFill\n * @param {Array} src\n * @param {String} find\n * @param {String} [findByKey]\n * @return {Boolean|Number} false when not found, or the index\n */\nfunction inArray(src, find, findByKey) {\n if (src.indexOf && !findByKey) {\n return src.indexOf(find);\n } else {\n var i = 0;\n while (i < src.length) {\n if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {\n return i;\n }\n i++;\n }\n return -1;\n }\n}\n\n/**\n * convert array-like objects to real arrays\n * @param {Object} obj\n * @returns {Array}\n */\nfunction toArray(obj) {\n return Array.prototype.slice.call(obj, 0);\n}\n\n/**\n * unique array with objects based on a key (like 'id') or just by the array's value\n * @param {Array} src [{id:1},{id:2},{id:1}]\n * @param {String} [key]\n * @param {Boolean} [sort=False]\n * @returns {Array} [{id:1},{id:2}]\n */\nfunction uniqueArray(src, key, sort) {\n var results = [];\n var values = [];\n var i = 0;\n\n while (i < src.length) {\n var val = key ? src[i][key] : src[i];\n if (inArray(values, val) < 0) {\n results.push(src[i]);\n }\n values[i] = val;\n i++;\n }\n\n if (sort) {\n if (!key) {\n results = results.sort();\n } else {\n results = results.sort(function sortUniqueArray(a, b) {\n return a[key] > b[key];\n });\n }\n }\n\n return results;\n}\n\n/**\n * get the prefixed property\n * @param {Object} obj\n * @param {String} property\n * @returns {String|Undefined} prefixed\n */\nfunction prefixed(obj, property) {\n var prefix, prop;\n var camelProp = property[0].toUpperCase() + property.slice(1);\n\n var i = 0;\n while (i < VENDOR_PREFIXES.length) {\n prefix = VENDOR_PREFIXES[i];\n prop = (prefix) ? prefix + camelProp : property;\n\n if (prop in obj) {\n return prop;\n }\n i++;\n }\n return undefined;\n}\n\n/**\n * get a unique id\n * @returns {number} uniqueId\n */\nvar _uniqueId = 1;\nfunction uniqueId() {\n return _uniqueId++;\n}\n\n/**\n * get the window object of an element\n * @param {HTMLElement} element\n * @returns {DocumentView|Window}\n */\nfunction getWindowForElement(element) {\n var doc = element.ownerDocument || element;\n return (doc.defaultView || doc.parentWindow || window);\n}\n\nvar MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;\n\nvar SUPPORT_TOUCH = ('ontouchstart' in window);\nvar SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;\nvar SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);\n\nvar INPUT_TYPE_TOUCH = 'touch';\nvar INPUT_TYPE_PEN = 'pen';\nvar INPUT_TYPE_MOUSE = 'mouse';\nvar INPUT_TYPE_KINECT = 'kinect';\n\nvar COMPUTE_INTERVAL = 25;\n\nvar INPUT_START = 1;\nvar INPUT_MOVE = 2;\nvar INPUT_END = 4;\nvar INPUT_CANCEL = 8;\n\nvar DIRECTION_NONE = 1;\nvar DIRECTION_LEFT = 2;\nvar DIRECTION_RIGHT = 4;\nvar DIRECTION_UP = 8;\nvar DIRECTION_DOWN = 16;\n\nvar DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;\nvar DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;\nvar DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;\n\nvar PROPS_XY = ['x', 'y'];\nvar PROPS_CLIENT_XY = ['clientX', 'clientY'];\n\n/**\n * create new input type manager\n * @param {Manager} manager\n * @param {Function} callback\n * @returns {Input}\n * @constructor\n */\nfunction Input(manager, callback) {\n var self = this;\n this.manager = manager;\n this.callback = callback;\n this.element = manager.element;\n this.target = manager.options.inputTarget;\n\n // smaller wrapper around the handler, for the scope and the enabled state of the manager,\n // so when disabled the input events are completely bypassed.\n this.domHandler = function(ev) {\n if (boolOrFn(manager.options.enable, [manager])) {\n self.handler(ev);\n }\n };\n\n this.init();\n\n}\n\nInput.prototype = {\n /**\n * should handle the inputEvent data and trigger the callback\n * @virtual\n */\n handler: function() { },\n\n /**\n * bind the events\n */\n init: function() {\n this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);\n this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);\n this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);\n },\n\n /**\n * unbind the events\n */\n destroy: function() {\n this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);\n this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);\n this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);\n }\n};\n\n/**\n * create new input type manager\n * called by the Manager constructor\n * @param {Hammer} manager\n * @returns {Input}\n */\nfunction createInputInstance(manager) {\n var Type;\n var inputClass = manager.options.inputClass;\n\n if (inputClass) {\n Type = inputClass;\n } else if (SUPPORT_POINTER_EVENTS) {\n Type = PointerEventInput;\n } else if (SUPPORT_ONLY_TOUCH) {\n Type = TouchInput;\n } else if (!SUPPORT_TOUCH) {\n Type = MouseInput;\n } else {\n Type = TouchMouseInput;\n }\n return new (Type)(manager, inputHandler);\n}\n\n/**\n * handle input events\n * @param {Manager} manager\n * @param {String} eventType\n * @param {Object} input\n */\nfunction inputHandler(manager, eventType, input) {\n var pointersLen = input.pointers.length;\n var changedPointersLen = input.changedPointers.length;\n var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));\n var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));\n\n input.isFirst = !!isFirst;\n input.isFinal = !!isFinal;\n\n if (isFirst) {\n manager.session = {};\n }\n\n // source event is the normalized value of the domEvents\n // like 'touchstart, mouseup, pointerdown'\n input.eventType = eventType;\n\n // compute scale, rotation etc\n computeInputData(manager, input);\n\n // emit secret event\n manager.emit('hammer.input', input);\n\n manager.recognize(input);\n manager.session.prevInput = input;\n}\n\n/**\n * extend the data with some usable properties like scale, rotate, velocity etc\n * @param {Object} manager\n * @param {Object} input\n */\nfunction computeInputData(manager, input) {\n var session = manager.session;\n var pointers = input.pointers;\n var pointersLength = pointers.length;\n\n // store the first input to calculate the distance and direction\n if (!session.firstInput) {\n session.firstInput = simpleCloneInputData(input);\n }\n\n // to compute scale and rotation we need to store the multiple touches\n if (pointersLength > 1 && !session.firstMultiple) {\n session.firstMultiple = simpleCloneInputData(input);\n } else if (pointersLength === 1) {\n session.firstMultiple = false;\n }\n\n var firstInput = session.firstInput;\n var firstMultiple = session.firstMultiple;\n var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;\n\n var center = input.center = getCenter(pointers);\n input.timeStamp = now();\n input.deltaTime = input.timeStamp - firstInput.timeStamp;\n\n input.angle = getAngle(offsetCenter, center);\n input.distance = getDistance(offsetCenter, center);\n\n computeDeltaXY(session, input);\n input.offsetDirection = getDirection(input.deltaX, input.deltaY);\n\n var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);\n input.overallVelocityX = overallVelocity.x;\n input.overallVelocityY = overallVelocity.y;\n input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;\n\n input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;\n input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;\n\n input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >\n session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);\n\n computeIntervalInputData(session, input);\n\n // find the correct target\n var target = manager.element;\n if (hasParent(input.srcEvent.target, target)) {\n target = input.srcEvent.target;\n }\n input.target = target;\n}\n\nfunction computeDeltaXY(session, input) {\n var center = input.center;\n var offset = session.offsetDelta || {};\n var prevDelta = session.prevDelta || {};\n var prevInput = session.prevInput || {};\n\n if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {\n prevDelta = session.prevDelta = {\n x: prevInput.deltaX || 0,\n y: prevInput.deltaY || 0\n };\n\n offset = session.offsetDelta = {\n x: center.x,\n y: center.y\n };\n }\n\n input.deltaX = prevDelta.x + (center.x - offset.x);\n input.deltaY = prevDelta.y + (center.y - offset.y);\n}\n\n/**\n * velocity is calculated every x ms\n * @param {Object} session\n * @param {Object} input\n */\nfunction computeIntervalInputData(session, input) {\n var last = session.lastInterval || input,\n deltaTime = input.timeStamp - last.timeStamp,\n velocity, velocityX, velocityY, direction;\n\n if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {\n var deltaX = input.deltaX - last.deltaX;\n var deltaY = input.deltaY - last.deltaY;\n\n var v = getVelocity(deltaTime, deltaX, deltaY);\n velocityX = v.x;\n velocityY = v.y;\n velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;\n direction = getDirection(deltaX, deltaY);\n\n session.lastInterval = input;\n } else {\n // use latest velocity info if it doesn't overtake a minimum period\n velocity = last.velocity;\n velocityX = last.velocityX;\n velocityY = last.velocityY;\n direction = last.direction;\n }\n\n input.velocity = velocity;\n input.velocityX = velocityX;\n input.velocityY = velocityY;\n input.direction = direction;\n}\n\n/**\n * create a simple clone from the input used for storage of firstInput and firstMultiple\n * @param {Object} input\n * @returns {Object} clonedInputData\n */\nfunction simpleCloneInputData(input) {\n // make a simple copy of the pointers because we will get a reference if we don't\n // we only need clientXY for the calculations\n var pointers = [];\n var i = 0;\n while (i < input.pointers.length) {\n pointers[i] = {\n clientX: round(input.pointers[i].clientX),\n clientY: round(input.pointers[i].clientY)\n };\n i++;\n }\n\n return {\n timeStamp: now(),\n pointers: pointers,\n center: getCenter(pointers),\n deltaX: input.deltaX,\n deltaY: input.deltaY\n };\n}\n\n/**\n * get the center of all the pointers\n * @param {Array} pointers\n * @return {Object} center contains `x` and `y` properties\n */\nfunction getCenter(pointers) {\n var pointersLength = pointers.length;\n\n // no need to loop when only one touch\n if (pointersLength === 1) {\n return {\n x: round(pointers[0].clientX),\n y: round(pointers[0].clientY)\n };\n }\n\n var x = 0, y = 0, i = 0;\n while (i < pointersLength) {\n x += pointers[i].clientX;\n y += pointers[i].clientY;\n i++;\n }\n\n return {\n x: round(x / pointersLength),\n y: round(y / pointersLength)\n };\n}\n\n/**\n * calculate the velocity between two points. unit is in px per ms.\n * @param {Number} deltaTime\n * @param {Number} x\n * @param {Number} y\n * @return {Object} velocity `x` and `y`\n */\nfunction getVelocity(deltaTime, x, y) {\n return {\n x: x / deltaTime || 0,\n y: y / deltaTime || 0\n };\n}\n\n/**\n * get the direction between two points\n * @param {Number} x\n * @param {Number} y\n * @return {Number} direction\n */\nfunction getDirection(x, y) {\n if (x === y) {\n return DIRECTION_NONE;\n }\n\n if (abs(x) >= abs(y)) {\n return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;\n}\n\n/**\n * calculate the absolute distance between two points\n * @param {Object} p1 {x, y}\n * @param {Object} p2 {x, y}\n * @param {Array} [props] containing x and y keys\n * @return {Number} distance\n */\nfunction getDistance(p1, p2, props) {\n if (!props) {\n props = PROPS_XY;\n }\n var x = p2[props[0]] - p1[props[0]],\n y = p2[props[1]] - p1[props[1]];\n\n return Math.sqrt((x * x) + (y * y));\n}\n\n/**\n * calculate the angle between two coordinates\n * @param {Object} p1\n * @param {Object} p2\n * @param {Array} [props] containing x and y keys\n * @return {Number} angle\n */\nfunction getAngle(p1, p2, props) {\n if (!props) {\n props = PROPS_XY;\n }\n var x = p2[props[0]] - p1[props[0]],\n y = p2[props[1]] - p1[props[1]];\n return Math.atan2(y, x) * 180 / Math.PI;\n}\n\n/**\n * calculate the rotation degrees between two pointersets\n * @param {Array} start array of pointers\n * @param {Array} end array of pointers\n * @return {Number} rotation\n */\nfunction getRotation(start, end) {\n return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);\n}\n\n/**\n * calculate the scale factor between two pointersets\n * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out\n * @param {Array} start array of pointers\n * @param {Array} end array of pointers\n * @return {Number} scale\n */\nfunction getScale(start, end) {\n return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);\n}\n\nvar MOUSE_INPUT_MAP = {\n mousedown: INPUT_START,\n mousemove: INPUT_MOVE,\n mouseup: INPUT_END\n};\n\nvar MOUSE_ELEMENT_EVENTS = 'mousedown';\nvar MOUSE_WINDOW_EVENTS = 'mousemove mouseup';\n\n/**\n * Mouse events input\n * @constructor\n * @extends Input\n */\nfunction MouseInput() {\n this.evEl = MOUSE_ELEMENT_EVENTS;\n this.evWin = MOUSE_WINDOW_EVENTS;\n\n this.pressed = false; // mousedown state\n\n Input.apply(this, arguments);\n}\n\ninherit(MouseInput, Input, {\n /**\n * handle mouse events\n * @param {Object} ev\n */\n handler: function MEhandler(ev) {\n var eventType = MOUSE_INPUT_MAP[ev.type];\n\n // on start we want to have the left mouse button down\n if (eventType & INPUT_START && ev.button === 0) {\n this.pressed = true;\n }\n\n if (eventType & INPUT_MOVE && ev.which !== 1) {\n eventType = INPUT_END;\n }\n\n // mouse must be down\n if (!this.pressed) {\n return;\n }\n\n if (eventType & INPUT_END) {\n this.pressed = false;\n }\n\n this.callback(this.manager, eventType, {\n pointers: [ev],\n changedPointers: [ev],\n pointerType: INPUT_TYPE_MOUSE,\n srcEvent: ev\n });\n }\n});\n\nvar POINTER_INPUT_MAP = {\n pointerdown: INPUT_START,\n pointermove: INPUT_MOVE,\n pointerup: INPUT_END,\n pointercancel: INPUT_CANCEL,\n pointerout: INPUT_CANCEL\n};\n\n// in IE10 the pointer types is defined as an enum\nvar IE10_POINTER_TYPE_ENUM = {\n 2: INPUT_TYPE_TOUCH,\n 3: INPUT_TYPE_PEN,\n 4: INPUT_TYPE_MOUSE,\n 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816\n};\n\nvar POINTER_ELEMENT_EVENTS = 'pointerdown';\nvar POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';\n\n// IE10 has prefixed support, and case-sensitive\nif (window.MSPointerEvent && !window.PointerEvent) {\n POINTER_ELEMENT_EVENTS = 'MSPointerDown';\n POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';\n}\n\n/**\n * Pointer events input\n * @constructor\n * @extends Input\n */\nfunction PointerEventInput() {\n this.evEl = POINTER_ELEMENT_EVENTS;\n this.evWin = POINTER_WINDOW_EVENTS;\n\n Input.apply(this, arguments);\n\n this.store = (this.manager.session.pointerEvents = []);\n}\n\ninherit(PointerEventInput, Input, {\n /**\n * handle mouse events\n * @param {Object} ev\n */\n handler: function PEhandler(ev) {\n var store = this.store;\n var removePointer = false;\n\n var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');\n var eventType = POINTER_INPUT_MAP[eventTypeNormalized];\n var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;\n\n var isTouch = (pointerType == INPUT_TYPE_TOUCH);\n\n // get index of the event in the store\n var storeIndex = inArray(store, ev.pointerId, 'pointerId');\n\n // start and mouse must be down\n if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {\n if (storeIndex < 0) {\n store.push(ev);\n storeIndex = store.length - 1;\n }\n } else if (eventType & (INPUT_END | INPUT_CANCEL)) {\n removePointer = true;\n }\n\n // it not found, so the pointer hasn't been down (so it's probably a hover)\n if (storeIndex < 0) {\n return;\n }\n\n // update the event in the store\n store[storeIndex] = ev;\n\n this.callback(this.manager, eventType, {\n pointers: store,\n changedPointers: [ev],\n pointerType: pointerType,\n srcEvent: ev\n });\n\n if (removePointer) {\n // remove from the store\n store.splice(storeIndex, 1);\n }\n }\n});\n\nvar SINGLE_TOUCH_INPUT_MAP = {\n touchstart: INPUT_START,\n touchmove: INPUT_MOVE,\n touchend: INPUT_END,\n touchcancel: INPUT_CANCEL\n};\n\nvar SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';\nvar SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';\n\n/**\n * Touch events input\n * @constructor\n * @extends Input\n */\nfunction SingleTouchInput() {\n this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;\n this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;\n this.started = false;\n\n Input.apply(this, arguments);\n}\n\ninherit(SingleTouchInput, Input, {\n handler: function TEhandler(ev) {\n var type = SINGLE_TOUCH_INPUT_MAP[ev.type];\n\n // should we handle the touch events?\n if (type === INPUT_START) {\n this.started = true;\n }\n\n if (!this.started) {\n return;\n }\n\n var touches = normalizeSingleTouches.call(this, ev, type);\n\n // when done, reset the started state\n if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {\n this.started = false;\n }\n\n this.callback(this.manager, type, {\n pointers: touches[0],\n changedPointers: touches[1],\n pointerType: INPUT_TYPE_TOUCH,\n srcEvent: ev\n });\n }\n});\n\n/**\n * @this {TouchInput}\n * @param {Object} ev\n * @param {Number} type flag\n * @returns {undefined|Array} [all, changed]\n */\nfunction normalizeSingleTouches(ev, type) {\n var all = toArray(ev.touches);\n var changed = toArray(ev.changedTouches);\n\n if (type & (INPUT_END | INPUT_CANCEL)) {\n all = uniqueArray(all.concat(changed), 'identifier', true);\n }\n\n return [all, changed];\n}\n\nvar TOUCH_INPUT_MAP = {\n touchstart: INPUT_START,\n touchmove: INPUT_MOVE,\n touchend: INPUT_END,\n touchcancel: INPUT_CANCEL\n};\n\nvar TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';\n\n/**\n * Multi-user touch events input\n * @constructor\n * @extends Input\n */\nfunction TouchInput() {\n this.evTarget = TOUCH_TARGET_EVENTS;\n this.targetIds = {};\n\n Input.apply(this, arguments);\n}\n\ninherit(TouchInput, Input, {\n handler: function MTEhandler(ev) {\n var type = TOUCH_INPUT_MAP[ev.type];\n var touches = getTouches.call(this, ev, type);\n if (!touches) {\n return;\n }\n\n this.callback(this.manager, type, {\n pointers: touches[0],\n changedPointers: touches[1],\n pointerType: INPUT_TYPE_TOUCH,\n srcEvent: ev\n });\n }\n});\n\n/**\n * @this {TouchInput}\n * @param {Object} ev\n * @param {Number} type flag\n * @returns {undefined|Array} [all, changed]\n */\nfunction getTouches(ev, type) {\n var allTouches = toArray(ev.touches);\n var targetIds = this.targetIds;\n\n // when there is only one touch, the process can be simplified\n if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {\n targetIds[allTouches[0].identifier] = true;\n return [allTouches, allTouches];\n }\n\n var i,\n targetTouches,\n changedTouches = toArray(ev.changedTouches),\n changedTargetTouches = [],\n target = this.target;\n\n // get target touches from touches\n targetTouches = allTouches.filter(function(touch) {\n return hasParent(touch.target, target);\n });\n\n // collect touches\n if (type === INPUT_START) {\n i = 0;\n while (i < targetTouches.length) {\n targetIds[targetTouches[i].identifier] = true;\n i++;\n }\n }\n\n // filter changed touches to only contain touches that exist in the collected target ids\n i = 0;\n while (i < changedTouches.length) {\n if (targetIds[changedTouches[i].identifier]) {\n changedTargetTouches.push(changedTouches[i]);\n }\n\n // cleanup removed touches\n if (type & (INPUT_END | INPUT_CANCEL)) {\n delete targetIds[changedTouches[i].identifier];\n }\n i++;\n }\n\n if (!changedTargetTouches.length) {\n return;\n }\n\n return [\n // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'\n uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),\n changedTargetTouches\n ];\n}\n\n/**\n * Combined touch and mouse input\n *\n * Touch has a higher priority then mouse, and while touching no mouse events are allowed.\n * This because touch devices also emit mouse events while doing a touch.\n *\n * @constructor\n * @extends Input\n */\n\nvar DEDUP_TIMEOUT = 2500;\nvar DEDUP_DISTANCE = 25;\n\nfunction TouchMouseInput() {\n Input.apply(this, arguments);\n\n var handler = bindFn(this.handler, this);\n this.touch = new TouchInput(this.manager, handler);\n this.mouse = new MouseInput(this.manager, handler);\n\n this.primaryTouch = null;\n this.lastTouches = [];\n}\n\ninherit(TouchMouseInput, Input, {\n /**\n * handle mouse and touch events\n * @param {Hammer} manager\n * @param {String} inputEvent\n * @param {Object} inputData\n */\n handler: function TMEhandler(manager, inputEvent, inputData) {\n var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),\n isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);\n\n if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {\n return;\n }\n\n // when we're in a touch event, record touches to de-dupe synthetic mouse event\n if (isTouch) {\n recordTouches.call(this, inputEvent, inputData);\n } else if (isMouse && isSyntheticEvent.call(this, inputData)) {\n return;\n }\n\n this.callback(manager, inputEvent, inputData);\n },\n\n /**\n * remove the event listeners\n */\n destroy: function destroy() {\n this.touch.destroy();\n this.mouse.destroy();\n }\n});\n\nfunction recordTouches(eventType, eventData) {\n if (eventType & INPUT_START) {\n this.primaryTouch = eventData.changedPointers[0].identifier;\n setLastTouch.call(this, eventData);\n } else if (eventType & (INPUT_END | INPUT_CANCEL)) {\n setLastTouch.call(this, eventData);\n }\n}\n\nfunction setLastTouch(eventData) {\n var touch = eventData.changedPointers[0];\n\n if (touch.identifier === this.primaryTouch) {\n var lastTouch = {x: touch.clientX, y: touch.clientY};\n this.lastTouches.push(lastTouch);\n var lts = this.lastTouches;\n var removeLastTouch = function() {\n var i = lts.indexOf(lastTouch);\n if (i > -1) {\n lts.splice(i, 1);\n }\n };\n setTimeout(removeLastTouch, DEDUP_TIMEOUT);\n }\n}\n\nfunction isSyntheticEvent(eventData) {\n var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;\n for (var i = 0; i < this.lastTouches.length; i++) {\n var t = this.lastTouches[i];\n var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);\n if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {\n return true;\n }\n }\n return false;\n}\n\nvar PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');\nvar NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;\n\n// magical touchAction value\nvar TOUCH_ACTION_COMPUTE = 'compute';\nvar TOUCH_ACTION_AUTO = 'auto';\nvar TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented\nvar TOUCH_ACTION_NONE = 'none';\nvar TOUCH_ACTION_PAN_X = 'pan-x';\nvar TOUCH_ACTION_PAN_Y = 'pan-y';\nvar TOUCH_ACTION_MAP = getTouchActionProps();\n\n/**\n * Touch Action\n * sets the touchAction property or uses the js alternative\n * @param {Manager} manager\n * @param {String} value\n * @constructor\n */\nfunction TouchAction(manager, value) {\n this.manager = manager;\n this.set(value);\n}\n\nTouchAction.prototype = {\n /**\n * set the touchAction value on the element or enable the polyfill\n * @param {String} value\n */\n set: function(value) {\n // find out the touch-action by the event handlers\n if (value == TOUCH_ACTION_COMPUTE) {\n value = this.compute();\n }\n\n if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {\n this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;\n }\n this.actions = value.toLowerCase().trim();\n },\n\n /**\n * just re-set the touchAction value\n */\n update: function() {\n this.set(this.manager.options.touchAction);\n },\n\n /**\n * compute the value for the touchAction property based on the recognizer's settings\n * @returns {String} value\n */\n compute: function() {\n var actions = [];\n each(this.manager.recognizers, function(recognizer) {\n if (boolOrFn(recognizer.options.enable, [recognizer])) {\n actions = actions.concat(recognizer.getTouchAction());\n }\n });\n return cleanTouchActions(actions.join(' '));\n },\n\n /**\n * this method is called on each input cycle and provides the preventing of the browser behavior\n * @param {Object} input\n */\n preventDefaults: function(input) {\n var srcEvent = input.srcEvent;\n var direction = input.offsetDirection;\n\n // if the touch action did prevented once this session\n if (this.manager.session.prevented) {\n srcEvent.preventDefault();\n return;\n }\n\n var actions = this.actions;\n var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];\n var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];\n var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];\n\n if (hasNone) {\n //do not prevent defaults if this is a tap gesture\n\n var isTapPointer = input.pointers.length === 1;\n var isTapMovement = input.distance < 2;\n var isTapTouchTime = input.deltaTime < 250;\n\n if (isTapPointer && isTapMovement && isTapTouchTime) {\n return;\n }\n }\n\n if (hasPanX && hasPanY) {\n // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent\n return;\n }\n\n if (hasNone ||\n (hasPanY && direction & DIRECTION_HORIZONTAL) ||\n (hasPanX && direction & DIRECTION_VERTICAL)) {\n return this.preventSrc(srcEvent);\n }\n },\n\n /**\n * call preventDefault to prevent the browser's default behavior (scrolling in most cases)\n * @param {Object} srcEvent\n */\n preventSrc: function(srcEvent) {\n this.manager.session.prevented = true;\n srcEvent.preventDefault();\n }\n};\n\n/**\n * when the touchActions are collected they are not a valid value, so we need to clean things up. *\n * @param {String} actions\n * @returns {*}\n */\nfunction cleanTouchActions(actions) {\n // none\n if (inStr(actions, TOUCH_ACTION_NONE)) {\n return TOUCH_ACTION_NONE;\n }\n\n var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);\n var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);\n\n // if both pan-x and pan-y are set (different recognizers\n // for different directions, e.g. horizontal pan but vertical swipe?)\n // we need none (as otherwise with pan-x pan-y combined none of these\n // recognizers will work, since the browser would handle all panning\n if (hasPanX && hasPanY) {\n return TOUCH_ACTION_NONE;\n }\n\n // pan-x OR pan-y\n if (hasPanX || hasPanY) {\n return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;\n }\n\n // manipulation\n if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {\n return TOUCH_ACTION_MANIPULATION;\n }\n\n return TOUCH_ACTION_AUTO;\n}\n\nfunction getTouchActionProps() {\n if (!NATIVE_TOUCH_ACTION) {\n return false;\n }\n var touchMap = {};\n var cssSupports = window.CSS && window.CSS.supports;\n ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {\n\n // If css.supports is not supported but there is native touch-action assume it supports\n // all values. This is the case for IE 10 and 11.\n touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;\n });\n return touchMap;\n}\n\n/**\n * Recognizer flow explained; *\n * All recognizers have the initial state of POSSIBLE when a input session starts.\n * The definition of a input session is from the first input until the last input, with all it's movement in it. *\n * Example session for mouse-input: mousedown -> mousemove -> mouseup\n *\n * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed\n * which determines with state it should be.\n *\n * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to\n * POSSIBLE to give it another change on the next cycle.\n *\n * Possible\n * |\n * +-----+---------------+\n * | |\n * +-----+-----+ |\n * | | |\n * Failed Cancelled |\n * +-------+------+\n * | |\n * Recognized Began\n * |\n * Changed\n * |\n * Ended/Recognized\n */\nvar STATE_POSSIBLE = 1;\nvar STATE_BEGAN = 2;\nvar STATE_CHANGED = 4;\nvar STATE_ENDED = 8;\nvar STATE_RECOGNIZED = STATE_ENDED;\nvar STATE_CANCELLED = 16;\nvar STATE_FAILED = 32;\n\n/**\n * Recognizer\n * Every recognizer needs to extend from this class.\n * @constructor\n * @param {Object} options\n */\nfunction Recognizer(options) {\n this.options = assign({}, this.defaults, options || {});\n\n this.id = uniqueId();\n\n this.manager = null;\n\n // default is enable true\n this.options.enable = ifUndefined(this.options.enable, true);\n\n this.state = STATE_POSSIBLE;\n\n this.simultaneous = {};\n this.requireFail = [];\n}\n\nRecognizer.prototype = {\n /**\n * @virtual\n * @type {Object}\n */\n defaults: {},\n\n /**\n * set options\n * @param {Object} options\n * @return {Recognizer}\n */\n set: function(options) {\n assign(this.options, options);\n\n // also update the touchAction, in case something changed about the directions/enabled state\n this.manager && this.manager.touchAction.update();\n return this;\n },\n\n /**\n * recognize simultaneous with an other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n recognizeWith: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {\n return this;\n }\n\n var simultaneous = this.simultaneous;\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n if (!simultaneous[otherRecognizer.id]) {\n simultaneous[otherRecognizer.id] = otherRecognizer;\n otherRecognizer.recognizeWith(this);\n }\n return this;\n },\n\n /**\n * drop the simultaneous link. it doesnt remove the link on the other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n dropRecognizeWith: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {\n return this;\n }\n\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n delete this.simultaneous[otherRecognizer.id];\n return this;\n },\n\n /**\n * recognizer can only run when an other is failing\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n requireFailure: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {\n return this;\n }\n\n var requireFail = this.requireFail;\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n if (inArray(requireFail, otherRecognizer) === -1) {\n requireFail.push(otherRecognizer);\n otherRecognizer.requireFailure(this);\n }\n return this;\n },\n\n /**\n * drop the requireFailure link. it does not remove the link on the other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n dropRequireFailure: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {\n return this;\n }\n\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n var index = inArray(this.requireFail, otherRecognizer);\n if (index > -1) {\n this.requireFail.splice(index, 1);\n }\n return this;\n },\n\n /**\n * has require failures boolean\n * @returns {boolean}\n */\n hasRequireFailures: function() {\n return this.requireFail.length > 0;\n },\n\n /**\n * if the recognizer can recognize simultaneous with an other recognizer\n * @param {Recognizer} otherRecognizer\n * @returns {Boolean}\n */\n canRecognizeWith: function(otherRecognizer) {\n return !!this.simultaneous[otherRecognizer.id];\n },\n\n /**\n * You should use `tryEmit` instead of `emit` directly to check\n * that all the needed recognizers has failed before emitting.\n * @param {Object} input\n */\n emit: function(input) {\n var self = this;\n var state = this.state;\n\n function emit(event) {\n self.manager.emit(event, input);\n }\n\n // 'panstart' and 'panmove'\n if (state < STATE_ENDED) {\n emit(self.options.event + stateStr(state));\n }\n\n emit(self.options.event); // simple 'eventName' events\n\n if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)\n emit(input.additionalEvent);\n }\n\n // panend and pancancel\n if (state >= STATE_ENDED) {\n emit(self.options.event + stateStr(state));\n }\n },\n\n /**\n * Check that all the require failure recognizers has failed,\n * if true, it emits a gesture event,\n * otherwise, setup the state to FAILED.\n * @param {Object} input\n */\n tryEmit: function(input) {\n if (this.canEmit()) {\n return this.emit(input);\n }\n // it's failing anyway\n this.state = STATE_FAILED;\n },\n\n /**\n * can we emit?\n * @returns {boolean}\n */\n canEmit: function() {\n var i = 0;\n while (i < this.requireFail.length) {\n if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {\n return false;\n }\n i++;\n }\n return true;\n },\n\n /**\n * update the recognizer\n * @param {Object} inputData\n */\n recognize: function(inputData) {\n // make a new copy of the inputData\n // so we can change the inputData without messing up the other recognizers\n var inputDataClone = assign({}, inputData);\n\n // is is enabled and allow recognizing?\n if (!boolOrFn(this.options.enable, [this, inputDataClone])) {\n this.reset();\n this.state = STATE_FAILED;\n return;\n }\n\n // reset when we've reached the end\n if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {\n this.state = STATE_POSSIBLE;\n }\n\n this.state = this.process(inputDataClone);\n\n // the recognizer has recognized a gesture\n // so trigger an event\n if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {\n this.tryEmit(inputDataClone);\n }\n },\n\n /**\n * return the state of the recognizer\n * the actual recognizing happens in this method\n * @virtual\n * @param {Object} inputData\n * @returns {Const} STATE\n */\n process: function(inputData) { }, // jshint ignore:line\n\n /**\n * return the preferred touch-action\n * @virtual\n * @returns {Array}\n */\n getTouchAction: function() { },\n\n /**\n * called when the gesture isn't allowed to recognize\n * like when another is being recognized or it is disabled\n * @virtual\n */\n reset: function() { }\n};\n\n/**\n * get a usable string, used as event postfix\n * @param {Const} state\n * @returns {String} state\n */\nfunction stateStr(state) {\n if (state & STATE_CANCELLED) {\n return 'cancel';\n } else if (state & STATE_ENDED) {\n return 'end';\n } else if (state & STATE_CHANGED) {\n return 'move';\n } else if (state & STATE_BEGAN) {\n return 'start';\n }\n return '';\n}\n\n/**\n * direction cons to string\n * @param {Const} direction\n * @returns {String}\n */\nfunction directionStr(direction) {\n if (direction == DIRECTION_DOWN) {\n return 'down';\n } else if (direction == DIRECTION_UP) {\n return 'up';\n } else if (direction == DIRECTION_LEFT) {\n return 'left';\n } else if (direction == DIRECTION_RIGHT) {\n return 'right';\n }\n return '';\n}\n\n/**\n * get a recognizer by name if it is bound to a manager\n * @param {Recognizer|String} otherRecognizer\n * @param {Recognizer} recognizer\n * @returns {Recognizer}\n */\nfunction getRecognizerByNameIfManager(otherRecognizer, recognizer) {\n var manager = recognizer.manager;\n if (manager) {\n return manager.get(otherRecognizer);\n }\n return otherRecognizer;\n}\n\n/**\n * This recognizer is just used as a base for the simple attribute recognizers.\n * @constructor\n * @extends Recognizer\n */\nfunction AttrRecognizer() {\n Recognizer.apply(this, arguments);\n}\n\ninherit(AttrRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof AttrRecognizer\n */\n defaults: {\n /**\n * @type {Number}\n * @default 1\n */\n pointers: 1\n },\n\n /**\n * Used to check if it the recognizer receives valid input, like input.distance > 10.\n * @memberof AttrRecognizer\n * @param {Object} input\n * @returns {Boolean} recognized\n */\n attrTest: function(input) {\n var optionPointers = this.options.pointers;\n return optionPointers === 0 || input.pointers.length === optionPointers;\n },\n\n /**\n * Process the input and return the state for the recognizer\n * @memberof AttrRecognizer\n * @param {Object} input\n * @returns {*} State\n */\n process: function(input) {\n var state = this.state;\n var eventType = input.eventType;\n\n var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);\n var isValid = this.attrTest(input);\n\n // on cancel input and we've recognized before, return STATE_CANCELLED\n if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {\n return state | STATE_CANCELLED;\n } else if (isRecognized || isValid) {\n if (eventType & INPUT_END) {\n return state | STATE_ENDED;\n } else if (!(state & STATE_BEGAN)) {\n return STATE_BEGAN;\n }\n return state | STATE_CHANGED;\n }\n return STATE_FAILED;\n }\n});\n\n/**\n * Pan\n * Recognized when the pointer is down and moved in the allowed direction.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction PanRecognizer() {\n AttrRecognizer.apply(this, arguments);\n\n this.pX = null;\n this.pY = null;\n}\n\ninherit(PanRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof PanRecognizer\n */\n defaults: {\n event: 'pan',\n threshold: 10,\n pointers: 1,\n direction: DIRECTION_ALL\n },\n\n getTouchAction: function() {\n var direction = this.options.direction;\n var actions = [];\n if (direction & DIRECTION_HORIZONTAL) {\n actions.push(TOUCH_ACTION_PAN_Y);\n }\n if (direction & DIRECTION_VERTICAL) {\n actions.push(TOUCH_ACTION_PAN_X);\n }\n return actions;\n },\n\n directionTest: function(input) {\n var options = this.options;\n var hasMoved = true;\n var distance = input.distance;\n var direction = input.direction;\n var x = input.deltaX;\n var y = input.deltaY;\n\n // lock to axis?\n if (!(direction & options.direction)) {\n if (options.direction & DIRECTION_HORIZONTAL) {\n direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;\n hasMoved = x != this.pX;\n distance = Math.abs(input.deltaX);\n } else {\n direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;\n hasMoved = y != this.pY;\n distance = Math.abs(input.deltaY);\n }\n }\n input.direction = direction;\n return hasMoved && distance > options.threshold && direction & options.direction;\n },\n\n attrTest: function(input) {\n return AttrRecognizer.prototype.attrTest.call(this, input) &&\n (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));\n },\n\n emit: function(input) {\n\n this.pX = input.deltaX;\n this.pY = input.deltaY;\n\n var direction = directionStr(input.direction);\n\n if (direction) {\n input.additionalEvent = this.options.event + direction;\n }\n this._super.emit.call(this, input);\n }\n});\n\n/**\n * Pinch\n * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).\n * @constructor\n * @extends AttrRecognizer\n */\nfunction PinchRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(PinchRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof PinchRecognizer\n */\n defaults: {\n event: 'pinch',\n threshold: 0,\n pointers: 2\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_NONE];\n },\n\n attrTest: function(input) {\n return this._super.attrTest.call(this, input) &&\n (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);\n },\n\n emit: function(input) {\n if (input.scale !== 1) {\n var inOut = input.scale < 1 ? 'in' : 'out';\n input.additionalEvent = this.options.event + inOut;\n }\n this._super.emit.call(this, input);\n }\n});\n\n/**\n * Press\n * Recognized when the pointer is down for x ms without any movement.\n * @constructor\n * @extends Recognizer\n */\nfunction PressRecognizer() {\n Recognizer.apply(this, arguments);\n\n this._timer = null;\n this._input = null;\n}\n\ninherit(PressRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof PressRecognizer\n */\n defaults: {\n event: 'press',\n pointers: 1,\n time: 251, // minimal time of the pointer to be pressed\n threshold: 9 // a minimal movement is ok, but keep it low\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_AUTO];\n },\n\n process: function(input) {\n var options = this.options;\n var validPointers = input.pointers.length === options.pointers;\n var validMovement = input.distance < options.threshold;\n var validTime = input.deltaTime > options.time;\n\n this._input = input;\n\n // we only allow little movement\n // and we've reached an end event, so a tap is possible\n if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {\n this.reset();\n } else if (input.eventType & INPUT_START) {\n this.reset();\n this._timer = setTimeoutContext(function() {\n this.state = STATE_RECOGNIZED;\n this.tryEmit();\n }, options.time, this);\n } else if (input.eventType & INPUT_END) {\n return STATE_RECOGNIZED;\n }\n return STATE_FAILED;\n },\n\n reset: function() {\n clearTimeout(this._timer);\n },\n\n emit: function(input) {\n if (this.state !== STATE_RECOGNIZED) {\n return;\n }\n\n if (input && (input.eventType & INPUT_END)) {\n this.manager.emit(this.options.event + 'up', input);\n } else {\n this._input.timeStamp = now();\n this.manager.emit(this.options.event, this._input);\n }\n }\n});\n\n/**\n * Rotate\n * Recognized when two or more pointer are moving in a circular motion.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction RotateRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(RotateRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof RotateRecognizer\n */\n defaults: {\n event: 'rotate',\n threshold: 0,\n pointers: 2\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_NONE];\n },\n\n attrTest: function(input) {\n return this._super.attrTest.call(this, input) &&\n (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);\n }\n});\n\n/**\n * Swipe\n * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction SwipeRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(SwipeRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof SwipeRecognizer\n */\n defaults: {\n event: 'swipe',\n threshold: 10,\n velocity: 0.3,\n direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,\n pointers: 1\n },\n\n getTouchAction: function() {\n return PanRecognizer.prototype.getTouchAction.call(this);\n },\n\n attrTest: function(input) {\n var direction = this.options.direction;\n var velocity;\n\n if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {\n velocity = input.overallVelocity;\n } else if (direction & DIRECTION_HORIZONTAL) {\n velocity = input.overallVelocityX;\n } else if (direction & DIRECTION_VERTICAL) {\n velocity = input.overallVelocityY;\n }\n\n return this._super.attrTest.call(this, input) &&\n direction & input.offsetDirection &&\n input.distance > this.options.threshold &&\n input.maxPointers == this.options.pointers &&\n abs(velocity) > this.options.velocity && input.eventType & INPUT_END;\n },\n\n emit: function(input) {\n var direction = directionStr(input.offsetDirection);\n if (direction) {\n this.manager.emit(this.options.event + direction, input);\n }\n\n this.manager.emit(this.options.event, input);\n }\n});\n\n/**\n * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur\n * between the given interval and position. The delay option can be used to recognize multi-taps without firing\n * a single tap.\n *\n * The eventData from the emitted event contains the property `tapCount`, which contains the amount of\n * multi-taps being recognized.\n * @constructor\n * @extends Recognizer\n */\nfunction TapRecognizer() {\n Recognizer.apply(this, arguments);\n\n // previous time and center,\n // used for tap counting\n this.pTime = false;\n this.pCenter = false;\n\n this._timer = null;\n this._input = null;\n this.count = 0;\n}\n\ninherit(TapRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof PinchRecognizer\n */\n defaults: {\n event: 'tap',\n pointers: 1,\n taps: 1,\n interval: 300, // max time between the multi-tap taps\n time: 250, // max time of the pointer to be down (like finger on the screen)\n threshold: 9, // a minimal movement is ok, but keep it low\n posThreshold: 10 // a multi-tap can be a bit off the initial position\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_MANIPULATION];\n },\n\n process: function(input) {\n var options = this.options;\n\n var validPointers = input.pointers.length === options.pointers;\n var validMovement = input.distance < options.threshold;\n var validTouchTime = input.deltaTime < options.time;\n\n this.reset();\n\n if ((input.eventType & INPUT_START) && (this.count === 0)) {\n return this.failTimeout();\n }\n\n // we only allow little movement\n // and we've reached an end event, so a tap is possible\n if (validMovement && validTouchTime && validPointers) {\n if (input.eventType != INPUT_END) {\n return this.failTimeout();\n }\n\n var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;\n var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;\n\n this.pTime = input.timeStamp;\n this.pCenter = input.center;\n\n if (!validMultiTap || !validInterval) {\n this.count = 1;\n } else {\n this.count += 1;\n }\n\n this._input = input;\n\n // if tap count matches we have recognized it,\n // else it has began recognizing...\n var tapCount = this.count % options.taps;\n if (tapCount === 0) {\n // no failing requirements, immediately trigger the tap event\n // or wait as long as the multitap interval to trigger\n if (!this.hasRequireFailures()) {\n return STATE_RECOGNIZED;\n } else {\n this._timer = setTimeoutContext(function() {\n this.state = STATE_RECOGNIZED;\n this.tryEmit();\n }, options.interval, this);\n return STATE_BEGAN;\n }\n }\n }\n return STATE_FAILED;\n },\n\n failTimeout: function() {\n this._timer = setTimeoutContext(function() {\n this.state = STATE_FAILED;\n }, this.options.interval, this);\n return STATE_FAILED;\n },\n\n reset: function() {\n clearTimeout(this._timer);\n },\n\n emit: function() {\n if (this.state == STATE_RECOGNIZED) {\n this._input.tapCount = this.count;\n this.manager.emit(this.options.event, this._input);\n }\n }\n});\n\n/**\n * Simple way to create a manager with a default set of recognizers.\n * @param {HTMLElement} element\n * @param {Object} [options]\n * @constructor\n */\nfunction Hammer(element, options) {\n options = options || {};\n options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);\n return new Manager(element, options);\n}\n\n/**\n * @const {string}\n */\nHammer.VERSION = '2.0.7';\n\n/**\n * default settings\n * @namespace\n */\nHammer.defaults = {\n /**\n * set if DOM events are being triggered.\n * But this is slower and unused by simple implementations, so disabled by default.\n * @type {Boolean}\n * @default false\n */\n domEvents: false,\n\n /**\n * The value for the touchAction property/fallback.\n * When set to `compute` it will magically set the correct value based on the added recognizers.\n * @type {String}\n * @default compute\n */\n touchAction: TOUCH_ACTION_COMPUTE,\n\n /**\n * @type {Boolean}\n * @default true\n */\n enable: true,\n\n /**\n * EXPERIMENTAL FEATURE -- can be removed/changed\n * Change the parent input target element.\n * If Null, then it is being set the to main element.\n * @type {Null|EventTarget}\n * @default null\n */\n inputTarget: null,\n\n /**\n * force an input class\n * @type {Null|Function}\n * @default null\n */\n inputClass: null,\n\n /**\n * Default recognizer setup when calling `Hammer()`\n * When creating a new Manager these will be skipped.\n * @type {Array}\n */\n preset: [\n // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]\n [RotateRecognizer, {enable: false}],\n [PinchRecognizer, {enable: false}, ['rotate']],\n [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],\n [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],\n [TapRecognizer],\n [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],\n [PressRecognizer]\n ],\n\n /**\n * Some CSS properties can be used to improve the working of Hammer.\n * Add them to this method and they will be set when creating a new Manager.\n * @namespace\n */\n cssProps: {\n /**\n * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.\n * @type {String}\n * @default 'none'\n */\n userSelect: 'none',\n\n /**\n * Disable the Windows Phone grippers when pressing an element.\n * @type {String}\n * @default 'none'\n */\n touchSelect: 'none',\n\n /**\n * Disables the default callout shown when you touch and hold a touch target.\n * On iOS, when you touch and hold a touch target such as a link, Safari displays\n * a callout containing information about the link. This property allows you to disable that callout.\n * @type {String}\n * @default 'none'\n */\n touchCallout: 'none',\n\n /**\n * Specifies whether zooming is enabled. Used by IE10>\n * @type {String}\n * @default 'none'\n */\n contentZooming: 'none',\n\n /**\n * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.\n * @type {String}\n * @default 'none'\n */\n userDrag: 'none',\n\n /**\n * Overrides the highlight color shown when the user taps a link or a JavaScript\n * clickable element in iOS. This property obeys the alpha value, if specified.\n * @type {String}\n * @default 'rgba(0,0,0,0)'\n */\n tapHighlightColor: 'rgba(0,0,0,0)'\n }\n};\n\nvar STOP = 1;\nvar FORCED_STOP = 2;\n\n/**\n * Manager\n * @param {HTMLElement} element\n * @param {Object} [options]\n * @constructor\n */\nfunction Manager(element, options) {\n this.options = assign({}, Hammer.defaults, options || {});\n\n this.options.inputTarget = this.options.inputTarget || element;\n\n this.handlers = {};\n this.session = {};\n this.recognizers = [];\n this.oldCssProps = {};\n\n this.element = element;\n this.input = createInputInstance(this);\n this.touchAction = new TouchAction(this, this.options.touchAction);\n\n toggleCssProps(this, true);\n\n each(this.options.recognizers, function(item) {\n var recognizer = this.add(new (item[0])(item[1]));\n item[2] && recognizer.recognizeWith(item[2]);\n item[3] && recognizer.requireFailure(item[3]);\n }, this);\n}\n\nManager.prototype = {\n /**\n * set options\n * @param {Object} options\n * @returns {Manager}\n */\n set: function(options) {\n assign(this.options, options);\n\n // Options that need a little more setup\n if (options.touchAction) {\n this.touchAction.update();\n }\n if (options.inputTarget) {\n // Clean up existing event listeners and reinitialize\n this.input.destroy();\n this.input.target = options.inputTarget;\n this.input.init();\n }\n return this;\n },\n\n /**\n * stop recognizing for this session.\n * This session will be discarded, when a new [input]start event is fired.\n * When forced, the recognizer cycle is stopped immediately.\n * @param {Boolean} [force]\n */\n stop: function(force) {\n this.session.stopped = force ? FORCED_STOP : STOP;\n },\n\n /**\n * run the recognizers!\n * called by the inputHandler function on every movement of the pointers (touches)\n * it walks through all the recognizers and tries to detect the gesture that is being made\n * @param {Object} inputData\n */\n recognize: function(inputData) {\n var session = this.session;\n if (session.stopped) {\n return;\n }\n\n // run the touch-action polyfill\n this.touchAction.preventDefaults(inputData);\n\n var recognizer;\n var recognizers = this.recognizers;\n\n // this holds the recognizer that is being recognized.\n // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED\n // if no recognizer is detecting a thing, it is set to `null`\n var curRecognizer = session.curRecognizer;\n\n // reset when the last recognizer is recognized\n // or when we're in a new session\n if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {\n curRecognizer = session.curRecognizer = null;\n }\n\n var i = 0;\n while (i < recognizers.length) {\n recognizer = recognizers[i];\n\n // find out if we are allowed try to recognize the input for this one.\n // 1. allow if the session is NOT forced stopped (see the .stop() method)\n // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one\n // that is being recognized.\n // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.\n // this can be setup with the `recognizeWith()` method on the recognizer.\n if (session.stopped !== FORCED_STOP && ( // 1\n !curRecognizer || recognizer == curRecognizer || // 2\n recognizer.canRecognizeWith(curRecognizer))) { // 3\n recognizer.recognize(inputData);\n } else {\n recognizer.reset();\n }\n\n // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the\n // current active recognizer. but only if we don't already have an active recognizer\n if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {\n curRecognizer = session.curRecognizer = recognizer;\n }\n i++;\n }\n },\n\n /**\n * get a recognizer by its event name.\n * @param {Recognizer|String} recognizer\n * @returns {Recognizer|Null}\n */\n get: function(recognizer) {\n if (recognizer instanceof Recognizer) {\n return recognizer;\n }\n\n var recognizers = this.recognizers;\n for (var i = 0; i < recognizers.length; i++) {\n if (recognizers[i].options.event == recognizer) {\n return recognizers[i];\n }\n }\n return null;\n },\n\n /**\n * add a recognizer to the manager\n * existing recognizers with the same event name will be removed\n * @param {Recognizer} recognizer\n * @returns {Recognizer|Manager}\n */\n add: function(recognizer) {\n if (invokeArrayArg(recognizer, 'add', this)) {\n return this;\n }\n\n // remove existing\n var existing = this.get(recognizer.options.event);\n if (existing) {\n this.remove(existing);\n }\n\n this.recognizers.push(recognizer);\n recognizer.manager = this;\n\n this.touchAction.update();\n return recognizer;\n },\n\n /**\n * remove a recognizer by name or instance\n * @param {Recognizer|String} recognizer\n * @returns {Manager}\n */\n remove: function(recognizer) {\n if (invokeArrayArg(recognizer, 'remove', this)) {\n return this;\n }\n\n recognizer = this.get(recognizer);\n\n // let's make sure this recognizer exists\n if (recognizer) {\n var recognizers = this.recognizers;\n var index = inArray(recognizers, recognizer);\n\n if (index !== -1) {\n recognizers.splice(index, 1);\n this.touchAction.update();\n }\n }\n\n return this;\n },\n\n /**\n * bind event\n * @param {String} events\n * @param {Function} handler\n * @returns {EventEmitter} this\n */\n on: function(events, handler) {\n if (events === undefined) {\n return;\n }\n if (handler === undefined) {\n return;\n }\n\n var handlers = this.handlers;\n each(splitStr(events), function(event) {\n handlers[event] = handlers[event] || [];\n handlers[event].push(handler);\n });\n return this;\n },\n\n /**\n * unbind event, leave emit blank to remove all handlers\n * @param {String} events\n * @param {Function} [handler]\n * @returns {EventEmitter} this\n */\n off: function(events, handler) {\n if (events === undefined) {\n return;\n }\n\n var handlers = this.handlers;\n each(splitStr(events), function(event) {\n if (!handler) {\n delete handlers[event];\n } else {\n handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);\n }\n });\n return this;\n },\n\n /**\n * emit event to the listeners\n * @param {String} event\n * @param {Object} data\n */\n emit: function(event, data) {\n // we also want to trigger dom events\n if (this.options.domEvents) {\n triggerDomEvent(event, data);\n }\n\n // no handlers, so skip it all\n var handlers = this.handlers[event] && this.handlers[event].slice();\n if (!handlers || !handlers.length) {\n return;\n }\n\n data.type = event;\n data.preventDefault = function() {\n data.srcEvent.preventDefault();\n };\n\n var i = 0;\n while (i < handlers.length) {\n handlers[i](data);\n i++;\n }\n },\n\n /**\n * destroy the manager and unbinds all events\n * it doesn't unbind dom events, that is the user own responsibility\n */\n destroy: function() {\n this.element && toggleCssProps(this, false);\n\n this.handlers = {};\n this.session = {};\n this.input.destroy();\n this.element = null;\n }\n};\n\n/**\n * add/remove the css properties as defined in manager.options.cssProps\n * @param {Manager} manager\n * @param {Boolean} add\n */\nfunction toggleCssProps(manager, add) {\n var element = manager.element;\n if (!element.style) {\n return;\n }\n var prop;\n each(manager.options.cssProps, function(value, name) {\n prop = prefixed(element.style, name);\n if (add) {\n manager.oldCssProps[prop] = element.style[prop];\n element.style[prop] = value;\n } else {\n element.style[prop] = manager.oldCssProps[prop] || '';\n }\n });\n if (!add) {\n manager.oldCssProps = {};\n }\n}\n\n/**\n * trigger dom event\n * @param {String} event\n * @param {Object} data\n */\nfunction triggerDomEvent(event, data) {\n var gestureEvent = document.createEvent('Event');\n gestureEvent.initEvent(event, true, true);\n gestureEvent.gesture = data;\n data.target.dispatchEvent(gestureEvent);\n}\n\nassign(Hammer, {\n INPUT_START: INPUT_START,\n INPUT_MOVE: INPUT_MOVE,\n INPUT_END: INPUT_END,\n INPUT_CANCEL: INPUT_CANCEL,\n\n STATE_POSSIBLE: STATE_POSSIBLE,\n STATE_BEGAN: STATE_BEGAN,\n STATE_CHANGED: STATE_CHANGED,\n STATE_ENDED: STATE_ENDED,\n STATE_RECOGNIZED: STATE_RECOGNIZED,\n STATE_CANCELLED: STATE_CANCELLED,\n STATE_FAILED: STATE_FAILED,\n\n DIRECTION_NONE: DIRECTION_NONE,\n DIRECTION_LEFT: DIRECTION_LEFT,\n DIRECTION_RIGHT: DIRECTION_RIGHT,\n DIRECTION_UP: DIRECTION_UP,\n DIRECTION_DOWN: DIRECTION_DOWN,\n DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,\n DIRECTION_VERTICAL: DIRECTION_VERTICAL,\n DIRECTION_ALL: DIRECTION_ALL,\n\n Manager: Manager,\n Input: Input,\n TouchAction: TouchAction,\n\n TouchInput: TouchInput,\n MouseInput: MouseInput,\n PointerEventInput: PointerEventInput,\n TouchMouseInput: TouchMouseInput,\n SingleTouchInput: SingleTouchInput,\n\n Recognizer: Recognizer,\n AttrRecognizer: AttrRecognizer,\n Tap: TapRecognizer,\n Pan: PanRecognizer,\n Swipe: SwipeRecognizer,\n Pinch: PinchRecognizer,\n Rotate: RotateRecognizer,\n Press: PressRecognizer,\n\n on: addEventListeners,\n off: removeEventListeners,\n each: each,\n merge: merge,\n extend: extend,\n assign: assign,\n inherit: inherit,\n bindFn: bindFn,\n prefixed: prefixed\n});\n\n// this prevents errors when Hammer is loaded in the presence of an AMD\n// style loader but by script tag, not by the loader.\nvar freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line\nfreeGlobal.Hammer = Hammer;\n\nif (typeof define === 'function' && define.amd) {\n define(function() {\n return Hammer;\n });\n} else if (typeof module != 'undefined' && module.exports) {\n module.exports = Hammer;\n} else {\n window[exportName] = Hammer;\n}\n\n})(window, document, 'Hammer');\n","import { LabeledButton } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * @typedef {import('../util/buckets').Bucket} Bucket\n * @typedef {import(\"preact\").ComponentChildren} Children\n */\n\n/**\n * Render a set of buckets in a vertical channel positioned along the edge of\n * the sidebar.\n *\n * @param {object} props\n * @param {Children} props.children\n */\nfunction BucketList({ children }) {\n return (\n <ul\n className={classnames(\n // 2020-11-20: Making bucket bar one pixel wider (23px vs 22px) is an\n // interim and pragmatic solution for an apparent glitch on\n // Safari and Chrome. Adding one pixel resolves this issue:\n // https://github.com/hypothesis/client/pull/2750\n 'absolute w-[23px] left-[-22px] h-full',\n // The background is set to low opacity when the sidebar is collapsed.\n 'bg-grey-2 sidebar-collapsed:bg-black/[.08]',\n // Disable pointer events along the sidebar itself; re-enable them in\n // bucket indicator buttons\n 'pointer-events-none'\n )}\n >\n {children}\n </ul>\n );\n}\n\n/**\n * Render a vertically-positioned bucket-list item.\n *\n * @param {object} props\n * @param {Children} props.children\n * @param {number} props.topPosition - The vertical top position, in pixels,\n * for this bucket item relative to the top of the containing BucketList\n */\nfunction BucketItem({ children, topPosition }) {\n return (\n <li\n className={classnames(\n 'absolute right-0',\n // Re-enable pointer events, which are disabled on the containing list\n 'pointer-events-auto'\n )}\n style={{ top: topPosition }}\n >\n {children}\n </li>\n );\n}\n\n/**\n * A list of buckets, including up and down navigation (when applicable) and\n * on-screen buckets\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n *\n * @param {object} props\n * @param {Bucket} props.above\n * @param {Bucket} props.below\n * @param {Bucket[]} props.buckets\n * @param {(tags: string[]) => void} props.onFocusAnnotations\n * @param {(tags: string[], direction: 'down'|'up') => void} props.onScrollToClosestOffScreenAnchor\n * @param {(tags: string[], toggle: boolean) => void} props.onSelectAnnotations\n */\nexport default function Buckets({\n above,\n below,\n buckets,\n onFocusAnnotations,\n onScrollToClosestOffScreenAnchor,\n onSelectAnnotations,\n}) {\n const showUpNavigation = above.tags.size > 0;\n const showDownNavigation = below.tags.size > 0;\n\n return (\n <BucketList>\n {showUpNavigation && (\n <BucketItem topPosition={above.position}>\n <LabeledButton\n className={classnames(\n 'BucketButton UpBucketButton',\n // Center the button vertically at `above.position` by pulling\n // its top margin up by about half the button's height.\n // This puts it nearer the toolbar's other buttons above the\n // bucket list.\n 'right-0 mt-[-11px]'\n )}\n data-testid=\"up-navigation-button\"\n onClick={() =>\n onScrollToClosestOffScreenAnchor([...above.tags], 'up')\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...above.tags])}\n onMouseEnter={() => onFocusAnnotations([...above.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${above.tags.size})`}\n >\n {above.tags.size}\n </LabeledButton>\n </BucketItem>\n )}\n {buckets.map((bucket, index) => (\n <BucketItem topPosition={bucket.position} key={index}>\n <LabeledButton\n className={classnames(\n 'BucketButton LeftBucketButton',\n // Center the bucket indicator button vertically on `bucket.position`\n // by pulling it by half the height of the button\n 'right-0 mt-[-8px]'\n )}\n onClick={event =>\n onSelectAnnotations(\n [...bucket.tags],\n event.metaKey || event.ctrlKey\n )\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...bucket.tags])}\n onMouseEnter={() => onFocusAnnotations([...bucket.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Select nearby annotations (${bucket.tags.size})`}\n >\n {bucket.tags.size}\n </LabeledButton>\n </BucketItem>\n ))}\n {showDownNavigation && (\n <BucketItem topPosition={below.position}>\n <LabeledButton\n className=\"BucketButton DownBucketButton right-0\"\n data-testid=\"down-navigation-button\"\n onClick={() =>\n onScrollToClosestOffScreenAnchor([...below.tags], 'down')\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...below.tags])}\n onMouseEnter={() => onFocusAnnotations([...below.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${below.tags.size})`}\n >\n {below.tags.size}\n </LabeledButton>\n </BucketItem>\n )}\n </BucketList>\n );\n}\n","import { render } from 'preact';\n\nimport Buckets from './components/Buckets';\nimport { computeBuckets } from './util/buckets';\n\n/**\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Controller for the \"bucket bar\" shown alongside the sidebar indicating where\n * annotations are in the document.\n *\n * @implements {Destroyable}\n */\nexport class BucketBar {\n /**\n * @param {HTMLElement} container\n * @param {object} options\n * @param {(tags: string[]) => void} options.onFocusAnnotations\n * @param {(tags: string[], direction: 'down'|'up') => void} options.onScrollToClosestOffScreenAnchor\n * @param {(tags: string[], toggle: boolean) => void} options.onSelectAnnotations\n */\n constructor(\n container,\n {\n onFocusAnnotations,\n onScrollToClosestOffScreenAnchor,\n onSelectAnnotations,\n }\n ) {\n this._bucketsContainer = document.createElement('div');\n container.appendChild(this._bucketsContainer);\n\n this._onFocusAnnotations = onFocusAnnotations;\n this._onScrollToClosestOffScreenAnchor = onScrollToClosestOffScreenAnchor;\n this._onSelectAnnotations = onSelectAnnotations;\n\n // Immediately render the bucket bar\n this.update([]);\n }\n\n destroy() {\n render(null, this._bucketsContainer);\n this._bucketsContainer.remove();\n }\n\n /**\n * @param {AnchorPosition[]} positions\n */\n update(positions) {\n const buckets = computeBuckets(positions);\n render(\n <Buckets\n above={buckets.above}\n below={buckets.below}\n buckets={buckets.buckets}\n onFocusAnnotations={tags => this._onFocusAnnotations(tags)}\n onScrollToClosestOffScreenAnchor={(tags, direction) =>\n this._onScrollToClosestOffScreenAnchor(tags, direction)\n }\n onSelectAnnotations={(tags, toogle) =>\n this._onSelectAnnotations(tags, toogle)\n }\n />,\n this._bucketsContainer\n );\n }\n}\n","import { IconButton } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * @typedef {import(\"@hypothesis/frontend-shared/lib/components/buttons\").IconButtonProps} IconButtonProps\n *\n * @typedef {Omit<IconButtonProps, \"className\">} ToolbarButtonProps\n */\n\n/**\n * Style an IconButton for use on the Toolbar\n *\n * @param {ToolbarButtonProps} props\n */\nfunction ToolbarButton({ ...buttonProps }) {\n const { icon, title, ...restProps } = buttonProps;\n return (\n <IconButton\n className={classnames(\n 'w-[30px] h-[30px]', // These buttons have precise dimensions\n 'rounded-px', // size of border radius in absolute units\n 'flex items-center justify-center',\n 'border bg-white text-grey-6 hover:text-grey-9',\n 'shadow transition-colors'\n )}\n icon={icon}\n title={title}\n {...restProps}\n />\n );\n}\n\n/**\n * @typedef ToolbarProps\n *\n * @prop {() => void} closeSidebar -\n * Callback for the \"Close sidebar\" button. This button is only shown when\n * `useMinimalControls` is true and the sidebar is open.\n * @prop {() => void} createAnnotation -\n * Callback for the \"Create annotation\" / \"Create page note\" button. The type\n * of annotation depends on whether there is a text selection and is decided\n * by the caller.\n * @prop {boolean} isSidebarOpen - Is the sidebar currently visible?\n * @prop {'annotation'|'note'} newAnnotationType -\n * Icon to show on the \"Create annotation\" button indicating what kind of annotation\n * will be created.\n * @prop {boolean} showHighlights - Are highlights currently visible in the document?\n * @prop {() => void} toggleHighlights -\n * Callback to toggle visibility of highlights in the document.\n * @prop {() => void} toggleSidebar -\n * Callback to toggle the visibility of the sidebar.\n * @prop {import(\"preact\").Ref<HTMLButtonElement>} [toggleSidebarRef] -\n * Ref that gets set to the toolbar button for toggling the sidebar.\n * This is exposed to enable the drag-to-resize functionality of this\n * button.\n * @prop {boolean} [useMinimalControls] -\n * If true, all controls are hidden except for the \"Close sidebar\" button\n * when the sidebar is open. This is enabled in the \"clean\" theme.\n */\n\n/**\n * Controls on the edge of the sidebar for opening/closing the sidebar,\n * controlling highlight visibility and creating new page notes.\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n *\n * @param {ToolbarProps} props\n */\nexport default function Toolbar({\n closeSidebar,\n createAnnotation,\n isSidebarOpen,\n newAnnotationType,\n showHighlights,\n toggleHighlights,\n toggleSidebar,\n toggleSidebarRef,\n useMinimalControls = false,\n}) {\n return (\n <div\n className={classnames(\n 'absolute left-[-33px] w-[33px] z-2',\n 'text-px-base leading-none' // non-scaling sizing\n )}\n >\n {/* In the clean theme (`useMinimalControls` is `true`),\n the only button that should appear is a button\n to close the sidebar, and only if the sidebar is open. This button is\n absolutely positioned some way down the edge of the sidebar.\n */}\n {useMinimalControls && isSidebarOpen && (\n <IconButton\n className={classnames(\n 'w-[27px] h-[27px] mt-[140px] ml-px-1.5',\n 'flex items-center justify-center bg-white border',\n 'text-grey-6 hover:text-grey-9 transition-colors',\n // Turn off right border to blend with sidebar\n 'border-r-0',\n // A more intense shadow than other ToolbarButtons, to match that\n // of the edge of the sidebar in clean theme\n 'shadow-sidebar'\n )}\n title=\"Close annotation sidebar\"\n icon=\"cancel\"\n onClick={closeSidebar}\n />\n )}\n {!useMinimalControls && (\n <>\n <IconButton\n className={classnames(\n // Height and width to align with the sidebar's top bar\n 'h-[40px] w-[33px] pl-px-1.5',\n 'bg-white text-grey-5 hover:text-grey-9',\n // Turn on left and bottom borders to continue the\n // border of the sidebar's top bar\n 'border-l border-b'\n )}\n buttonRef={toggleSidebarRef}\n title=\"Annotation sidebar\"\n icon={isSidebarOpen ? 'caret-right' : 'caret-left'}\n expanded={isSidebarOpen}\n pressed={isSidebarOpen}\n onClick={toggleSidebar}\n />\n <div className=\"space-y-px-1.5 mt-px-2\">\n <ToolbarButton\n title=\"Show highlights\"\n icon={showHighlights ? 'show' : 'hide'}\n selected={showHighlights}\n onClick={toggleHighlights}\n />\n <ToolbarButton\n title={\n newAnnotationType === 'note'\n ? 'New page note'\n : 'New annotation'\n }\n icon={newAnnotationType === 'note' ? 'note' : 'annotate'}\n onClick={createAnnotation}\n />\n </div>\n </>\n )}\n </div>\n );\n}\n","import { createRef, render } from 'preact';\n\nimport Toolbar from './components/Toolbar';\n\n/**\n * @typedef ToolbarOptions\n * @prop {() => void} createAnnotation\n * @prop {(open: boolean) => void} setSidebarOpen\n * @prop {(visible: boolean) => void} setHighlightsVisible\n */\n\n/**\n * Controller for the toolbar on the edge of the sidebar.\n *\n * This toolbar provides controls for opening and closing the sidebar, toggling\n * highlight visibility etc.\n */\nexport class ToolbarController {\n /**\n * @param {HTMLElement} container - Element into which the toolbar is rendered\n * @param {ToolbarOptions} options\n */\n constructor(container, options) {\n const { createAnnotation, setSidebarOpen, setHighlightsVisible } = options;\n\n this._container = container;\n\n this._useMinimalControls = false;\n\n /** @type {'annotation'|'note'} */\n this._newAnnotationType = 'note';\n\n this._highlightsVisible = false;\n this._sidebarOpen = false;\n\n this._closeSidebar = () => setSidebarOpen(false);\n this._toggleSidebar = () => setSidebarOpen(!this._sidebarOpen);\n this._toggleHighlights = () =>\n setHighlightsVisible(!this._highlightsVisible);\n this._createAnnotation = () => {\n createAnnotation();\n setSidebarOpen(true);\n };\n\n /** Reference to the sidebar toggle button. */\n this._sidebarToggleButton = createRef();\n\n this.render();\n }\n\n getWidth() {\n const content = /** @type {HTMLElement} */ (this._container.firstChild);\n return content.getBoundingClientRect().width;\n }\n\n /**\n * Set whether the toolbar is in the \"minimal controls\" mode where\n * only the \"Close\" button is shown.\n */\n set useMinimalControls(minimal) {\n this._useMinimalControls = minimal;\n this.render();\n }\n\n get useMinimalControls() {\n return this._useMinimalControls;\n }\n\n /**\n * Update the toolbar to reflect whether the sidebar is open or not.\n */\n set sidebarOpen(open) {\n this._sidebarOpen = open;\n this.render();\n }\n\n get sidebarOpen() {\n return this._sidebarOpen;\n }\n\n /**\n * Update the toolbar to reflect whether the \"Create annotation\" button will\n * create a page note (if there is no selection) or an annotation (if there is\n * a selection).\n */\n set newAnnotationType(type) {\n this._newAnnotationType = type;\n this.render();\n }\n\n get newAnnotationType() {\n return this._newAnnotationType;\n }\n\n /**\n * Update the toolbar to reflect whether highlights are currently visible.\n */\n set highlightsVisible(visible) {\n this._highlightsVisible = visible;\n this.render();\n }\n\n get highlightsVisible() {\n return this._highlightsVisible;\n }\n\n /**\n * Return the DOM element that toggles the sidebar's visibility.\n *\n * @type {HTMLButtonElement}\n */\n get sidebarToggleButton() {\n return this._sidebarToggleButton.current;\n }\n\n render() {\n render(\n <Toolbar\n closeSidebar={this._closeSidebar}\n createAnnotation={this._createAnnotation}\n newAnnotationType={this._newAnnotationType}\n isSidebarOpen={this._sidebarOpen}\n showHighlights={this._highlightsVisible}\n toggleHighlights={this._toggleHighlights}\n toggleSidebar={this._toggleSidebar}\n toggleSidebarRef={this._sidebarToggleButton}\n useMinimalControls={this.useMinimalControls}\n />,\n this._container\n );\n }\n}\n","import * as Hammer from 'hammerjs';\n\nimport { addConfigFragment } from '../shared/config-fragment';\nimport { sendErrorsTo } from '../shared/frame-error-capture';\nimport { ListenerCollection } from '../shared/listener-collection';\nimport { PortRPC } from '../shared/messaging';\n\nimport { annotationCounts } from './annotation-counts';\nimport { BucketBar } from './bucket-bar';\nimport { createAppConfig } from './config/app';\nimport { FeatureFlags } from './features';\nimport { sidebarTrigger } from './sidebar-trigger';\nimport { ToolbarController } from './toolbar';\nimport { createShadowRoot } from './util/shadow-root';\n\n/**\n * @typedef {import('./guest').Guest} Guest\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/config').Service} Service\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').HostToSidebarEvent} HostToSidebarEvent\n * @typedef {import('../types/port-rpc-events').SidebarToHostEvent} SidebarToHostEvent\n */\n\n// Minimum width to which the iframeContainer can be resized.\nexport const MIN_RESIZE = 280;\n\n/**\n * Client configuration used to launch the sidebar application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n *\n * @typedef {{ sidebarAppUrl: string } & Record<string, unknown>} SidebarConfig\n */\n\n/**\n * Client configuration used by the sidebar container ({@link Sidebar}).\n *\n * @typedef SidebarContainerConfig\n * @prop {Service[]} [services] - Details of the annotation service the\n * client should connect to. This includes callbacks provided by the host\n * page to handle certain actions in the sidebar (eg. the Login button).\n * @prop {string} [externalContainerSelector] - CSS selector of a container\n * element in the host page which the sidebar should be added into, instead\n * of creating a new container.\n * @prop {(layout: SidebarLayout) => void} [onLayoutChange] - Callback that\n * allows the host page to react to the sidebar being opened, closed or\n * resized\n */\n\n/**\n * Create the iframe that will load the sidebar application.\n *\n * @param {SidebarConfig} config\n * @return {HTMLIFrameElement}\n */\nfunction createSidebarIframe(config) {\n const sidebarURL = /** @type {string} */ (config.sidebarAppUrl);\n const sidebarAppSrc = addConfigFragment(\n sidebarURL,\n createAppConfig(sidebarURL, config)\n );\n\n const sidebarFrame = document.createElement('iframe');\n\n // Enable media in annotations to be shown fullscreen\n sidebarFrame.setAttribute('allowfullscreen', '');\n\n sidebarFrame.src = sidebarAppSrc;\n sidebarFrame.title = 'Hypothesis annotation viewer';\n sidebarFrame.className = 'sidebar-frame';\n\n return sidebarFrame;\n}\n\n/**\n * The `Sidebar` class creates (1) the sidebar application iframe, (2) its container,\n * as well as (3) the adjacent controls.\n *\n * @implements {Destroyable}\n */\nexport class Sidebar {\n /**\n * @param {HTMLElement} element\n * @param {import('./util/emitter').EventBus} eventBus -\n * Enables communication between components sharing the same eventBus\n * @param {SidebarContainerConfig & SidebarConfig} config\n */\n constructor(element, eventBus, config) {\n this._emitter = eventBus.createEmitter();\n\n /**\n * Tracks which `Guest` has a text selection. `null` indicates to default\n * to the first connected guest frame.\n *\n * @type {PortRPC<GuestToHostEvent, HostToGuestEvent>|null}\n */\n this._guestWithSelection = null;\n\n /**\n * Channels for host-guest communication.\n *\n * @type {PortRPC<GuestToHostEvent, HostToGuestEvent>[]}\n */\n this._guestRPC = [];\n\n /**\n * Channel for host-sidebar communication.\n *\n * @type {PortRPC<SidebarToHostEvent, HostToSidebarEvent>}\n */\n this._sidebarRPC = new PortRPC();\n\n /**\n * The `<iframe>` element containing the sidebar application.\n */\n this.iframe = createSidebarIframe(config);\n\n this._config = config;\n\n /** @type {BucketBar|null} */\n this.bucketBar = null;\n\n this.features = new FeatureFlags();\n\n if (config.externalContainerSelector) {\n this.externalFrame =\n /** @type {HTMLElement} */\n (document.querySelector(config.externalContainerSelector)) ?? element;\n this.externalFrame.appendChild(this.iframe);\n } else {\n this.iframeContainer = document.createElement('div');\n this.iframeContainer.style.display = 'none';\n this.iframeContainer.className = 'sidebar-container';\n\n if (config.theme === 'clean') {\n this.iframeContainer.classList.add('theme-clean');\n } else {\n this.bucketBar = new BucketBar(this.iframeContainer, {\n onFocusAnnotations: tags =>\n this._guestRPC.forEach(rpc => rpc.call('focusAnnotations', tags)),\n onScrollToClosestOffScreenAnchor: (tags, direction) =>\n this._guestRPC.forEach(rpc =>\n rpc.call('scrollToClosestOffScreenAnchor', tags, direction)\n ),\n onSelectAnnotations: (tags, toggle) =>\n this._guestRPC.forEach(rpc =>\n rpc.call('selectAnnotations', tags, toggle)\n ),\n });\n }\n\n this.iframeContainer.appendChild(this.iframe);\n\n // Wrap up the 'iframeContainer' element into a shadow DOM so it is not affected by host CSS styles\n this.hypothesisSidebar = document.createElement('hypothesis-sidebar');\n const shadowRoot = createShadowRoot(this.hypothesisSidebar);\n shadowRoot.appendChild(this.iframeContainer);\n\n element.appendChild(this.hypothesisSidebar);\n }\n\n // Register the sidebar as a handler for Hypothesis errors in this frame.\n if (this.iframe.contentWindow) {\n sendErrorsTo(this.iframe.contentWindow);\n }\n\n this._listeners = new ListenerCollection();\n\n // Set up the toolbar on the left edge of the sidebar.\n const toolbarContainer = document.createElement('div');\n this.toolbar = new ToolbarController(toolbarContainer, {\n createAnnotation: () => {\n if (this._guestRPC.length === 0) {\n return;\n }\n\n const rpc = this._guestWithSelection ?? this._guestRPC[0];\n rpc.call('createAnnotation');\n },\n setSidebarOpen: open => (open ? this.open() : this.close()),\n setHighlightsVisible: show => this.setHighlightsVisible(show),\n });\n\n if (config.theme === 'clean') {\n this.toolbar.useMinimalControls = true;\n } else {\n this.toolbar.useMinimalControls = false;\n }\n\n if (this.iframeContainer) {\n // If using our own container frame for the sidebar, add the toolbar to it.\n this.iframeContainer.prepend(toolbarContainer);\n this.toolbarWidth = this.toolbar.getWidth();\n } else {\n // If using a host-page provided container for the sidebar, the toolbar is\n // not shown.\n this.toolbarWidth = 0;\n }\n\n this._listeners.add(window, 'resize', () => this._onResize());\n\n this._gestureState = {\n // Initial position at the start of a drag/pan resize event (in pixels).\n initial: /** @type {number|null} */ (null),\n\n // Final position at end of drag resize event.\n final: /** @type {number|null} */ (null),\n };\n this._setupGestures();\n this.close();\n\n // Publisher-provided callback functions\n const [serviceConfig] = config.services || [];\n if (serviceConfig) {\n this.onLoginRequest = serviceConfig.onLoginRequest;\n this.onLogoutRequest = serviceConfig.onLogoutRequest;\n this.onSignupRequest = serviceConfig.onSignupRequest;\n this.onProfileRequest = serviceConfig.onProfileRequest;\n this.onHelpRequest = serviceConfig.onHelpRequest;\n }\n\n this.onLayoutChange = config.onLayoutChange;\n\n /** @type {SidebarLayout} */\n this._layoutState = {\n expanded: false,\n width: 0,\n toolbarWidth: 0,\n };\n\n // Initial layout notification\n this._updateLayoutState(false);\n this._setupSidebarEvents();\n }\n\n destroy() {\n this._guestRPC.forEach(rpc => rpc.destroy());\n this._sidebarRPC.destroy();\n this.bucketBar?.destroy();\n this._listeners.removeAll();\n this._hammerManager?.destroy();\n if (this.hypothesisSidebar) {\n this.hypothesisSidebar.remove();\n } else {\n this.iframe.remove();\n }\n this._emitter.destroy();\n\n // Unregister the sidebar iframe as a handler for errors in this frame.\n sendErrorsTo(null);\n }\n\n /**\n * Setup communication with a frame that has connected to the host.\n *\n * @param {'guest'|'sidebar'} source\n * @param {MessagePort} port\n */\n onFrameConnected(source, port) {\n switch (source) {\n case 'guest':\n this._connectGuest(port);\n break;\n case 'sidebar':\n this._sidebarRPC.connect(port);\n break;\n }\n }\n\n /**\n * @param {MessagePort} port\n */\n _connectGuest(port) {\n /** @type {PortRPC<GuestToHostEvent, HostToGuestEvent>} */\n const guestRPC = new PortRPC();\n\n guestRPC.on('textSelected', () => {\n this._guestWithSelection = guestRPC;\n this.toolbar.newAnnotationType = 'annotation';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n guestRPC.on('textUnselected', () => {\n this._guestWithSelection = null;\n this.toolbar.newAnnotationType = 'note';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n // The listener will do nothing if the sidebar doesn't have a bucket bar\n // (clean theme)\n const bucketBar = this.bucketBar;\n // Currently, we ignore `anchorsChanged` for all the guests except the first connected guest.\n if (bucketBar) {\n guestRPC.on(\n 'anchorsChanged',\n /** @param {AnchorPosition[]} positions */\n positions => {\n if (this._guestRPC.indexOf(guestRPC) === 0) {\n bucketBar.update(positions);\n }\n }\n );\n }\n\n guestRPC.on('close', () => {\n guestRPC.destroy();\n if (guestRPC === this._guestWithSelection) {\n this._guestWithSelection = null;\n }\n this._guestRPC = this._guestRPC.filter(rpc => rpc !== guestRPC);\n });\n\n guestRPC.connect(port);\n this._guestRPC.push(guestRPC);\n\n guestRPC.call('sidebarLayoutChanged', this._layoutState);\n }\n\n _setupSidebarEvents() {\n annotationCounts(document.body, this._sidebarRPC);\n sidebarTrigger(document.body, () => this.open());\n\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n /** @param {Record<string, boolean>} flags */ flags =>\n this.features.update(flags)\n );\n\n this._sidebarRPC.on('connect', () => {\n // Show the UI\n if (this.iframeContainer) {\n this.iframeContainer.style.display = '';\n }\n\n const showHighlights = this._config.showHighlights === 'always';\n this.setHighlightsVisible(showHighlights);\n\n if (\n this._config.openSidebar ||\n this._config.annotations ||\n this._config.query ||\n this._config.group\n ) {\n this.open();\n }\n });\n\n this._sidebarRPC.on('showHighlights', () =>\n this.setHighlightsVisible(true)\n );\n\n this._sidebarRPC.on('openSidebar', () => this.open());\n\n this._sidebarRPC.on('closeSidebar', () => this.close());\n\n // Sidebar listens to the `openNotebook` event coming from the sidebar's\n // iframe and re-publishes it via the emitter to the Notebook\n this._sidebarRPC.on(\n 'openNotebook',\n /** @param {string} groupId */\n groupId => {\n this.hide();\n this._emitter.publish('openNotebook', groupId);\n }\n );\n\n this._emitter.subscribe('closeNotebook', () => {\n this.show();\n });\n\n /** @type {Array<[SidebarToHostEvent, Function|undefined]>} */\n const eventHandlers = [\n ['loginRequested', this.onLoginRequest],\n ['logoutRequested', this.onLogoutRequest],\n ['signupRequested', this.onSignupRequest],\n ['profileRequested', this.onProfileRequest],\n ['helpRequested', this.onHelpRequest],\n ];\n eventHandlers.forEach(([event, handler]) => {\n if (handler) {\n this._sidebarRPC.on(event, () => handler());\n }\n });\n }\n\n _resetGestureState() {\n this._gestureState = { initial: null, final: null };\n }\n\n _setupGestures() {\n const toggleButton = this.toolbar.sidebarToggleButton;\n if (toggleButton) {\n this._hammerManager = new Hammer.Manager(toggleButton);\n this._hammerManager.on(\n 'panstart panend panleft panright',\n /* istanbul ignore next */\n event => this._onPan(event)\n );\n this._hammerManager.add(\n new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL })\n );\n }\n }\n\n // Schedule any changes needed to update the sidebar layout.\n _updateLayout() {\n // Only schedule one frame at a time.\n if (this.renderFrame) {\n return;\n }\n\n // Schedule a frame.\n this.renderFrame = requestAnimationFrame(() => {\n this.renderFrame = null;\n\n if (\n this._gestureState.final !== this._gestureState.initial &&\n this.iframeContainer\n ) {\n const margin = /** @type {number} */ (this._gestureState.final);\n const width = -margin;\n this.iframeContainer.style.marginLeft = `${margin}px`;\n if (width >= MIN_RESIZE) {\n this.iframeContainer.style.width = `${width}px`;\n }\n this._updateLayoutState();\n }\n });\n }\n\n /**\n * Update the current layout state and notify the embedder if they provided\n * an `onLayoutChange` callback in the Hypothesis config, as well as guests\n * so they can enable/adapt side-by-side mode.\n *\n * This is called when the sidebar is opened, closed or resized.\n *\n * @param {boolean} [expanded] -\n * `true` or `false` if the sidebar is being directly opened or closed, as\n * opposed to being resized via the sidebar's drag handles\n */\n _updateLayoutState(expanded) {\n // The sidebar structure is:\n //\n // [ Toolbar ][ ]\n // [ ---------- ][ Sidebar iframe container (@frame) ]\n // [ Bucket Bar ][ ]\n //\n // The sidebar iframe is hidden or shown by adjusting the left margin of\n // its container.\n\n const toolbarWidth = (this.iframeContainer && this.toolbar.getWidth()) || 0;\n const frame = /** @type {HTMLElement} */ (\n this.iframeContainer ?? this.externalFrame\n );\n const rect = frame.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(frame);\n const width = parseInt(computedStyle.width);\n const leftMargin = parseInt(computedStyle.marginLeft);\n\n // The width of the sidebar that is visible on screen, including the\n // toolbar, which is always visible.\n let frameVisibleWidth = toolbarWidth;\n\n if (typeof expanded === 'boolean') {\n if (expanded) {\n frameVisibleWidth += width;\n }\n } else {\n if (leftMargin < MIN_RESIZE) {\n frameVisibleWidth -= leftMargin;\n } else {\n frameVisibleWidth += width;\n }\n\n // Infer expanded state based on whether at least part of the sidebar\n // frame is visible.\n expanded = frameVisibleWidth > toolbarWidth;\n }\n\n const layoutState = /** @type {SidebarLayout} */ ({\n expanded,\n width: expanded ? frameVisibleWidth : toolbarWidth,\n height: rect.height,\n toolbarWidth,\n });\n\n this._layoutState = layoutState;\n if (this.onLayoutChange) {\n this.onLayoutChange(layoutState);\n }\n\n this._guestRPC.forEach(rpc =>\n rpc.call('sidebarLayoutChanged', layoutState)\n );\n }\n\n /**\n * On window resize events, update the marginLeft of the sidebar by calling hide/show methods.\n */\n _onResize() {\n if (this.toolbar.sidebarOpen === true) {\n if (window.innerWidth < MIN_RESIZE) {\n this.close();\n } else {\n this.open();\n }\n }\n }\n\n /** @param {HammerInput} event */\n _onPan(event) {\n const frame = this.iframeContainer;\n if (!frame) {\n return;\n }\n\n switch (event.type) {\n case 'panstart':\n this._resetGestureState();\n\n // Disable animated transition of sidebar position\n frame.classList.add('sidebar-no-transition');\n\n // Disable pointer events on the iframe.\n frame.style.pointerEvents = 'none';\n\n this._gestureState.initial = parseInt(\n getComputedStyle(frame).marginLeft\n );\n\n break;\n case 'panend':\n frame.classList.remove('sidebar-no-transition');\n\n // Re-enable pointer events on the iframe.\n frame.style.pointerEvents = '';\n\n // Snap open or closed.\n if (\n this._gestureState.final === null ||\n this._gestureState.final <= -MIN_RESIZE\n ) {\n this.open();\n } else {\n this.close();\n }\n this._resetGestureState();\n break;\n case 'panleft':\n case 'panright': {\n if (typeof this._gestureState.initial !== 'number') {\n return;\n }\n\n const margin = this._gestureState.initial;\n const delta = event.deltaX;\n this._gestureState.final = Math.min(Math.round(margin + delta), 0);\n this._updateLayout();\n break;\n }\n }\n }\n\n open() {\n this._sidebarRPC.call('sidebarOpened');\n\n if (this.iframeContainer) {\n const width = this.iframeContainer.getBoundingClientRect().width;\n this.iframeContainer.style.marginLeft = `${-1 * width}px`;\n this.iframeContainer.classList.remove('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = true;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(true);\n }\n\n this._updateLayoutState(true);\n }\n\n close() {\n if (this.iframeContainer) {\n this.iframeContainer.style.marginLeft = '';\n this.iframeContainer.classList.add('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = false;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(false);\n }\n\n this._updateLayoutState(false);\n }\n\n /**\n * Set whether highlights are visible in guest frames.\n *\n * @param {boolean} visible\n */\n setHighlightsVisible(visible) {\n this.toolbar.highlightsVisible = visible;\n\n // Notify sidebar app of change which will in turn reflect state to guest frames.\n this._sidebarRPC.call('setHighlightsVisible', visible);\n }\n\n /**\n * Shows the sidebar's controls\n */\n show() {\n if (this.iframeContainer) {\n this.iframeContainer.classList.remove('is-hidden');\n }\n }\n\n /**\n * Hides the sidebar's controls\n */\n hide() {\n if (this.iframeContainer) {\n this.iframeContainer.classList.add('is-hidden');\n }\n }\n}\n","const ANNOTATION_COUNT_ATTR = 'data-hypothesis-annotation-count';\n\n/**\n * Update the elements in the container element with the count data attribute\n * with the new annotation count. See:\n * https://h.readthedocs.io/projects/client/en/latest/publishers/host-page-integration/#cmdoption-arg-data-hypothesis-annotation-count\n *\n * @param {Element} rootEl - The DOM element which contains the elements that\n * display annotation count.\n * @param {import('../shared/messaging').PortRPC<'publicAnnotationCountChanged', string>} rpc - Channel for host-sidebar communication\n */\nexport function annotationCounts(rootEl, rpc) {\n rpc.on('publicAnnotationCountChanged', updateAnnotationCountElems);\n\n /** @param {number} newCount */\n function updateAnnotationCountElems(newCount) {\n const elems = rootEl.querySelectorAll('[' + ANNOTATION_COUNT_ATTR + ']');\n Array.from(elems).forEach(elem => {\n elem.textContent = newCount.toString();\n });\n }\n}\n","const SIDEBAR_TRIGGER_BTN_ATTR = 'data-hypothesis-trigger';\n\n/**\n * Show the sidebar when user clicks on an element with the\n * trigger data attribute.\n *\n * @param {Element} rootEl - The DOM element which contains the trigger elements.\n * @param {() => void} showFn - Function which shows the sidebar.\n */\nexport function sidebarTrigger(rootEl, showFn) {\n const triggerElems = rootEl.querySelectorAll(\n '[' + SIDEBAR_TRIGGER_BTN_ATTR + ']'\n );\n\n Array.from(triggerElems).forEach(triggerElem => {\n triggerElem.addEventListener('click', e => {\n showFn();\n e.stopPropagation();\n });\n });\n}\n","import { TinyEmitter } from 'tiny-emitter';\n\n/** @typedef {import('../../types/annotator').Destroyable} Destroyable */\n\n/**\n * Emitter is a communication class that implements the publisher/subscriber\n * pattern. It allows sending and listening events through a shared EventBus.\n * The different elements of the application can communicate with each other\n * without being tightly coupled.\n *\n * @implements {Destroyable}\n */\nexport class Emitter {\n /**\n * @param {TinyEmitter} emitter\n */\n constructor(emitter) {\n this._emitter = emitter;\n\n /** @type {[event: string, callback: Function][]} */\n this._subscriptions = [];\n }\n\n /**\n * Fire an event.\n *\n * @param {string} event\n * @param {unknown[]} args\n */\n publish(event, ...args) {\n this._emitter.emit(event, ...args);\n }\n\n /**\n * Register an event listener.\n *\n * @param {string} event\n * @param {Function} callback\n */\n subscribe(event, callback) {\n this._emitter.on(event, callback);\n this._subscriptions.push([event, callback]);\n }\n\n /**\n * Remove an event listener.\n *\n * @param {string} event\n * @param {Function} callback\n */\n unsubscribe(event, callback) {\n this._emitter.off(event, callback);\n this._subscriptions = this._subscriptions.filter(\n ([subEvent, subCallback]) =>\n subEvent !== event || subCallback !== callback\n );\n }\n\n /**\n * Remove all event listeners.\n */\n destroy() {\n for (let [event, callback] of this._subscriptions) {\n this._emitter.off(event, callback);\n }\n this._subscriptions = [];\n }\n}\n\nexport class EventBus {\n constructor() {\n this._emitter = new TinyEmitter();\n }\n\n createEmitter() {\n return new Emitter(this._emitter);\n }\n}\n","// Load polyfill for :focus-visible pseudo-class.\nimport 'focus-visible';\n\n// Enable debug checks for Preact. Removed in prod builds by Rollup config.\nimport 'preact/debug';\n\n// Load icons.\nimport { registerIcons } from '@hypothesis/frontend-shared';\nimport { annotatorIcons } from './icons';\nregisterIcons(annotatorIcons);\n\nimport {\n PortProvider,\n installPortCloseWorkaroundForSafari,\n} from '../shared/messaging';\nimport { getConfig } from './config/index';\nimport { Guest } from './guest';\nimport { HypothesisInjector } from './hypothesis-injector';\nimport {\n VitalSourceInjector,\n vitalSourceFrameRole,\n} from './integrations/vitalsource';\nimport { Notebook } from './notebook';\nimport { Sidebar } from './sidebar';\nimport { EventBus } from './util/emitter';\n\n/** @typedef {import('../types/annotator').Destroyable} Destroyable */\n\n// Look up the URL of the sidebar. This element is added to the page by the\n// boot script before the \"annotator\" bundle loads.\nconst sidebarLinkElement = /** @type {HTMLLinkElement} */ (\n document.querySelector(\n 'link[type=\"application/annotator+html\"][rel=\"sidebar\"]'\n )\n);\n\n/**\n * @typedef {import('./components/NotebookModal').NotebookConfig} NotebookConfig\n * @typedef {import('./guest').GuestConfig} GuestConfig\n * @typedef {import('./hypothesis-injector').InjectConfig} InjectConfig\n * @typedef {import('./sidebar').SidebarConfig} SidebarConfig\n * @typedef {import('./sidebar').SidebarContainerConfig} SidebarContainerConfig\n */\n\n/**\n * Entry point for the part of the Hypothesis client that runs in the page being\n * annotated.\n *\n * Depending on the client configuration in the current frame, this can\n * initialize different functionality. In \"host\" frames the sidebar controls and\n * iframe containing the sidebar application are created. In \"guest\" frames the\n * functionality to support anchoring and creating annotations is loaded. An\n * instance of Hypothesis will have one host frame, one sidebar frame and one or\n * more guest frames. The most common case is that the host frame, where the\n * client is initially loaded, is also the only guest frame.\n */\nfunction init() {\n const annotatorConfig = /** @type {GuestConfig & InjectConfig} */ (\n getConfig('annotator')\n );\n\n const hostFrame = annotatorConfig.subFrameIdentifier ? window.parent : window;\n\n /** @type {Destroyable[]} */\n const destroyables = [];\n\n if (hostFrame === window) {\n // Ensure port \"close\" notifications from eg. guest frames are delivered properly.\n const removeWorkaround = installPortCloseWorkaroundForSafari();\n destroyables.push({ destroy: removeWorkaround });\n\n const sidebarConfig = /** @type {SidebarConfig} */ (getConfig('sidebar'));\n\n const hypothesisAppsOrigin = new URL(sidebarConfig.sidebarAppUrl).origin;\n const portProvider = new PortProvider(hypothesisAppsOrigin);\n\n const eventBus = new EventBus();\n const sidebar = new Sidebar(document.body, eventBus, sidebarConfig);\n const notebook = new Notebook(\n document.body,\n eventBus,\n /** @type {NotebookConfig} */ (getConfig('notebook'))\n );\n\n portProvider.on('frameConnected', (source, port) =>\n sidebar.onFrameConnected(source, port)\n );\n destroyables.push(portProvider, sidebar, notebook);\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'container') {\n const vitalSourceInjector = new VitalSourceInjector(annotatorConfig);\n destroyables.push(vitalSourceInjector);\n } else {\n // Set up automatic injection of the client into iframes in this frame.\n const hypothesisInjector = new HypothesisInjector(\n document.body,\n annotatorConfig\n );\n // Create the guest that handles creating annotations and displaying highlights.\n const guest = new Guest(document.body, annotatorConfig, hostFrame);\n destroyables.push(hypothesisInjector, guest);\n }\n\n sidebarLinkElement.addEventListener('destroy', () => {\n destroyables.forEach(instance => instance.destroy());\n\n // Remove all the `<link>`, `<script>` and `<style>` elements added to the\n // page by the boot script.\n const clientAssets = document.querySelectorAll('[data-hypothesis-asset]');\n clientAssets.forEach(el => el.remove());\n });\n}\n\n/**\n * Returns a Promise that resolves when the document has loaded (but subresources\n * may still be loading).\n *\n * @return {Promise<void>}\n */\nfunction documentReady() {\n return new Promise(resolve => {\n if (document.readyState !== 'loading') {\n resolve();\n }\n // nb. `readystatechange` may be emitted twice, but `resolve` only resolves\n // on the first call.\n document.addEventListener('readystatechange', () => resolve());\n });\n}\n\ndocumentReady().then(init);\n"],"names":["applyFocusVisiblePolyfill","scope","hadKeyboardEvent","hadFocusVisibleRecently","hadFocusVisibleRecentlyTimeout","inputTypesAllowlist","text","search","url","tel","email","password","number","date","month","week","time","datetime","isValidFocusTarget","el","document","nodeName","classList","focusTriggersKeyboardModality","type","tagName","readOnly","isContentEditable","addFocusVisibleClass","contains","add","setAttribute","removeFocusVisibleClass","hasAttribute","remove","removeAttribute","onKeyDown","e","metaKey","altKey","ctrlKey","activeElement","onPointerDown","onFocus","target","onBlur","window","clearTimeout","setTimeout","onVisibilityChange","visibilityState","addInitialPointerMoveListeners","addEventListener","onInitialPointerMove","removeInitialPointerMoveListeners","removeEventListener","toLowerCase","nodeType","Node","DOCUMENT_FRAGMENT_NODE","host","DOCUMENT_NODE","documentElement","event","CustomEvent","error","createEvent","initCustomEvent","dispatchEvent","factory","n","l","u","t","o","r","c","s","a","h","parentNode","removeChild","y","i","f","props","key","ref","__k","__","__b","__e","__d","__c","__h","constructor","__v","vnode","d","children","_","this","context","k","indexOf","length","b","base","m","push","g","__r","debounceRendering","sort","some","__P","j","__n","ownerSVGElement","z","w","v","p","A","Array","isArray","x","P","N","M","appendChild","nextSibling","insertBefore","$","setProperty","test","H","style","cssText","replace","slice","T","I","C","contextType","value","__E","prototype","render","O","sub","state","__s","getDerivedStateFromProps","componentWillMount","componentDidMount","componentWillReceiveProps","shouldComponentUpdate","forEach","componentWillUpdate","componentDidUpdate","getChildContext","getSnapshotBeforeUpdate","L","diffed","call","localName","createTextNode","createElementNS","createElement","is","data","childNodes","dangerouslySetInnerHTML","attributes","name","__html","innerHTML","checked","current","unmount","componentWillUnmount","S","arguments","defaultProps","firstChild","getDerivedStateFromError","setState","componentDidCatch","forceUpdate","Promise","then","bind","resolve","__PREACT_DEVTOOLS__","attachPreact","Fragment","Component","displayName","__o","reduce","__source","fileName","lineNumber","console","warn","WeakMap","hasOwnProperty","Object","toString","JSON","stringify","pop","useEffect","useLayoutEffect","lazyPropTypes","Error","componentStack","propTypes","has","set","__f","keys","message","get","create","__self","__proto__","join","hasOwn","classNames","classes","arg","argType","inner","apply","module","exports","default","__H","shift","requestAnimationFrame","cancelAnimationFrame","filter","iconRegistry","Map","SvgIcon","className","inline","title","markup","element","useRef","svg","querySelector","spanProps","_jsxDEV","classnames","columnNumber","registerIcon","Symbol","_jsxFileName","ButtonBase","buttonRef","icon","iconPosition","size","variant","expanded","pressed","restProps","_restProps$role","role","ariaProps","IconButton","LabeledButton","cancelSVG","Icon","containerClasses","Link","linkRef","rel","annotatorIcons","annotate","cancel","caution","hide","highlight","jstor","note","pointer","show","ListenerCollection","_listeners","eventTarget","eventType","listener","options","symbol","listenerId","delete","removeAll","clear","byteToHex","val","str","generateHexString","len","bytes","Uint8Array","crypto","getRandomValues","from","map","isMessage","property","isMessageEqual","PortFinder","hostFrame","source","_hostFrame","_source","destroy","async","isValidRequest","requestId","reject","postRequest","postMessage","frame1","frame2","intervalId","setInterval","timeoutId","clearInterval","ports","E","on","callback","ctx","fn","once","self","off","emit","evtArr","evts","liveEvents","tinyEmitterModule","TinyEmitter","tinyEmitter","errorDestination","serializeError","err","stack","String","undefined","sendError","postErr","DOMException","sendErr","sendErrorsTo","destination","PortProvider","hypothesisAppsOrigin","_hypothesisAppsOrigin","_emitter","_handledRequests","Set","_sidebarHostChannel","MessageChannel","_allowedMessages","allowedOrigin","_listen","errorContext","sentErrors","reportError","origin","channel","MessagePort","ServiceWorker","isSourceWindow","find","allowedMessage","_messageMatches","messageChannel","targetOrigin","port1","port2","args","eventName","handler","VERSION","PROTOCOL","sendCall","port","method","sequence","protocol","version","shouldUseSafariWorkaround","userAgent","webkitVersionMatch","match","parseInt","PortRPC","navigator","currentWindow","_port","_methods","_sequence","_callbacks","parent","_pendingCalls","_receivedCloseEvent","connect","_handle","start","close","_destroyed","seq","finalArg","_parseMessage","msg","response","cb","assign","dest","src","parseJsonConfig","config","settingsElements","querySelectorAll","settings","parse","textContent","toBoolean","trim","toLocaleLowerCase","numericalVal","Number","isNaN","Boolean","urlFromLinkTag","window_","link","href","checkIfString","settingsFrom","configFuncSettings","hypothesisConfig","docs","configFuncSettingsFrom","jsonConfigs","hostPageSetting","ignoreOtherConfiguration","annotations","annotFragmentMatch","location","annotationsFromURL","clientUrl","group","groupFragmentMatch","groupFromURL","notebookAppUrl","showHighlights","sidebarAppUrl","query","queryFragmentMatch","decodeURIComponent","queryFromURL","getHostPageSetting","configDefinitions","allowInBrowserExt","defaultValue","getValue","appType","branding","contentPartner","enableExperimentalNewNoteButton","focus","theme","usernameUrl","onLayoutChange","openSidebar","coerce","requestConfigFromFrame","services","subFrameIdentifier","externalContainerSelector","getConfig","contexts","annotator","sidebar","notebook","configurationKeys","configDef","hasDefault","isURLFromBrowserExtension","startsWith","modifiers","alt","ctrl","meta","installShortcut","shortcut","onPress","rootElement","onKeydown","parts","split","requiredModifiers","requiredKey","part","modifierFlag","shiftKey","mappings","Left","Up","Down","Right","Spacebar","Del","normalizeKeyName","matchShortcut","useShortcut","NumberIcon","badgeCount","_jsx","AdderToolbarArrow","arrowDirection","ToolbarButton","label","onClick","_jsxs","AdderToolbar","isVisible","onCommand","annotationCount","annotateShortcut","highlightShortcut","showShortcut","dir","visibility","_Fragment","isTouchDevice","_window","matchMedia","matches","createShadowRoot","container","shadowRoot","attachShadow","mode","_document$querySelect","linkEl","loadStyles","applyFocusVisible","stopPropagation","passive","toPx","pixels","Adder","_outerContainer","_shadowRoot","position","top","left","_view","ownerDocument","defaultView","_width","getBoundingClientRect","width","_height","height","_isVisible","_arrowDirection","_onAnnotate","onAnnotate","_onHighlight","onHighlight","_onShowAnnotations","onShowAnnotations","annotationsForSelection","_render","selectionRect","isRTLselection","_calculateTarget","_showAt","hMargin","Math","min","adderWidth","touchScreenOffset","adderHeight","innerHeight","max","innerWidth","_findZindex","elementsFromPoint","zIndexes","getComputedStyle","zIndex","isInteger","parentRect","parentEl","parentElement","nearestPositionedAncestor","command","nodeTextLength","node","ELEMENT_NODE","TEXT_NODE","previousSiblingsTextLength","sibling","previousSibling","resolveOffsets","offsets","nextOffset","nodeIter","createNodeIterator","NodeFilter","SHOW_TEXT","results","textNode","currentNode","nextNode","offset","RangeError","TextPosition","relativeTo","direction","tw","createTreeWalker","getRootNode","forwards","previousNode","static","fromPoint","textOffset","TextRange","end","toRange","range","Range","setStart","setEnd","startContainer","startOffset","endContainer","endOffset","root","placeholderSelector","isInPlaceholder","closest","isSelectionBackwards","selection","focusNode","anchorNode","focusOffset","anchorOffset","getRangeAt","isNodeInRange","_node$nodeValue$lengt","_node$nodeValue","nodeValue","comparePoint","forEachNodeInRange","commonAncestorContainer","SHOW_ALL","selectionFocusRect","isCollapsed","textBoxes","whitespaceOnly","textNodes","rects","nodeRange","createRange","selectNodeContents","collapsed","viewportRects","getClientRects","detach","concat","getTextBoundingBoxes","SVG_NAMESPACE","highlightRange","cssClass","splitText","wholeTextNodesInRange","inPlaceholder","textNodeSpans","prevNode","currentSpan","whitespace","span","highlights","nodes","highlightEl","replaceChild","highlightEls","canvasEl","pageEl","getPDFCanvas","svgHighlightLayer","svgStyle","mixBlendMode","canvasRect","highlightRects","highlightRect","rect","svgHighlight","append","drawHighlightsAbovePDFCanvas","replaceWith","replacements","removeHighlights","setHighlightsFocused","focused","toggle","collection","acc","bottom","right","BucketBarClient","contentContainer","hostRPC","_hostRPC","_updatePending","_anchors","update","anchors","positions","annotation","tag","$tag","anchor1","anchor2","computeAnchorPositions","shownWarnings","warnOnce","reset","annotatorFlags","FeatureFlags","knownFlags","super","_flags","_knownFlags","flags","flag","entries","flagEnabled","_this$_flags$get","includes","allFlags","fromEntries","reverse","oneIfNotZero","advanceBlock","peq","hIn","pV","mV","hInIsNegative","eq","xV","xH","pH","mH","hOut","lastRowMask","findMatchEnds","pattern","maxErrors","bMax","ceil","Uint32Array","fill","emptyPeq","asciiPeq","charCodeAt","charPeq","idx","score","charCode","carry","maxBlockScore","remainder","splice","errors","patRev","minStart","rm","findMatchStarts","matchPos","exactMatches","approxSearch","textMatchScore","matchQuote","quote","scoreMatch","quoteScore","prefixScore","prefix","suffixScore","suffix","posScore","hint","abs","quoteWeight","scoredMatches","getPathSegment","result","getNodeName","pos","tmp","getNodePosition","xpathFromNode","xpath","elem","nthChildOfType","index","toUpperCase","matchIndex","child","nodeFromXPath","body","segments","segment","elementName","elementIndex","separatorPos","indexStr","evaluateSimpleXPath","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","RangeAnchor","selector","startPos","fromCharOffset","endPos","toSelector","normalizedRange","fromRange","textRange","TextPositionAnchor","fromOffsets","TextQuoteAnchor","exact","toPositionAnchor","anchor","selectors","maybeAssertQuote","_quote","promise","range_","catch","fromSelector","position_","quote_","describe","types","normalizeURI","uri","baseURI","URL","HTMLMetadata","_getDocumentHref","links","_getLinks","getDocumentMetadata","metadata","dc","_getMetaTags","eprints","facebook","highwire","prism","twitter","favicon","_getFavicon","_getTitle","dcLink","documentFingerprint","attribute","tags","getAttribute","content","RegExp","linkElements","hreflang","_absoluteUrl","values","doi","id","dcRelationValues","dcIdentifierValues","identifier","dcUrnRelationComponent","dcUrnIdentifierComponent","dcUrn","encodeURIComponent","allowedSchemes","scheme","rectIsEmpty","linesOverlap","rectIntersects","rectA","rectB","rectsOverlapVertically","rectsOverlapHorizontally","unionRects","DOMRect","rectCenter","DOMPoint","contentSelectors","textRectRange","textRect","elementContentRect","scrollLeft","scrollTop","scrollHeight","scrollWidth","textNodesInRect","shouldVisit","contentIntersectsRect","getScrollAnchor","viewport","anchorRange","hasFixedPosition","textNodeLoop","textLen","word","wordBox","preserveScrollPosition","scrollRoot","anchorTop","scrollDelta","COMPLETE","CANCELED","setElementScroll","scrollTo","animate","scrollSettings","_scrollSettings","maxSynchronousAlignments","parentPosition","differenceX","differenceY","targetWidth","targetHeight","align","targetPosition","leftAlign","topAlign","leftOffset","topOffset","leftScalar","topScalar","isWindow","pageXOffset","pageYOffset","lockX","lockY","offsetLeft","offsetTop","clientWidth","clientHeight","getTargetScrollLocation","Date","now","startTime","timeValue","endIterations","easeValue","ease","scrollAncestor","task","raf","defaultIsWindow","defaultIsScrollable","overflow","defaultValidTarget","findParentElement","assignedSlot","ownerWindow","scrollIntoView","pow","parents","validTarget","isScrollable","debug","log","scrollingElements","done","cancelHandler","idle","lastSettings","passiveOptions","endType","cancellable","transitionScrollTo","interpolate","fraction","scrollElement","maxDuration","scrollStart","scrollDuration","scrollFraction","HTMLIntegration","features","_htmlMeta","_sideBySideEnabled","_sideBySideActive","_lastLayout","_flagsChanged","sideBySide","fitSideBySide","canAnnotate","layout","maximumWidthToFit","active","_activateSideBySide","_deactivateSideBySide","sidebarWidth","rightMargin","computeLeftMargin","marginLeft","marginRight","beforeBodyLeft","contentArea","leftMarginVotes","rightMarginVotes","contentSelector","paragraphs","textLength","_leftMarginVotes$get","_rightMarginVotes$get","leftVotes","rightVotes","leftMargin","leftPos","rightPos","guessMainContentArea","freeSpace","adjustedMargin","_anchor$highlights","defineProperty","configurable","scrollElementIntoView","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","freeGlobal","global","freeSelf","Function","objectToString","nativeMax","nativeMin","isObject","toNumber","isObjectLike","isSymbol","other","valueOf","isBinary","lodash_debounce","func","wait","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","TypeError","invokeFunc","thisArg","leadingEdge","timerExpired","shouldInvoke","timeSinceLastCall","trailingEdge","remainingWait","debounced","isInvoking","flush","advance","count","countChars","translateOffsets","input","output","beforeStartCount","startToEndCount","outputStart","RenderingStates","pageTextCache","quotePositionCache","quotePositionCacheKey","getNodeTextLayer","_el$closest","getPDFViewer","PDFViewerApplication","pdfViewer","getPageView","pageIndex","pageView","pdfPage","onPagesLoaded","eventBus","getPageTextContent","cachedText","pageText","getTextContent","normalizeWhitespace","items","it","getPageText","findPageByOffset","viewer","pageStartOffset","pageEndOffset","pagesCount","isSpace","char","isNotSpace","anchorByPosition","page","all","renderingState","textLayer","renderingDone","textLayerDiv","textLayerStr","textLayerStart","textLayerEnd","stripSpaces","placeholder","createPlaceholder","div","setStartBefore","setEndAfter","stripped","matchedText","substring","cacheKey","cachedPos","quoteSelector","positionHint","pageCount","pageIndexes","expectedPageIndex","expectedOffsetInPage","strippedPrefix","strippedSuffix","strippedQuote","bestMatch","strippedText","strippedHint","exactQuoteMatch","exactPrefixMatch","exactSuffixMatch","hasContext","anchorQuote","getTextLayerForRange","startTextLayer","endTextLayer","startPageIndex","getSiblingIndex","pageOffset","getPageOffset","Banners","ContentPartnerBanner","provider","onClose","WarningBanner","PDFMetadata","app","_loaded","initializedPromise","initialized","timeout","pdfViewerInitialized","downloadComplete","finish","getUri","getPDFURL","fingerprintToURN","getFingerprint","info","documentInfo","contentDispositionFilename","pdfDocument","getMetadata","Title","pathSegments","pathname","filenameFromURL","fingerprints","fingerprint","anchorIsInPlaceholder","delay","ms","PDFIntegration","_pdfViewerApp$appConf","_pdfViewerApp$appConf2","_options$reanchoringM","_options$contentPartn","pdfViewerApp","pdfContainer","appConfig","appContainer","pdfMetadata","observer","MutationObserver","debounce","_update","observe","attributeFilter","childList","subtree","_reanchoringMaxWait","reanchoringMaxWait","_banner","_bannerState","noTextWarning","_updateBannerState","_checkForSelectableText","_updateAnnotationLayerVisibility","getSelection","_this$_banner","disconnect","canDescribe","hasText","documentHasText","outerContainer","_this$_banner2","prepend","bannerHeight","refreshAnnotations","_page$textLayer","_container$querySelec","hl","sidebarLayout","reservedSpace","toolbarWidth","currentScaleValue","_anchorOffset","_waitForAnnotationToBeAnchored","_anchor","offsetParent","offsetRelativeTo","FrameObserver","onFrameAdded","onFrameRemoved","_element","_onFrameAdded","_onFrameRemoved","_annotatableFrames","_isDisconnected","_mutationObserver","_discoverFrames","frame","onNextDocumentReady","contentWindow","_removeFrame","frames","_addFrame","unsubscribe","onDocumentReady","doc","pollInterval","pollTimer","pollForDocumentChange","documents","WeakSet","cancelPoll","checkForDocumentChange","currentDocument","contentDocument","_frame$contentDocumen","hasBlankDocumentThatWillNavigate","readyState","_frame$contentWindow","canceled","initialCheckTimer","ImageTextLayer","image","charBoxes","containerParent","color","textAlign","fontFamily","fontSize","getContext","font","scaledValue","dimension","unit","columns","words","currentWord","addWord","lines","currentLine","addLine","prevWord","prevCenter","xDist","currentColumn","addColumn","line","prevLine","yDist","analyzeLayout","column","columnEl","display","lineEl","marginTop","whiteSpace","wordEl","transformOrigin","metrics","measureText","xScale","yScale","transform","updateTextLayerSize","imageWidth","imageHeight","_updateTextLayerSize","ResizeObserver","_imageSizeObserver","updateSync","_this$_imageSizeObser","HypothesisInjector","_config","_frameObserver","injectClient","assetRoot","injectedConfig","configElement","innerText","bootScript","iframeDocument","findBookElement","document_","vitalSourceFrameRole","_window_$frameElement","parentDoc","frameElement","VitalSourceInjector","bookElement","contentFrames","injectClientIntoContentFrame","getPDFPageImage","VitalSourceContentIntegration","html_side_by_side","_htmlIntegration","stopEvents","insertAdjacentElement","removeScrollingAttr","makeContentFrameScrollable","bookImage","pageData","innerPageData","charRects","glyphs","glyph","_textLayer","_this$_textLayer","bookContainer","newWidth","scrollToAnchor","createIntegration","selectedRange","rangeCount","SelectionObserver","isMouseDown","_pendingCallback","scheduleCallback","eventHandler","_cancelPendingCallback","_document","itemForNode","checkedNodes","item","rangeUtil","_node$_annotation","_annotation","annotationsAt","getHighlightsContainingNode","ann","resolveAnchor","removeTextSelection","_document$getSelectio","removeAllRanges","Guest","_highlightsVisible","_isAdderVisible","_informHostOnNextSelectionClear","selectedRanges","_adder","createAnnotation","selectAnnotations","_selectionObserver","_onSelection","_onClearSelection","_annotations","_frameIdentifier","_portFinder","_integration","_connectHost","_sidebarRPC","_connectSidebar","_bucketBarClient","_setupElementEvents","_focusedAnnotations","maybeCloseSidebar","_repositionAdder","frameIdentifier","_window$getSelection","_focusAnnotations","_scrollToClosestOffScreenAnchor","ancestor","hostPort","discover","bubbles","cancelable","detail","setHighlightsVisible","getDocumentInfo","removeAllHighlights","$orphan","every","_updateAnchors","notify","ranges","$highlight","closestAnchor","closestTop","findClosestOffscreenAnchor","isBackwards","focusRect","visible","sideBySideActive","focusedAnnotationTags","addConfigFragment","baseURL","params","URLSearchParams","hash","createAppConfig","appURL","_appConfig$services","hostURL","service","onLoginRequest","onLoginRequestProvided","onLogoutRequest","onLogoutRequestProvided","onSignupRequest","onSignupRequestProvided","onProfileRequest","onProfileRequestProvided","onHelpRequest","onHelpRequestProvided","NotebookIframe","groupId","allowFullScreen","NotebookModal","iframeKey","setIframeKey","useState","isHidden","setIsHidden","setGroupId","originalDocumentOverflowStyle","emitterRef","emitter","createEmitter","subscribe","hidden","_emitterRef$current","publish","Notebook","exportName","VENDOR_PREFIXES","TEST_ELEMENT","round","setTimeoutContext","bindFn","invokeArrayArg","each","obj","iterator","deprecate","deprecationMessage","nextKey","extend","merge","inherit","properties","childP","baseP","_super","boolOrFn","ifUndefined","val1","val2","addEventListeners","splitStr","removeEventListeners","hasParent","inStr","inArray","findByKey","toArray","uniqueArray","prefixed","prop","camelProp","_uniqueId","getWindowForElement","parentWindow","SUPPORT_TOUCH","SUPPORT_POINTER_EVENTS","SUPPORT_ONLY_TOUCH","INPUT_TYPE_TOUCH","INPUT_TYPE_MOUSE","DIRECTION_VERTICAL","DIRECTION_UP","PROPS_XY","PROPS_CLIENT_XY","Input","manager","inputTarget","domHandler","ev","enable","init","inputHandler","pointersLen","pointers","changedPointersLen","changedPointers","isFirst","isFinal","session","pointersLength","firstInput","simpleCloneInputData","firstMultiple","offsetCenter","center","getCenter","timeStamp","deltaTime","angle","getAngle","distance","getDistance","offsetDelta","prevDelta","prevInput","deltaX","deltaY","computeDeltaXY","offsetDirection","getDirection","overallVelocity","getVelocity","overallVelocityX","overallVelocityY","scale","rotation","getRotation","maxPointers","velocity","velocityX","velocityY","last","lastInterval","computeIntervalInputData","srcEvent","computeInputData","recognize","clientX","clientY","p1","p2","sqrt","atan2","PI","evEl","evTarget","evWin","MOUSE_INPUT_MAP","mousedown","mousemove","mouseup","MOUSE_ELEMENT_EVENTS","MOUSE_WINDOW_EVENTS","MouseInput","button","which","pointerType","POINTER_INPUT_MAP","pointerdown","pointermove","pointerup","pointercancel","pointerout","IE10_POINTER_TYPE_ENUM","POINTER_ELEMENT_EVENTS","POINTER_WINDOW_EVENTS","PointerEventInput","store","pointerEvents","MSPointerEvent","PointerEvent","removePointer","eventTypeNormalized","isTouch","storeIndex","pointerId","SINGLE_TOUCH_INPUT_MAP","touchstart","touchmove","touchend","touchcancel","SINGLE_TOUCH_TARGET_EVENTS","SINGLE_TOUCH_WINDOW_EVENTS","SingleTouchInput","started","normalizeSingleTouches","touches","changed","changedTouches","TOUCH_INPUT_MAP","TOUCH_TARGET_EVENTS","TouchInput","targetIds","getTouches","allTouches","targetTouches","changedTargetTouches","touch","TouchMouseInput","mouse","primaryTouch","lastTouches","recordTouches","eventData","setLastTouch","lastTouch","lts","isSyntheticEvent","dx","dy","inputEvent","inputData","isMouse","sourceCapabilities","firesTouchEvents","PREFIXED_TOUCH_ACTION","NATIVE_TOUCH_ACTION","TOUCH_ACTION_COMPUTE","TOUCH_ACTION_AUTO","TOUCH_ACTION_MANIPULATION","TOUCH_ACTION_NONE","TOUCH_ACTION_PAN_X","TOUCH_ACTION_PAN_Y","TOUCH_ACTION_MAP","touchMap","cssSupports","CSS","supports","getTouchActionProps","TouchAction","compute","actions","touchAction","recognizers","recognizer","getTouchAction","hasPanX","hasPanY","cleanTouchActions","preventDefaults","prevented","preventDefault","hasNone","isTapPointer","isTapMovement","isTapTouchTime","DIRECTION_LEFT","preventSrc","STATE_FAILED","Recognizer","defaults","simultaneous","requireFail","stateStr","directionStr","getRecognizerByNameIfManager","otherRecognizer","AttrRecognizer","PanRecognizer","pX","pY","PinchRecognizer","PressRecognizer","_timer","_input","RotateRecognizer","SwipeRecognizer","TapRecognizer","pTime","pCenter","Hammer","preset","Manager","recognizeWith","dropRecognizeWith","requireFailure","dropRequireFailure","hasRequireFailures","canRecognizeWith","additionalEvent","tryEmit","canEmit","inputDataClone","process","attrTest","optionPointers","isRecognized","isValid","threshold","DIRECTION_HORIZONTAL","directionTest","hasMoved","inOut","validPointers","validMovement","validTime","taps","interval","posThreshold","validTouchTime","failTimeout","validInterval","validMultiTap","tapCount","domEvents","inputClass","cssProps","userSelect","touchSelect","touchCallout","contentZooming","userDrag","tapHighlightColor","handlers","oldCssProps","toggleCssProps","stop","force","stopped","curRecognizer","existing","events","gestureEvent","initEvent","gesture","triggerDomEvent","INPUT_START","INPUT_MOVE","INPUT_END","INPUT_CANCEL","STATE_POSSIBLE","STATE_BEGAN","STATE_CHANGED","STATE_ENDED","STATE_RECOGNIZED","STATE_CANCELLED","DIRECTION_NONE","DIRECTION_RIGHT","DIRECTION_DOWN","DIRECTION_ALL","Tap","Pan","Swipe","Pinch","Rotate","Press","BucketList","BucketItem","topPosition","Buckets","above","below","buckets","onFocusAnnotations","onScrollToClosestOffScreenAnchor","onSelectAnnotations","showUpNavigation","showDownNavigation","onMouseEnter","onMouseOut","bucket","BucketBar","_bucketsContainer","_onFocusAnnotations","_onScrollToClosestOffScreenAnchor","_onSelectAnnotations","anchorPositions","aboveTags","belowTags","currentBucket","newBucket","aPos","isContainedWithin","updatedBottom","updatedHeight","computeBuckets","toogle","buttonProps","Toolbar","closeSidebar","isSidebarOpen","newAnnotationType","toggleHighlights","toggleSidebar","toggleSidebarRef","useMinimalControls","selected","ToolbarController","setSidebarOpen","_container","_useMinimalControls","_newAnnotationType","_sidebarOpen","_closeSidebar","_toggleSidebar","_toggleHighlights","_createAnnotation","_sidebarToggleButton","getWidth","minimal","sidebarOpen","open","highlightsVisible","sidebarToggleButton","MIN_RESIZE","Sidebar","_guestWithSelection","_guestRPC","iframe","sidebarURL","sidebarAppSrc","sidebarFrame","createSidebarIframe","bucketBar","externalFrame","iframeContainer","rpc","hypothesisSidebar","toolbarContainer","toolbar","_this$_guestWithSelec","_onResize","_gestureState","initial","final","_setupGestures","serviceConfig","_layoutState","_updateLayoutState","_setupSidebarEvents","_this$bucketBar","_this$_hammerManager","_hammerManager","onFrameConnected","_connectGuest","guestRPC","rootEl","newCount","elems","showFn","triggerElems","triggerElem","sidebarTrigger","_resetGestureState","toggleButton","_onPan","_updateLayout","renderFrame","margin","_this$iframeContainer","computedStyle","frameVisibleWidth","layoutState","delta","Emitter","_subscriptions","subEvent","subCallback","EventBus","icons","registerIcons","sidebarLinkElement","annotatorConfig","destroyables","removeWorkaround","_event$data","installPortCloseWorkaroundForSafari","sidebarConfig","portProvider","vitalSourceInjector","hypothesisInjector","guest","instance"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"annotator.bundle.js","sources":["app:///node_modules/focus-visible/dist/focus-visible.js","app:///node_modules/preact/dist/preact.mjs","app:///node_modules/preact/devtools/dist/devtools.mjs","app:///node_modules/preact/debug/dist/debug.mjs","app:///node_modules/classnames/index.js","app:///node_modules/preact/hooks/dist/hooks.mjs","app:///node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs","app:///node_modules/@hypothesis/frontend-shared/lib/components/SvgIcon.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/buttons.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Checkbox.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Dialog.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Icon.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Link.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Panel.js","app:///node_modules/@hypothesis/frontend-shared/lib/components/Spinner.js","app:///src/annotator/icons.js","app:///src/shared/listener-collection.js","app:///src/shared/random.js","app:///src/shared/messaging/port-util.js","app:///src/shared/messaging/port-finder.js","app:///node_modules/tiny-emitter/index.js","app:///src/shared/frame-error-capture.js","app:///src/shared/messaging/port-provider.js","app:///src/shared/messaging/port-rpc.js","app:///src/boot/parse-json-config.js","app:///src/shared/type-coercions.js","app:///src/annotator/config/url-from-link-tag.js","app:///src/annotator/config/settings.js","app:///src/annotator/config/config-func-settings-from.js","app:///src/annotator/config/index.js","app:///src/annotator/config/is-browser-extension.js","app:///src/shared/shortcut.js","app:///src/annotator/components/AdderToolbar.js","app:///src/shared/user-agent.js","app:///src/annotator/util/shadow-root.js","app:///src/annotator/adder.js","app:///src/annotator/anchoring/text-range.js","app:///src/annotator/anchoring/placeholder.js","app:///src/annotator/range-util.js","app:///src/annotator/highlighter.js","app:///src/annotator/bucket-bar-client.js","app:///src/annotator/util/buckets.js","app:///src/shared/warn-once.js","app:///src/annotator/features.js","app:///node_modules/approx-string-match/build/src/index.js","app:///src/annotator/anchoring/match-quote.js","app:///src/annotator/anchoring/xpath.js","app:///src/annotator/anchoring/types.js","app:///src/annotator/anchoring/html.js","app:///src/annotator/util/url.js","app:///src/annotator/integrations/html-metadata.js","app:///src/annotator/util/geometry.js","app:///src/annotator/integrations/html-side-by-side.js","app:///node_modules/scroll-into-view/scrollIntoView.js","app:///src/annotator/util/scroll.js","app:///src/annotator/integrations/html.js","app:///node_modules/lodash.debounce/index.js","app:///src/annotator/util/normalize.js","app:///src/annotator/anchoring/pdf.js","app:///src/annotator/components/Banners.js","app:///src/annotator/components/ContentPartnerBanner.js","app:///src/annotator/components/WarningBanner.js","app:///src/annotator/integrations/pdf-metadata.js","app:///src/annotator/integrations/pdf.js","app:///src/annotator/frame-observer.js","app:///src/annotator/integrations/image-text-layer.js","app:///src/annotator/hypothesis-injector.js","app:///src/annotator/integrations/vitalsource.js","app:///src/annotator/integrations/index.js","app:///src/annotator/selection-observer.js","app:///src/annotator/guest.js","app:///src/annotator/util/frame.js","app:///src/shared/config-fragment.js","app:///src/annotator/config/app.js","app:///src/annotator/components/NotebookModal.js","app:///src/annotator/notebook.js","app:///node_modules/hammerjs/hammer.js","app:///src/annotator/components/Buckets.js","app:///src/annotator/bucket-bar.js","app:///src/annotator/components/Toolbar.js","app:///src/annotator/toolbar.js","app:///src/annotator/sidebar.js","app:///src/annotator/annotation-counts.js","app:///src/annotator/sidebar-trigger.js","app:///src/annotator/util/emitter.js","app:///src/annotator/index.js"],"sourcesContent":["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on <html> whenever the\n // window blurs, even if you're tabbing out of the page. ¯\\_(ツ)_/¯\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n","var n,l,u,i,t,o,r,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function h(n){var l=n.parentNode;l&&l.removeChild(n)}function v(l,u,i){var t,o,r,f={};for(r in u)\"key\"==r?t=u[r]:\"ref\"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),\"function\"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return y(l,f,t,o,null)}function y(n,i,t,o,r){var f={type:n,props:i,key:t,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null==r&&null!=l.vnode&&l.vnode(f),f}function p(){return{current:null}}function d(n){return n.children}function _(n,l){this.props=n,this.context=l}function k(n,l){if(null==l)return n.__?k(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return\"function\"==typeof n.type?k(n):null}function b(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return b(n)}}function m(n){(!n.__d&&(n.__d=!0)&&t.push(n)&&!g.__r++||r!==l.debounceRendering)&&((r=l.debounceRendering)||o)(g)}function g(){for(var n;g.__r=t.length;)n=t.sort(function(n,l){return n.__v.__b-l.__v.__b}),t=[],n.some(function(n){var l,u,i,t,o,r;n.__d&&(o=(t=(l=n).__v).__e,(r=l.__P)&&(u=[],(i=a({},t)).__v=t.__v+1,j(r,t,i,l.__n,void 0!==r.ownerSVGElement,null!=t.__h?[o]:null,u,null==o?k(t):o,t.__h),z(u,t),t.__e!=o&&b(t)))})}function w(n,l,u,i,t,o,r,f,s,a){var h,v,p,_,b,m,g,w=i&&i.__k||c,A=w.length;for(u.__k=[],h=0;h<l.length;h++)if(null!=(_=u.__k[h]=null==(_=l[h])||\"boolean\"==typeof _?null:\"string\"==typeof _||\"number\"==typeof _||\"bigint\"==typeof _?y(null,_,null,null,_):Array.isArray(_)?y(d,{children:_},null,null,null):_.__b>0?y(_.type,_.props,_.key,null,_.__v):_)){if(_.__=u,_.__b=u.__b+1,null===(p=w[h])||p&&_.key==p.key&&_.type===p.type)w[h]=void 0;else for(v=0;v<A;v++){if((p=w[v])&&_.key==p.key&&_.type===p.type){w[v]=void 0;break}p=null}j(n,_,p=p||e,t,o,r,f,s,a),b=_.__e,(v=_.ref)&&p.ref!=v&&(g||(g=[]),p.ref&&g.push(p.ref,null,_),g.push(v,_.__c||b,_)),null!=b?(null==m&&(m=b),\"function\"==typeof _.type&&_.__k===p.__k?_.__d=s=x(_,s,n):s=P(n,_,p,w,b,s),\"function\"==typeof u.type&&(u.__d=s)):s&&p.__e==s&&s.parentNode!=n&&(s=k(p))}for(u.__e=m,h=A;h--;)null!=w[h]&&(\"function\"==typeof u.type&&null!=w[h].__e&&w[h].__e==u.__d&&(u.__d=k(i,h+1)),N(w[h],w[h]));if(g)for(h=0;h<g.length;h++)M(g[h],g[++h],g[++h])}function x(n,l,u){for(var i,t=n.__k,o=0;t&&o<t.length;o++)(i=t[o])&&(i.__=n,l=\"function\"==typeof i.type?x(i,l,u):P(u,i,i,t,i.__e,l));return l}function A(n,l){return l=l||[],null==n||\"boolean\"==typeof n||(Array.isArray(n)?n.some(function(n){A(n,l)}):l.push(n)),l}function P(n,l,u,i,t,o){var r,f,e;if(void 0!==l.__d)r=l.__d,l.__d=void 0;else if(null==u||t!=o||null==t.parentNode)n:if(null==o||o.parentNode!==n)n.appendChild(t),r=null;else{for(f=o,e=0;(f=f.nextSibling)&&e<i.length;e+=2)if(f==t)break n;n.insertBefore(t,o),r=o}return void 0!==r?r:t.nextSibling}function C(n,l,u,i,t){var o;for(o in u)\"children\"===o||\"key\"===o||o in l||H(n,o,null,u[o],i);for(o in l)t&&\"function\"!=typeof l[o]||\"children\"===o||\"key\"===o||\"value\"===o||\"checked\"===o||u[o]===l[o]||H(n,o,l[o],u[o],i)}function $(n,l,u){\"-\"===l[0]?n.setProperty(l,u):n[l]=null==u?\"\":\"number\"!=typeof u||s.test(l)?u:u+\"px\"}function H(n,l,u,i,t){var o;n:if(\"style\"===l)if(\"string\"==typeof u)n.style.cssText=u;else{if(\"string\"==typeof i&&(n.style.cssText=i=\"\"),i)for(l in i)u&&l in u||$(n.style,l,\"\");if(u)for(l in u)i&&u[l]===i[l]||$(n.style,l,u[l])}else if(\"o\"===l[0]&&\"n\"===l[1])o=l!==(l=l.replace(/Capture$/,\"\")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=u,u?i||n.addEventListener(l,o?T:I,o):n.removeEventListener(l,o?T:I,o);else if(\"dangerouslySetInnerHTML\"!==l){if(t)l=l.replace(/xlink(H|:h)/,\"h\").replace(/sName$/,\"s\");else if(\"href\"!==l&&\"list\"!==l&&\"form\"!==l&&\"tabIndex\"!==l&&\"download\"!==l&&l in n)try{n[l]=null==u?\"\":u;break n}catch(n){}\"function\"==typeof u||(null!=u&&(!1!==u||\"a\"===l[0]&&\"r\"===l[1])?n.setAttribute(l,u):n.removeAttribute(l))}}function I(n){this.l[n.type+!1](l.event?l.event(n):n)}function T(n){this.l[n.type+!0](l.event?l.event(n):n)}function j(n,u,i,t,o,r,f,e,c){var s,h,v,y,p,k,b,m,g,x,A,P,C,$=u.type;if(void 0!==u.constructor)return null;null!=i.__h&&(c=i.__h,e=u.__e=i.__e,u.__h=null,r=[e]),(s=l.__b)&&s(u);try{n:if(\"function\"==typeof $){if(m=u.props,g=(s=$.contextType)&&t[s.__c],x=s?g?g.props.value:s.__:t,i.__c?b=(h=u.__c=i.__c).__=h.__E:(\"prototype\"in $&&$.prototype.render?u.__c=h=new $(m,x):(u.__c=h=new _(m,x),h.constructor=$,h.render=O),g&&g.sub(h),h.props=m,h.state||(h.state={}),h.context=x,h.__n=t,v=h.__d=!0,h.__h=[]),null==h.__s&&(h.__s=h.state),null!=$.getDerivedStateFromProps&&(h.__s==h.state&&(h.__s=a({},h.__s)),a(h.__s,$.getDerivedStateFromProps(m,h.__s))),y=h.props,p=h.state,v)null==$.getDerivedStateFromProps&&null!=h.componentWillMount&&h.componentWillMount(),null!=h.componentDidMount&&h.__h.push(h.componentDidMount);else{if(null==$.getDerivedStateFromProps&&m!==y&&null!=h.componentWillReceiveProps&&h.componentWillReceiveProps(m,x),!h.__e&&null!=h.shouldComponentUpdate&&!1===h.shouldComponentUpdate(m,h.__s,x)||u.__v===i.__v){h.props=m,h.state=h.__s,u.__v!==i.__v&&(h.__d=!1),h.__v=u,u.__e=i.__e,u.__k=i.__k,u.__k.forEach(function(n){n&&(n.__=u)}),h.__h.length&&f.push(h);break n}null!=h.componentWillUpdate&&h.componentWillUpdate(m,h.__s,x),null!=h.componentDidUpdate&&h.__h.push(function(){h.componentDidUpdate(y,p,k)})}if(h.context=x,h.props=m,h.__v=u,h.__P=n,A=l.__r,P=0,\"prototype\"in $&&$.prototype.render)h.state=h.__s,h.__d=!1,A&&A(u),s=h.render(h.props,h.state,h.context);else do{h.__d=!1,A&&A(u),s=h.render(h.props,h.state,h.context),h.state=h.__s}while(h.__d&&++P<25);h.state=h.__s,null!=h.getChildContext&&(t=a(a({},t),h.getChildContext())),v||null==h.getSnapshotBeforeUpdate||(k=h.getSnapshotBeforeUpdate(y,p)),C=null!=s&&s.type===d&&null==s.key?s.props.children:s,w(n,Array.isArray(C)?C:[C],u,i,t,o,r,f,e,c),h.base=u.__e,u.__h=null,h.__h.length&&f.push(h),b&&(h.__E=h.__=null),h.__e=!1}else null==r&&u.__v===i.__v?(u.__k=i.__k,u.__e=i.__e):u.__e=L(i.__e,u,i,t,o,r,f,c);(s=l.diffed)&&s(u)}catch(n){u.__v=null,(c||null!=r)&&(u.__e=e,u.__h=!!c,r[r.indexOf(e)]=null),l.__e(n,u,i)}}function z(n,u){l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function L(l,u,i,t,o,r,f,c){var s,a,v,y=i.props,p=u.props,d=u.type,_=0;if(\"svg\"===d&&(o=!0),null!=r)for(;_<r.length;_++)if((s=r[_])&&\"setAttribute\"in s==!!d&&(d?s.localName===d:3===s.nodeType)){l=s,r[_]=null;break}if(null==l){if(null===d)return document.createTextNode(p);l=o?document.createElementNS(\"http://www.w3.org/2000/svg\",d):document.createElement(d,p.is&&p),r=null,c=!1}if(null===d)y===p||c&&l.data===p||(l.data=p);else{if(r=r&&n.call(l.childNodes),a=(y=i.props||e).dangerouslySetInnerHTML,v=p.dangerouslySetInnerHTML,!c){if(null!=r)for(y={},_=0;_<l.attributes.length;_++)y[l.attributes[_].name]=l.attributes[_].value;(v||a)&&(v&&(a&&v.__html==a.__html||v.__html===l.innerHTML)||(l.innerHTML=v&&v.__html||\"\"))}if(C(l,p,y,o,c),v)u.__k=[];else if(_=u.props.children,w(l,Array.isArray(_)?_:[_],u,i,t,o&&\"foreignObject\"!==d,r,f,r?r[0]:i.__k&&k(i,0),c),null!=r)for(_=r.length;_--;)null!=r[_]&&h(r[_]);c||(\"value\"in p&&void 0!==(_=p.value)&&(_!==l.value||\"progress\"===d&&!_||\"option\"===d&&_!==y.value)&&H(l,\"value\",_,y.value,!1),\"checked\"in p&&void 0!==(_=p.checked)&&_!==l.checked&&H(l,\"checked\",_,y.checked,!1))}return l}function M(n,u,i){try{\"function\"==typeof n?n(u):n.current=u}catch(n){l.__e(n,i)}}function N(n,u,i){var t,o;if(l.unmount&&l.unmount(n),(t=n.ref)&&(t.current&&t.current!==n.__e||M(t,null,u)),null!=(t=n.__c)){if(t.componentWillUnmount)try{t.componentWillUnmount()}catch(n){l.__e(n,u)}t.base=t.__P=null}if(t=n.__k)for(o=0;o<t.length;o++)t[o]&&N(t[o],u,\"function\"!=typeof n.type);i||null==n.__e||h(n.__e),n.__e=n.__d=void 0}function O(n,l,u){return this.constructor(n,u)}function S(u,i,t){var o,r,f;l.__&&l.__(u,i),r=(o=\"function\"==typeof t)?null:t&&t.__k||i.__k,f=[],j(i,u=(!o&&t||i).__k=v(d,null,[u]),r||e,e,void 0!==i.ownerSVGElement,!o&&t?[t]:r?null:i.firstChild?n.call(i.childNodes):null,f,!o&&t?t:r?r.__e:i.firstChild,o),z(f,u)}function q(n,l){S(n,l,q)}function B(l,u,i){var t,o,r,f=a({},l.props);for(r in u)\"key\"==r?t=u[r]:\"ref\"==r?o=u[r]:f[r]=u[r];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),y(l.type,f,t||l.key,o||l.ref,null)}function D(n,l){var u={__c:l=\"__cC\"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,i;return this.getChildContext||(u=[],(i={})[l]=this,this.getChildContext=function(){return i},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(m)},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=c.slice,l={__e:function(n,l,u,i){for(var t,o,r;l=l.__;)if((t=l.__c)&&!t.__)try{if((o=t.constructor)&&null!=o.getDerivedStateFromError&&(t.setState(o.getDerivedStateFromError(n)),r=t.__d),null!=t.componentDidCatch&&(t.componentDidCatch(n,i||{}),r=t.__d),r)return t.__E=t}catch(l){n=l}throw n}},u=0,i=function(n){return null!=n&&void 0===n.constructor},_.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=a({},this.state),\"function\"==typeof n&&(n=n(a({},u),this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),m(this))},_.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),m(this))},_.prototype.render=d,t=[],o=\"function\"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,g.__r=0,f=0;export{S as render,q as hydrate,v as createElement,v as h,d as Fragment,p as createRef,i as isValidElement,_ as Component,B as cloneElement,D as createContext,A as toChildArray,l as options};\n//# sourceMappingURL=preact.module.js.map\n","import{options as n,Fragment as o,Component as e}from\"preact\";function t(o,e){return n.__a&&n.__a(e),o}\"undefined\"!=typeof window&&window.__PREACT_DEVTOOLS__&&window.__PREACT_DEVTOOLS__.attachPreact(\"10.8.1\",n,{Fragment:o,Component:e});export{t as addHookName};\n//# sourceMappingURL=devtools.module.js.map\n","import{options as n,Fragment as t,Component as e}from\"preact\";import\"preact/devtools\";var o={};function r(){o={}}function a(n){return n.type===t?\"Fragment\":\"function\"==typeof n.type?n.type.displayName||n.type.name:\"string\"==typeof n.type?n.type:\"#text\"}var i=[],s=[];function c(){return i.length>0?i[i.length-1]:null}var l=!1;function u(n){return\"function\"==typeof n.type&&n.type!=t}function f(n){for(var t=[n],e=n;null!=e.__o;)t.push(e.__o),e=e.__o;return t.reduce(function(n,t){n+=\" in \"+a(t);var e=t.__source;return e?n+=\" (at \"+e.fileName+\":\"+e.lineNumber+\")\":l||(l=!0,console.warn(\"Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.\")),n+\"\\n\"},\"\")}var p=\"function\"==typeof WeakMap,d=e.prototype.setState;e.prototype.setState=function(n,t){return null==this.__v?null==this.state&&console.warn('Calling \"this.setState\" inside the constructor of a component is a no-op and might be a bug in your application. Instead, set \"this.state = {}\" directly.\\n\\n'+f(c())):null==this.__P&&console.warn('Can\\'t call \"this.setState\" on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.\\n\\n'+f(this.__v)),d.call(this,n,t)};var h=e.prototype.forceUpdate;function y(n){var t=n.props,e=a(n),o=\"\";for(var r in t)if(t.hasOwnProperty(r)&&\"children\"!==r){var i=t[r];\"function\"==typeof i&&(i=\"function \"+(i.displayName||i.name)+\"() {}\"),i=Object(i)!==i||i.toString?i+\"\":Object.prototype.toString.call(i),o+=\" \"+r+\"=\"+JSON.stringify(i)}var s=t.children;return\"<\"+e+o+(s&&s.length?\">..</\"+e+\">\":\" />\")}e.prototype.forceUpdate=function(n){return null==this.__v?console.warn('Calling \"this.forceUpdate\" inside the constructor of a component is a no-op and might be a bug in your application.\\n\\n'+f(c())):null==this.__P&&console.warn('Can\\'t call \"this.forceUpdate\" on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.\\n\\n'+f(this.__v)),h.call(this,n)},function(){!function(){var t=n.__b,e=n.diffed,o=n.__,r=n.vnode,a=n.__r;n.diffed=function(n){u(n)&&s.pop(),i.pop(),e&&e(n)},n.__b=function(n){u(n)&&i.push(n),t&&t(n)},n.__=function(n,t){s=[],o&&o(n,t)},n.vnode=function(n){n.__o=s.length>0?s[s.length-1]:null,r&&r(n)},n.__r=function(n){u(n)&&s.push(n),a&&a(n)}}();var t=!1,e=n.__b,r=n.diffed,c=n.vnode,l=n.__e,d=n.__,h=n.__h,m=p?{useEffect:new WeakMap,useLayoutEffect:new WeakMap,lazyPropTypes:new WeakMap}:null,v=[];n.__e=function(n,t,e,o){if(t&&t.__c&&\"function\"==typeof n.then){var r=n;n=new Error(\"Missing Suspense. The throwing component was: \"+a(t));for(var i=t;i;i=i.__)if(i.__c&&i.__c.__c){n=r;break}if(n instanceof Error)throw n}try{(o=o||{}).componentStack=f(t),l(n,t,e,o),\"function\"!=typeof n.then&&setTimeout(function(){throw n})}catch(n){throw n}},n.__=function(n,t){if(!t)throw new Error(\"Undefined parent passed to render(), this is the second argument.\\nCheck if the element is available in the DOM/has the correct id.\");var e;switch(t.nodeType){case 1:case 11:case 9:e=!0;break;default:e=!1}if(!e){var o=a(n);throw new Error(\"Expected a valid HTML node as a second argument to render.\\tReceived \"+t+\" instead: render(<\"+o+\" />, \"+t+\");\")}d&&d(n,t)},n.__b=function(n){var r=n.type,i=function n(t){return t?\"function\"==typeof t.type?n(t.__):t:{}}(n.__);if(t=!0,void 0===r)throw new Error(\"Undefined component passed to createElement()\\n\\nYou likely forgot to export your component or might have mixed up default and named imports\"+y(n)+\"\\n\\n\"+f(n));if(null!=r&&\"object\"==typeof r){if(void 0!==r.__k&&void 0!==r.__e)throw new Error(\"Invalid type passed to createElement(): \"+r+\"\\n\\nDid you accidentally pass a JSX literal as JSX twice?\\n\\n let My\"+a(n)+\" = \"+y(r)+\";\\n let vnode = <My\"+a(n)+\" />;\\n\\nThis usually happens when you export a JSX literal and not the component.\\n\\n\"+f(n));throw new Error(\"Invalid type passed to createElement(): \"+(Array.isArray(r)?\"array\":r))}if(\"thead\"!==r&&\"tfoot\"!==r&&\"tbody\"!==r||\"table\"===i.type?\"tr\"===r&&\"thead\"!==i.type&&\"tfoot\"!==i.type&&\"tbody\"!==i.type&&\"table\"!==i.type?console.error(\"Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot/table> parent.\"+y(n)+\"\\n\\n\"+f(n)):\"td\"===r&&\"tr\"!==i.type?console.error(\"Improper nesting of table. Your <td> should have a <tr> parent.\"+y(n)+\"\\n\\n\"+f(n)):\"th\"===r&&\"tr\"!==i.type&&console.error(\"Improper nesting of table. Your <th> should have a <tr>.\"+y(n)+\"\\n\\n\"+f(n)):console.error(\"Improper nesting of table. Your <thead/tbody/tfoot> should have a <table> parent.\"+y(n)+\"\\n\\n\"+f(n)),void 0!==n.ref&&\"function\"!=typeof n.ref&&\"object\"!=typeof n.ref&&!(\"$$typeof\"in n))throw new Error('Component\\'s \"ref\" property should be a function, or an object created by createRef(), but got ['+typeof n.ref+\"] instead\\n\"+y(n)+\"\\n\\n\"+f(n));if(\"string\"==typeof n.type)for(var s in n.props)if(\"o\"===s[0]&&\"n\"===s[1]&&\"function\"!=typeof n.props[s]&&null!=n.props[s])throw new Error(\"Component's \\\"\"+s+'\" property should be a function, but got ['+typeof n.props[s]+\"] instead\\n\"+y(n)+\"\\n\\n\"+f(n));if(\"function\"==typeof n.type&&n.type.propTypes){if(\"Lazy\"===n.type.displayName&&m&&!m.lazyPropTypes.has(n.type)){var c=\"PropTypes are not supported on lazy(). Use propTypes on the wrapped component itself. \";try{var l=n.type();m.lazyPropTypes.set(n.type,!0),console.warn(c+\"Component wrapped in lazy() is \"+a(l))}catch(n){console.warn(c+\"We will log the wrapped component's name once it is loaded.\")}}var u=n.props;n.type.__f&&delete(u=function(n,t){for(var e in t)n[e]=t[e];return n}({},u)).ref,function(n,t,e,r,a){Object.keys(n).forEach(function(e){var i;try{i=n[e](t,e,r,\"prop\",null,\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\")}catch(n){i=n}!i||i.message in o||(o[i.message]=!0,console.error(\"Failed prop type: \"+i.message+(a&&\"\\n\"+a()||\"\")))})}(n.type.propTypes,u,0,a(n),function(){return f(n)})}e&&e(n)},n.__h=function(n,e,o){if(!n||!t)throw new Error(\"Hook can only be invoked from render methods.\");h&&h(n,e,o)};var b=function(n,t){return{get:function(){var e=\"get\"+n+t;v&&v.indexOf(e)<0&&(v.push(e),console.warn(\"getting vnode.\"+n+\" is deprecated, \"+t))},set:function(){var e=\"set\"+n+t;v&&v.indexOf(e)<0&&(v.push(e),console.warn(\"setting vnode.\"+n+\" is not allowed, \"+t))}}},w={nodeName:b(\"nodeName\",\"use vnode.type\"),attributes:b(\"attributes\",\"use vnode.props\"),children:b(\"children\",\"use vnode.props.children\")},g=Object.create({},w);n.vnode=function(n){var t=n.props;if(null!==n.type&&null!=t&&(\"__source\"in t||\"__self\"in t)){var e=n.props={};for(var o in t){var r=t[o];\"__source\"===o?n.__source=r:\"__self\"===o?n.__self=r:e[o]=r}}n.__proto__=g,c&&c(n)},n.diffed=function(n){if(n.__k&&n.__k.forEach(function(t){if(t&&void 0===t.type){delete t.__,delete t.__b;var e=Object.keys(t).join(\",\");throw new Error(\"Objects are not valid as a child. Encountered an object with the keys {\"+e+\"}.\\n\\n\"+f(n))}}),t=!1,r&&r(n),null!=n.__k)for(var e=[],o=0;o<n.__k.length;o++){var a=n.__k[o];if(a&&null!=a.key){var i=a.key;if(-1!==e.indexOf(i)){console.error('Following component has two or more children with the same key attribute: \"'+i+'\". This may cause glitches and misbehavior in rendering process. Component: \\n\\n'+y(n)+\"\\n\\n\"+f(n));break}e.push(i)}}}}();export{r as resetPropWarnings};\n//# sourceMappingURL=debug.module.js.map\n","/*!\n Copyright (c) 2018 Jed Watson.\n Licensed under the MIT License (MIT), see\n http://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames() {\n\t\tvar classes = [];\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (!arg) continue;\n\n\t\t\tvar argType = typeof arg;\n\n\t\t\tif (argType === 'string' || argType === 'number') {\n\t\t\t\tclasses.push(arg);\n\t\t\t} else if (Array.isArray(arg)) {\n\t\t\t\tif (arg.length) {\n\t\t\t\t\tvar inner = classNames.apply(null, arg);\n\t\t\t\t\tif (inner) {\n\t\t\t\t\t\tclasses.push(inner);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (argType === 'object') {\n\t\t\t\tif (arg.toString === Object.prototype.toString) {\n\t\t\t\t\tfor (var key in arg) {\n\t\t\t\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\t\t\t\tclasses.push(key);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tclasses.push(arg.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn classes.join(' ');\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import{options as n}from\"preact\";var t,u,r,o,i=0,c=[],f=n.__b,e=n.__r,a=n.diffed,v=n.__c,l=n.unmount;function m(t,r){n.__h&&n.__h(u,t,i||r),i=0;var o=u.__H||(u.__H={__:[],__h:[]});return t>=o.__.length&&o.__.push({}),o.__[t]}function d(n){return i=1,p(z,n)}function p(n,r,o){var i=m(t++,2);return i.t=n,i.__c||(i.__=[o?o(r):z(void 0,r),function(n){var t=i.t(i.__[0],n);i.__[0]!==t&&(i.__=[t,i.__[1]],i.__c.setState({}))}],i.__c=u),i.__}function y(r,o){var i=m(t++,3);!n.__s&&w(i.__H,o)&&(i.__=r,i.u=o,u.__H.__h.push(i))}function h(r,o){var i=m(t++,4);!n.__s&&w(i.__H,o)&&(i.__=r,i.u=o,u.__h.push(i))}function s(n){return i=5,A(function(){return{current:n}},[])}function _(n,t,u){i=6,h(function(){return\"function\"==typeof n?(n(t()),function(){return n(null)}):n?(n.current=t(),function(){return n.current=null}):void 0},null==u?u:u.concat(n))}function A(n,u){var r=m(t++,7);return w(r.__H,u)?(r.o=n(),r.u=u,r.__h=n,r.o):r.__}function F(n,t){return i=8,A(function(){return n},t)}function T(n){var r=u.context[n.__c],o=m(t++,9);return o.c=n,r?(null==o.__&&(o.__=!0,r.sub(u)),r.props.value):n.__}function q(t,u){n.useDebugValue&&n.useDebugValue(u?u(t):t)}function x(n){var r=m(t++,10),o=d();return r.__=n,u.componentDidCatch||(u.componentDidCatch=function(n){r.__&&r.__(n),o[1](n)}),[o[0],function(){o[1](void 0)}]}function b(){for(var t;t=c.shift();)if(t.__P)try{t.__H.__h.forEach(j),t.__H.__h.forEach(k),t.__H.__h=[]}catch(u){t.__H.__h=[],n.__e(u,t.__v)}}n.__b=function(n){u=null,f&&f(n)},n.__r=function(n){e&&e(n),t=0;var o=(u=n.__c).__H;o&&(r===u?(o.__h=[],u.__h=[],o.__.forEach(function(n){n.o=n.u=void 0})):(o.__.forEach(function(n){n.u&&(n.__H=n.u),n.o&&(n.__=n.o),n.o=n.u=void 0}),o.__h.forEach(j),o.__h.forEach(k),o.__h=[])),r=u},n.diffed=function(t){a&&a(t);var i=t.__c;i&&i.__H&&i.__H.__h.length&&(1!==c.push(i)&&o===n.requestAnimationFrame||((o=n.requestAnimationFrame)||function(n){var t,u=function(){clearTimeout(r),g&&cancelAnimationFrame(t),setTimeout(n)},r=setTimeout(u,100);g&&(t=requestAnimationFrame(u))})(b)),u=null,r=null},n.__c=function(t,u){u.some(function(t){try{t.__H&&t.__H.__.forEach(function(n){n.u&&(n.__H=n.u),n.o&&(n.__=n.o),n.o=n.u=void 0}),t.__h.forEach(j),t.__h=t.__h.filter(function(n){return!n.__||k(n)})}catch(r){u.some(function(n){n.__h&&(n.__h=[])}),u=[],n.__e(r,t.__v)}}),v&&v(t,u)},n.unmount=function(t){l&&l(t);var u,r=t.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{j(n)}catch(n){u=n}}),u&&n.__e(u,r.__v))};var g=\"function\"==typeof requestAnimationFrame;function j(n){var t=u,r=n.__c;\"function\"==typeof r&&(n.__c=void 0,r()),u=t}function k(n){var t=u;n.__c=n.__(),u=t}function w(n,t){return!n||n.length!==t.length||t.some(function(t,u){return t!==n[u]})}function z(n,t){return\"function\"==typeof t?t(n):t}export{d as useState,p as useReducer,y as useEffect,h as useLayoutEffect,s as useRef,_ as useImperativeHandle,A as useMemo,F as useCallback,T as useContext,q as useDebugValue,x as useErrorBoundary};\n//# sourceMappingURL=hooks.module.js.map\n","import{options as r,Fragment as _}from\"preact\";export{Fragment}from\"preact\";var o=0;function e(_,e,n,t,f){var l,s,u={};for(s in e)\"ref\"==s?l=e[s]:u[s]=e[s];var a={type:_,props:u,key:n,ref:l,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:--o,__source:f,__self:t};if(\"function\"==typeof _&&(l=_.defaultProps))for(s in l)void 0===u[s]&&(u[s]=l[s]);return r.vnode&&r.vnode(a),a}export{e as jsx,e as jsxs,e as jsxDEV};\n//# sourceMappingURL=jsxRuntime.module.js.map\n","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/SvgIcon.js\";\nimport classnames from 'classnames';\nimport { useLayoutEffect, useRef } from 'preact/hooks';\n/**\n * Object mapping icon names to SVG markup.\n *\n * @typedef {Map<string|symbol, string>} IconMap\n */\n\n/**\n * @template T\n * @typedef {import(\"preact/hooks\").Ref<T>} Ref\n */\n\n/**\n * Map of icon name to SVG data.\n *\n * @type {IconMap}\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst iconRegistry = new Map();\n/**\n * @typedef SvgIconProps\n * @prop {string|symbol} name - The name of the icon to display.\n * The name must match a name that has already been registered using the\n * `registerIcon` or `registerIcons` functions.\n * @prop {string} [className] - A CSS class to apply to the `<svg>` element.\n * @prop {boolean} [inline] - Apply a style allowing for inline display of icon wrapper.\n * @prop {string} [title] - Optional title attribute to apply to the SVG's containing `span`.\n */\n\n/**\n * Component that renders icons using inline `<svg>` elements.\n * This enables their appearance to be customized via CSS.\n *\n * This matches the way we do icons on the website, see\n * https://github.com/hypothesis/h/pull/3675\n *\n * @param {SvgIconProps} props\n */\n\nexport function SvgIcon({\n name,\n className = '',\n inline = false,\n title = ''\n}) {\n const markup = iconRegistry.get(name);\n\n if (!markup) {\n throw new Error(`Icon \"${name.toString()}\" is not registered`);\n }\n\n const element =\n /** @type {{ current: HTMLElement }} */\n useRef();\n useLayoutEffect(() => {\n const svg = element.current.querySelector('svg'); // The icon should always contain an `<svg>` element, but check here as we\n // don't validate the markup when it is registered.\n\n if (svg) {\n svg.setAttribute('class', className);\n }\n }, [className, // `markup` is a dependency of this effect because the SVG is replaced if\n // it changes.\n markup]);\n const spanProps = {};\n\n if (title) {\n spanProps.title = title;\n }\n\n return _jsxDEV(\"span\", {\n className: classnames('Hyp-SvgIcon', {\n 'Hyp-SvgIcon--inline': inline\n }),\n dangerouslySetInnerHTML: {\n __html: markup\n },\n ref: element,\n ...spanProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 69,\n columnNumber: 5\n }, this);\n}\n/**\n * Register an icon for use with the `SvgIcon` component.\n *\n * Returns a symbol that can be passed as the `name` prop to `SvgIcon` in order\n * to render this icon.\n *\n * @param {string|symbol} name - A name for this icon\n * @param {string} markup - SVG markup for the icon\n * @return {symbol}\n */\n\nexport function registerIcon(name, markup) {\n const key = typeof name === 'string' ? Symbol(name) : name;\n iconRegistry.set(key, markup);\n return key;\n}\n/**\n * Register icons for use with the `SvgIcon` component.\n *\n * @deprecated Prefer the `registerIcon` function instead which will return a\n * key that does not conflict with existing icons.\n *\n * @param {Record<string, string>} icons\n * @param {Object} options\n * @param {boolean} [options.reset] - If `true`, remove existing registered icons.\n */\n\nexport function registerIcons(icons, {\n reset = false\n} = {}) {\n if (reset) {\n iconRegistry.clear();\n }\n\n for (let [key, value] of Object.entries(icons)) {\n iconRegistry.set(key, value);\n }\n}\n/**\n * Return the currently available icons.\n *\n * To register icons, don't mutate this directly but call `registerIcons`\n * instead.\n *\n * @return {IconMap}\n */\n\nexport function availableIcons() {\n return iconRegistry;\n}\n//# sourceMappingURL=SvgIcon.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/buttons.js\";\nimport classnames from 'classnames';\nimport { SvgIcon } from './SvgIcon';\n/**\n * @typedef ButtonProps\n * @prop {import('preact').Ref<HTMLButtonElement>} [buttonRef]\n * @prop {string} [classes] - Optional CSS class name(s) to use _in addition_\n * to the button component's own clases\n * @prop {string} [className] - Optional CSS class name that will _replace_\n * the button component's own classes\n * @prop {string|symbol} [icon] - Name of `SvgIcon` to render in the button\n * @prop {'left'|'right'} [iconPosition] - Icon positioned to left or to\n * right of button text\n * @prop {boolean} [expanded] - Is the element associated with this button\n * expanded (set `aria-expanded`)\n * @prop {never} [aria-expanded] - Use `expanded` prop instead\n * @prop {boolean} [pressed] - Is this button currently \"active?\" (set\n * `aria-pressed` or `aria-selected` depending on button `role`)\n * @prop {never} [aria-pressed] - Use `pressed` prop instead\n * @prop {'small'|'medium'|'large'} [size='medium'] - Relative button size:\n * affects padding\n * @prop {string} [title] - Button title; used for `aria-label` attribute\n * @prop {never} [aria-label] - Use `title` prop instead\n * @prop {'normal'|'primary'|'light'|'dark'} [variant='normal'] - For styling: element variant\n */\n\n/**\n * Fold in HTML button prop definitions into ButtonProps, but ignore `size` because it's inherited\n * from HTMLElement and conflicts with the _ButtonProps.size prop above. Ignore `icon`\n * because it is typed to `string` only and we need to be able to accept {string|symbol}\n *\n * @typedef {Omit<import('preact').JSX.HTMLAttributes<HTMLButtonElement>, 'size' | 'icon'> } HTMLButtonElementProps\n * @typedef {ButtonProps & HTMLButtonElementProps} ButtonBaseProps\n */\n\n/**\n * @typedef IconButtonBaseProps\n * @prop {string|symbol} icon - Icon is required for icon buttons\n * @prop {string} title - Title is required for icon buttons\n * @prop {never} [children] - children are not allowed (use LabeledButton instead)\n */\n\n/**\n * @typedef {ButtonBaseProps & IconButtonBaseProps} IconButtonProps\n */\n\n/**\n * @param {ButtonBaseProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\n\nfunction ButtonBase({\n // Custom props.\n buttonRef,\n classes,\n className,\n icon,\n iconPosition = 'left',\n size = 'medium',\n variant = 'normal',\n expanded,\n pressed,\n // Standard <button> props.\n type = 'button',\n ...restProps\n}) {\n var _restProps$role;\n\n const role = (_restProps$role = restProps === null || restProps === void 0 ? void 0 : restProps.role) !== null && _restProps$role !== void 0 ? _restProps$role : 'button';\n const ariaProps = {\n 'aria-label': restProps.title\n }; // aria-pressed and aria-expanded are not allowed for buttons with\n // an aria role of `tab`. Instead, the aria-selected attribute is expected.\n\n if (role === 'tab') {\n ariaProps['aria-selected'] = pressed;\n } else {\n ariaProps['aria-pressed'] = pressed;\n ariaProps['aria-expanded'] = expanded;\n }\n\n return _jsxDEV(\"button\", {\n ref: buttonRef,\n className: classnames(className, `${className}--${size}`, `${className}--${variant}`, {\n [`${className}--icon-${iconPosition}`]: icon\n }, classes),\n type: type,\n ...ariaProps,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 82,\n columnNumber: 5\n }, this);\n}\n/**\n * An icon-only button\n *\n * @param {IconButtonProps} props\n */\n\n\nexport function IconButton({\n className = 'Hyp-IconButton',\n ...restProps\n}) {\n const {\n icon\n } = restProps;\n return _jsxDEV(ButtonBase, {\n className: className,\n ...restProps,\n children: _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 109,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 108,\n columnNumber: 5\n }, this);\n}\n/**\n * A labeled button, with or without an icon\n *\n * @param {ButtonBaseProps} props\n */\n\nexport function LabeledButton({\n children,\n className = 'Hyp-LabeledButton',\n ...restProps\n}) {\n const {\n icon,\n iconPosition = 'left'\n } = restProps;\n return _jsxDEV(ButtonBase, {\n className: className,\n ...restProps,\n children: [icon && iconPosition === 'left' && _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 127,\n columnNumber: 43\n }, this), children, icon && iconPosition === 'right' && _jsxDEV(SvgIcon, {\n name: icon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 129,\n columnNumber: 44\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 126,\n columnNumber: 5\n }, this);\n}\n/**\n * A button styled to appear as an HTML link (<a>)\n *\n * @param {ButtonBaseProps} props\n */\n\nexport function LinkButton(props) {\n return _jsxDEV(ButtonBase, {\n className: \"Hyp-LinkButton\",\n ...props\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 140,\n columnNumber: 10\n }, this);\n}\n//# sourceMappingURL=buttons.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Checkbox.js\";\n// @ts-ignore\nimport checkboxSVG from '../../images/icons/checkbox.svg';\nimport classnames from 'classnames';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the checkbox icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nimport { Fragment as _Fragment } from \"preact/jsx-dev-runtime\";\nconst checkboxIcon = registerIcon('checkbox', checkboxSVG);\n/**\n * @typedef CheckboxBaseProps\n * @prop {string} [classes] - Additional CSS classes to apply to the <input>\n * @prop {string} name - The `name` of the checkbox.\n * @prop {import('preact').Ref<HTMLInputElement>} [inputRef] - Access to the input\n * element in case a parent element wants for example to focus on it.\n * @prop {(checked: boolean) => void} [onToggle] - Callback when checkbox is\n * checked/unchecked\n * @prop {never} [type] - Type is always 'checkbox'\n * @prop {never} [children] - Children are not allowed\n *\n * The props for Checkbox component extends and narrows the attributes of the native input element.\n * `onToggle` event should only be associated to HTMLDetailsElement, but Preact is not very strict with types.\n * We omit the `onToggle` because it clashes with our definition.\n * @typedef {Omit<import('preact').JSX.HTMLAttributes<HTMLInputElement>, 'onToggle'> & CheckboxBaseProps} CheckboxProps\n */\n\n/**\n * @typedef LabeledCheckboxBaseProps\n * @prop {import('preact').ComponentChildren} children - Label text or elements\n * @prop {string} [containerClasses] - Optional additional classes for the container\n * <label> element\n *\n * @typedef {Omit<CheckboxProps, 'children'> & LabeledCheckboxBaseProps} LabeledCheckboxProps\n */\n\n/**\n * A checkbox input.\n *\n * A checkbox component is a combination of an <input> element and a sibling\n * <svg> element that is used for the visual appearance of the checkbox.\n *\n * @param {CheckboxProps} props\n */\n\nexport function Checkbox({\n classes = '',\n inputRef,\n onToggle,\n onClick,\n ...restProps\n}) {\n /**\n * @param {import('preact').JSX.TargetedMouseEvent<HTMLInputElement>} event\n * @this HTMLInputElement\n */\n function onPressed(event) {\n onToggle === null || onToggle === void 0 ? void 0 : onToggle(event.currentTarget.checked); // preact event handlers expects `this` context to be of type `never`\n // https://github.com/preactjs/preact/issues/3137\n\n onClick === null || onClick === void 0 ? void 0 : onClick.call(\n /** @type {never} */\n this, event);\n }\n\n return _jsxDEV(_Fragment, {\n children: [_jsxDEV(\"input\", {\n className: classnames('Hyp-Checkbox', classes),\n ref: inputRef,\n type: \"checkbox\",\n onClick: onPressed,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 64,\n columnNumber: 7\n }, this), _jsxDEV(SvgIcon, {\n className: \"hyp-svg-checkbox\",\n name: checkboxIcon\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 71,\n columnNumber: 7\n }, this)]\n }, void 0, true);\n}\n/**\n * A labeled checkbox input\n *\n * @param {LabeledCheckboxProps} props\n */\n\nexport function LabeledCheckbox({\n children,\n id,\n containerClasses = '',\n ...restProps\n}) {\n var _id;\n\n (_id = id) !== null && _id !== void 0 ? _id : id = restProps.name;\n return _jsxDEV(\"label\", {\n htmlFor: id,\n className: classnames('Hyp-LabeledCheckbox', containerClasses),\n children: [_jsxDEV(Checkbox, {\n id: id,\n ...restProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 93,\n columnNumber: 7\n }, this), _jsxDEV(\"span\", {\n \"data-testid\": \"label-text\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 94,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 89,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Checkbox.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Dialog.js\";\nimport classnames from 'classnames';\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'; // @ts-ignore\n\nimport cancelSVG from '../../images/icons/cancel.svg';\nimport { IconButton, LabeledButton } from './buttons';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the checkbox icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst cancelIcon = registerIcon('cancel', cancelSVG);\nlet idCounter = 0;\n/**\n * Return an element ID beginning with `prefix` that is unique per component instance.\n *\n * This avoids different instances of a component re-using the same ID.\n *\n * @param {string} prefix\n */\n\nfunction useUniqueId(prefix) {\n const [id] = useState(() => {\n ++idCounter;\n return `${prefix}-${idCounter}`;\n });\n return id;\n}\n/**\n * @typedef {import('preact').ComponentChildren} Children\n *\n * @typedef DialogProps\n * @prop {Children} [buttons] -\n * Additional `Button` elements to display at the bottom of the dialog.\n * A \"Cancel\" button is added automatically if the `onCancel` prop is set.\n * @prop {string} [cancelLabel] - Label for the cancel button\n * @prop {Children} children\n * @prop {string} [contentClass] - CSS class to apply to the dialog's content\n * @prop {string|symbol} [icon] - Name of optional icon to render in header\n * @prop {import(\"preact/hooks\").Ref<HTMLElement>|null} [initialFocus] -\n * Child element to focus when the dialog is rendered. If not provided,\n * the Dialog's container will be automatically focused on opening. Set to\n * `null` to opt out of automatic focus control.\n * @prop {() => void} [onCancel] -\n * A callback to invoke when the user cancels the dialog. If provided, a\n * \"Cancel\" button will be displayed.\n * @prop {'dialog'|'alertdialog'} [role] - The aria role for the dialog (defaults to\" dialog\")\n * @prop {string} title\n * @prop {boolean} [withCancelButton=true] - If `onCancel` is provided, render\n * a Cancel button as one of the Dialog's buttons (along with any other\n * `buttons`)\n * @prop {boolean} [withCloseButton=true] - If `onCancel` is provided, render\n * a close button (X icon) in the Dialog's header\n */\n\n/**\n * HTML control that can be disabled.\n *\n * @typedef {HTMLElement & { disabled: boolean }} InputElement\n */\n\n/**\n * Render a \"panel\"-like interface with a title and optional icon and/or\n * close button. Grabs focus on initial render, defaulting to the entire\n * Dialog container element, or `initialFocus` HTMLElement if provided.\n *\n * @param {DialogProps} props\n */\n\n\nexport function Dialog({\n buttons,\n cancelLabel = 'Cancel',\n children,\n contentClass,\n icon,\n initialFocus,\n onCancel,\n role = 'dialog',\n title,\n withCancelButton = true,\n withCloseButton = true\n}) {\n const dialogDescriptionId = useUniqueId('dialog-description');\n const dialogTitleId = useUniqueId('dialog-title');\n const rootEl =\n /** @type {{ current: HTMLDivElement }} */\n useRef();\n useEffect(() => {\n // Setting `initialFocus` to `null` opts out of focus handling\n if (initialFocus !== null) {\n const focusEl =\n /** @type {InputElement|null} */\n initialFocus === null || initialFocus === void 0 ? void 0 : initialFocus.current;\n\n if (focusEl && !focusEl.disabled) {\n focusEl.focus();\n } else {\n // The `initialFocus` prop has not been set, so use automatic focus handling.\n // Modern accessibility guidance is to focus the dialog itself rather than\n // trying to be smart about focusing a particular control within the\n // dialog.\n rootEl.current.focus();\n }\n } // We only want to run this effect once when the dialog is mounted.\n //\n // eslint-disable-next-line react-hooks/exhaustive-deps\n\n }, []); // Try to assign the dialog an accessible description, using the content of\n // the first paragraph of text in it.\n //\n // A limitation of this approach is that it doesn't update if the dialog's\n // content changes after the initial render.\n\n useLayoutEffect(() => {\n const description = rootEl.current.querySelector('p');\n\n if (description) {\n description.id = dialogDescriptionId;\n rootEl.current.setAttribute('aria-describedby', dialogDescriptionId);\n }\n }, [dialogDescriptionId]);\n const hasCancelButton = onCancel && withCancelButton;\n const hasCloseButton = onCancel && withCloseButton;\n const hasButtons = buttons || hasCancelButton;\n return _jsxDEV(\"div\", {\n \"aria-labelledby\": dialogTitleId,\n className: classnames('Hyp-Dialog', {\n 'Hyp-Dialog--closeable': hasCloseButton\n }, contentClass),\n ref: rootEl,\n role: role,\n tabIndex: -1,\n children: [_jsxDEV(\"header\", {\n className: \"Hyp-Dialog__header\",\n children: [icon && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__header-icon\",\n children: _jsxDEV(SvgIcon, {\n name: icon,\n title: title,\n \"data-testid\": \"header-icon\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 139,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 138,\n columnNumber: 11\n }, this), _jsxDEV(\"h2\", {\n className: \"Hyp-Dialog__title\",\n id: dialogTitleId,\n children: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 142,\n columnNumber: 9\n }, this), onCancel && withCloseButton && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__close\",\n children: _jsxDEV(IconButton, {\n \"data-testid\": \"close-button\",\n icon: cancelIcon,\n title: \"Close\",\n onClick: onCancel\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 147,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 146,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 136,\n columnNumber: 7\n }, this), children, hasButtons && _jsxDEV(\"div\", {\n className: \"Hyp-Dialog__actions\",\n children: [hasCancelButton && _jsxDEV(LabeledButton, {\n \"data-testid\": \"cancel-button\",\n onClick: onCancel,\n children: cancelLabel\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 160,\n columnNumber: 13\n }, this), buttons]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 158,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 125,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Dialog.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Icon.js\";\nimport classnames from 'classnames';\nimport { useLayoutEffect, useRef } from 'preact/hooks';\nimport { availableIcons } from './SvgIcon';\n/**\n * @template T\n * @typedef {import(\"preact/hooks\").Ref<T>} Ref\n */\n\n/**\n * @typedef IconProps\n * @prop {string|symbol} name - The name of the icon to display.\n * The name must match a name that has already been registered using the\n * `registerIcon` or `registerIcons` functions.\n * @prop {string} [classes] - CSS classes to apply to the `<svg>` element.\n * @prop {string} [containerClasses] - CSS classes to apply to wrapping element.\n * @prop {string} [title] - Optional title attribute to apply to the SVG's containing `span`.\n */\n\n/**\n * Component that renders icons using inline `<svg>` elements.\n *\n * @param {IconProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nexport function Icon({\n name,\n classes = '',\n containerClasses = '',\n title = ''\n}) {\n const registeredIcons = availableIcons();\n const markup = registeredIcons.get(name);\n\n if (!markup) {\n throw new Error(`Icon \"${name.toString()}\" is not registered`);\n }\n\n const element =\n /** @type {{ current: HTMLElement }} */\n useRef();\n useLayoutEffect(() => {\n const svg = element.current.querySelector('svg'); // The icon should always contain an `<svg>` element, but check here as we\n // don't validate the markup when it is registered.\n\n if (svg) {\n svg.setAttribute('class', classnames('Hyp-Icon', classes));\n }\n }, [classes, // `markup` is a dependency of this effect because the SVG is replaced if\n // it changes.\n markup]);\n const spanProps = {};\n\n if (title) {\n spanProps.title = title;\n }\n\n return _jsxDEV(\"span\", {\n className: containerClasses,\n dangerouslySetInnerHTML: {\n __html: markup\n },\n ref: element,\n ...spanProps\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 60,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Icon.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Link.js\";\nimport classnames from 'classnames';\n/**\n * @typedef {import('preact').ComponentChildren} Children\n *\n * @typedef LinkBaseProps\n * @prop {Children} children\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {import('preact').Ref<HTMLAnchorElement>} [linkRef] - Optional ref for\n * the rendered anchor element\n */\n\n/**\n * @typedef {LinkBaseProps & import('preact').JSX.HTMLAttributes<HTMLAnchorElement>} LinkProps\n */\n\n/**\n * Style and add some attributes to an anchor (`<a>`) element\n *\n * @param {LinkProps} props\n */\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nexport function Link({\n children,\n classes = '',\n linkRef,\n ...restProps\n}) {\n return _jsxDEV(\"a\", {\n className: classnames('Hyp-Link', classes),\n ref: linkRef,\n rel: \"noopener noreferrer\",\n ...restProps,\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 24,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Link.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Panel.js\";\nimport classnames from 'classnames'; // @ts-ignore\n\nimport cancelSVG from '../../images/icons/cancel.svg';\nimport { IconButton } from './buttons';\nimport { registerIcon, SvgIcon } from './SvgIcon'; // Register the cancel icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst cancelIcon = registerIcon('cancel', cancelSVG);\n/**\n * @typedef PanelProps\n * @prop {import(\"preact\").ComponentChildren} children\n * @prop {string} [icon] - Name of optional icon to render in header\n * @prop {() => void} [onClose] - handler for closing the panel; if provided,\n * will render a close button that invokes this onClick\n * @prop {string} title\n */\n\n/**\n * Render a \"panel\"-like interface with a title and optional icon and/or\n * close button.\n *\n * @param {PanelProps} props\n */\n\nexport function Panel({\n children,\n icon,\n onClose,\n title\n}) {\n const withCloseButton = !!onClose;\n return _jsxDEV(\"div\", {\n className: classnames('Hyp-Panel', {\n 'Hyp-Panel--closeable': withCloseButton\n }),\n children: [_jsxDEV(\"header\", {\n className: \"Hyp-Panel__header\",\n children: [icon && _jsxDEV(\"div\", {\n className: \"Hyp-Panel__header-icon\",\n children: _jsxDEV(SvgIcon, {\n name: icon,\n title: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 37,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 36,\n columnNumber: 11\n }, this), _jsxDEV(\"h2\", {\n className: \"Hyp-Panel__title\",\n children: title\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 40,\n columnNumber: 9\n }, this), withCloseButton && _jsxDEV(\"div\", {\n className: \"Hyp-Panel__close\",\n children: _jsxDEV(IconButton, {\n icon: cancelIcon,\n title: \"Close\",\n onClick: onClose\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 43,\n columnNumber: 13\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 42,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 34,\n columnNumber: 7\n }, this), _jsxDEV(\"div\", {\n className: \"Hyp-Panel__content\",\n children: children\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 47,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 29,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Panel.js.map","var _jsxFileName = \"/home/runner/work/frontend-shared/frontend-shared/src/components/Spinner.js\";\nimport classnames from 'classnames'; // @ts-ignore\n\nimport spinnerSVG from '../../images/icons/spinner--spokes.svg';\nimport { Icon } from './Icon';\nimport { registerIcon } from './SvgIcon'; // Register the spinner icon for use\n\nimport { jsxDEV as _jsxDEV } from \"preact/jsx-dev-runtime\";\nconst spinnerIcon = registerIcon('spinner', spinnerSVG);\n/**\n * @typedef SpinnerProps\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {'small'|'medium'|'large'} [size='medium'] - Relative size of spinner\n * to surrounding content\n */\n\n/**\n * @typedef FullScreenSpinnerProps\n * @prop {string} [classes] - Additional CSS classes to apply\n * @prop {string} [containerClasses] - CSS classes to apply to wrapping element.\n */\n\n/**\n * Loading indicator.\n *\n * @param {SpinnerProps} props\n */\n\nexport function Spinner({\n classes = '',\n size = 'medium'\n}) {\n const baseClass = `Hyp-Spinner--${size}`;\n return _jsxDEV(Icon, {\n name: spinnerIcon,\n containerClasses: classnames(baseClass, classes)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 32,\n columnNumber: 5\n }, this);\n}\n/**\n * Full-screen loading indicator.\n *\n * @param {FullScreenSpinnerProps} props\n */\n\nexport function FullScreenSpinner({\n classes = '',\n containerClasses = ''\n}) {\n return _jsxDEV(\"div\", {\n className: classnames('Hyp-FullScreenSpinner', containerClasses),\n children: _jsxDEV(Spinner, {\n classes: classnames('Hyp-FullScreenSpinner__spinner', classes),\n size: \"large\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 47,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 46,\n columnNumber: 5\n }, this);\n}\n//# sourceMappingURL=Spinner.js.map","// @ts-nocheck\n\nimport {\n cancel,\n caretLeft,\n caretRight,\n caution,\n hide,\n highlight,\n note,\n show,\n} from '@hypothesis/frontend-shared/lib/icons';\n\n// Different variant than shared icon\nimport annotateIcon from '../images/icons/annotate.svg';\n// Not in shared icon set\nimport jstor from '../images/icons/jstor.svg';\nimport pointerIcon from '../images/icons/pointer.svg';\n\n/**\n * Set of icons used by the annotator part of the application via the `Icon`\n * component.\n */\nexport const annotatorIcons = {\n annotate: annotateIcon,\n cancel,\n caution,\n 'caret-left': caretLeft,\n 'caret-right': caretRight,\n hide,\n highlight,\n jstor,\n note,\n pointer: pointerIcon,\n show,\n};\n","/**\n * @typedef Listener\n * @prop {EventTarget} eventTarget\n * @prop {string} eventType\n * @prop {(event: Event) => void} listener\n */\n\n/**\n * Return the event type that a listener will receive.\n *\n * For example `EventType<HTMLElement, 'keydown'>` evaluates to `KeyboardEvent`.\n *\n * The event type is extracted from the target's `on${Type}` property (eg.\n * `HTMLElement.onkeydown` here) If there is no such property, the type defaults\n * to `Event`.\n *\n * @template {EventTarget} Target\n * @template {string} Type\n * @typedef {`on${Type}` extends keyof Target ?\n * Target[`on${Type}`] extends ((...args: any[]) => void)|null ?\n * Parameters<NonNullable<Target[`on${Type}`]>>[0]\n * : Event : Event} EventType\n */\n\n/**\n * Utility that provides a way to conveniently remove a set of DOM event\n * listeners when they are no longer needed.\n */\nexport class ListenerCollection {\n constructor() {\n /** @type {Map<Symbol, Listener>} */\n this._listeners = new Map();\n }\n\n /**\n * Add a listener and return an ID that can be used to remove it later\n *\n * @template {string} Type\n * @template {EventTarget} Target\n * @param {Target} eventTarget\n * @param {Type} eventType\n * @param {(event: EventType<Target, Type>) => void} listener\n * @param {AddEventListenerOptions} [options]\n */\n add(eventTarget, eventType, listener, options) {\n eventTarget.addEventListener(\n eventType,\n /** @type {EventListener} */ (listener),\n options\n );\n const symbol = Symbol();\n this._listeners.set(symbol, {\n eventTarget,\n eventType,\n // eslint-disable-next-line object-shorthand\n listener: /** @type {EventListener} */ (listener),\n });\n return symbol;\n }\n\n /**\n * Remove a specific listener.\n *\n * @param {Symbol} listenerId\n */\n remove(listenerId) {\n const event = this._listeners.get(listenerId);\n if (event) {\n const { eventTarget, eventType, listener } = event;\n eventTarget.removeEventListener(eventType, listener);\n this._listeners.delete(listenerId);\n }\n }\n\n removeAll() {\n this._listeners.forEach(({ eventTarget, eventType, listener }) => {\n eventTarget.removeEventListener(eventType, listener);\n });\n this._listeners.clear();\n }\n}\n","/** @param {number} val */\nfunction byteToHex(val) {\n const str = val.toString(16);\n return str.length === 1 ? '0' + str : str;\n}\n\n/**\n * Generate a random hex string of `len` chars.\n *\n * @param {number} len - An even-numbered length string to generate.\n * @return {string}\n */\nexport function generateHexString(len) {\n const bytes = new Uint8Array(len / 2);\n window.crypto.getRandomValues(bytes);\n return Array.from(bytes).map(byteToHex).join('');\n}\n","/**\n * These types are the used in by `PortProvider` and `PortFinder` for the\n * inter-frame discovery and communication processes.\n *\n * @typedef {'guest'|'host'|'notebook'|'sidebar'} Frame\n *\n * @typedef Message\n * @prop {Frame} frame1\n * @prop {Frame} frame2\n * @prop {'offer'|'request'} type\n * @prop {string} requestId - ID of the request. Used to associate `offer`\n * responses with requests and enable PortProvider to ignore re-sent requests.\n */\n\n/**\n * Return true if an object, eg. from the data field of a `MessageEvent`, is a\n * valid `Message`.\n *\n * @param {any} data\n * @return {data is Message}\n */\nexport function isMessage(data) {\n if (data === null || typeof data !== 'object') {\n return false;\n }\n\n for (let property of ['frame1', 'frame2', 'type', 'requestId']) {\n if (typeof data[property] !== 'string') {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Return true if the data payload from a MessageEvent matches `message`.\n *\n * @param {any} data\n * @param {Partial<Message>} message\n */\nexport function isMessageEqual(data, message) {\n if (!isMessage(data)) {\n return false;\n }\n\n // We assume `JSON.stringify` cannot throw because `isMessage` verifies that\n // all the fields we serialize here are serializable values.\n return (\n JSON.stringify(data, Object.keys(message).sort()) ===\n JSON.stringify(message, Object.keys(message).sort())\n );\n}\n\n/**\n * Check that source is of type Window.\n *\n * @param {MessageEventSource|null} source\n * @return {source is Window}\n */\nexport function isSourceWindow(source) {\n if (\n // `source` can be of type Window, MessagePort, ServiceWorker, or null.\n // `source instanceof Window` doesn't work in Chrome if `source` is a\n // cross-origin window.\n source === null ||\n source instanceof MessagePort ||\n (window.ServiceWorker && source instanceof ServiceWorker)\n ) {\n return false;\n }\n\n return true;\n}\n","import { ListenerCollection } from '../listener-collection';\nimport { generateHexString } from '../random';\nimport { isMessageEqual } from './port-util';\n\n/** Timeout for waiting for the host frame to respond to a port request. */\nexport const MAX_WAIT_FOR_PORT = 1000 * 20;\n\n/** Polling interval for requests to the host frame for a port. */\nexport const POLLING_INTERVAL_FOR_PORT = 250;\n\n/**\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n * @typedef {import('./port-util').Message} Message\n * @typedef {import('./port-util').Frame} Frame\n */\n\n/**\n * PortFinder is used by non-host frames in the client to establish a\n * MessagePort-based connection to other frames. It is used together with\n * PortProvider which runs in the host frame. See PortProvider for an overview.\n *\n * @implements {Destroyable}\n */\nexport class PortFinder {\n /**\n * @param {object} options\n * @param {Exclude<Frame, 'host'>} options.source - the role of this frame\n * @param {Window} options.hostFrame - the frame where the `PortProvider` is\n * listening for messages.\n */\n constructor({ hostFrame, source }) {\n this._hostFrame = hostFrame;\n this._source = source;\n this._listeners = new ListenerCollection();\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Request a specific port from the host frame\n *\n * @param {Frame} target - the frame aiming to be discovered\n * @return {Promise<MessagePort>}\n */\n async discover(target) {\n let isValidRequest = false;\n if (\n (this._source === 'guest' && target === 'host') ||\n (this._source === 'guest' && target === 'sidebar') ||\n (this._source === 'sidebar' && target === 'host') ||\n (this._source === 'notebook' && target === 'sidebar')\n ) {\n isValidRequest = true;\n }\n\n if (!isValidRequest) {\n throw new Error('Invalid request of channel/port');\n }\n\n const requestId = generateHexString(6);\n\n return new Promise((resolve, reject) => {\n const postRequest = () => {\n this._hostFrame.postMessage(\n {\n frame1: this._source,\n frame2: target,\n type: 'request',\n requestId,\n },\n '*'\n );\n };\n\n // Because `guest` iframes load in parallel to the `host` frame, we can\n // not assume that the code in the `host` frame is executed before the\n // code in a `guest` frame. Hence, we can't assume that `PortProvider` (in\n // the `host` frame) is initialized before `PortFinder` (in the non-host\n // frames). Therefore, for the `PortFinder`, we implement a polling\n // strategy (sending a message every N milliseconds) until a response is\n // received.\n const intervalId = setInterval(postRequest, POLLING_INTERVAL_FOR_PORT);\n\n // The `host` frame maybe busy, that's why we should wait.\n const timeoutId = setTimeout(() => {\n clearInterval(intervalId);\n reject(\n new Error(\n `Unable to establish ${this._source}-${target} communication channel`\n )\n );\n }, MAX_WAIT_FOR_PORT);\n\n const listenerId = this._listeners.add(window, 'message', event => {\n const { data, ports } = event;\n if (\n isMessageEqual(data, {\n frame1: this._source,\n frame2: target,\n type: 'offer',\n requestId,\n })\n ) {\n clearInterval(intervalId);\n clearTimeout(timeoutId);\n this._listeners.remove(listenerId);\n resolve(ports[0]);\n }\n });\n\n postRequest();\n });\n }\n}\n","function E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n","/** @type {Window|null} */\nlet errorDestination = null;\n\n/**\n * Wrap a callback with an error handler which forwards errors to another frame\n * using {@link sendError}.\n *\n * @template {unknown[]} Args\n * @template Result\n * @param {(...args: Args) => Result} callback\n * @param {string} context - A short message indicating where the error happened.\n * @return {(...args: Args) => Result}\n */\nexport function captureErrors(callback, context) {\n return (...args) => {\n try {\n return callback(...args);\n } catch (err) {\n sendError(err, context);\n throw err;\n }\n };\n}\n\n/**\n * @typedef ErrorData\n * @prop {string} message\n * @prop {string} [stack]\n */\n\n/**\n * Return a cloneable representation of an Error.\n *\n * This is needed in browsers that don't support structured-cloning of Error\n * objects, or if the error is not cloneable for some reason.\n *\n * @param {Error|unknown} err\n * @return {ErrorData}\n */\nfunction serializeError(err) {\n if (!(err instanceof Error)) {\n return { message: String(err), stack: undefined };\n }\n\n return {\n message: err.message,\n stack: err.stack,\n };\n}\n\n/**\n * Convert error data serialized by {@link serializeError} back into an Error.\n *\n * @param {ErrorData} data\n * @return {Error}\n */\nfunction deserializeError(data) {\n const err = new Error(data.message);\n err.stack = data.stack;\n return err;\n}\n\n/**\n * Forward an error to the frame registered with {@link sendErrorsTo}.\n *\n * Errors are delivered on a best-effort basis. If no error handling frame has\n * been registered or the frame is still loading, the error will not be received.\n *\n * Ideally we would use a more robust delivery system which can queue messages\n * until they can be processed (eg. using MessagePort). We use `window.postMessage`\n * for the moment because we are trying to rule out problems with\n * MessageChannel/MessagePort when setting up sidebar <-> host communication.\n *\n * @param {unknown} error\n * @param {string} context - A short message indicating where the error happened.\n */\nexport function sendError(error, context) {\n if (!errorDestination) {\n return;\n }\n\n const data = {\n type: 'hypothesis-error',\n error: error instanceof Error ? error : serializeError(error),\n context,\n };\n\n try {\n // Try to send the error. If this fails because the browser doesn't support\n // structured cloning of errors, use a fallback.\n try {\n errorDestination.postMessage(data, '*');\n } catch (postErr) {\n if (\n postErr instanceof DOMException &&\n postErr.name === 'DataCloneError'\n ) {\n data.error = serializeError(data.error);\n errorDestination.postMessage(data, '*');\n } else {\n throw postErr;\n }\n }\n } catch (sendErr) {\n console.warn('Unable to report Hypothesis error', sendErr);\n }\n}\n\n/**\n * Register a handler for errors sent to the current frame using {@link sendError}\n *\n * @param {(error: unknown, context: string) => void} callback\n * @return {() => void} A function that unregisters the handler\n */\nexport function handleErrorsInFrames(callback) {\n /** @param {MessageEvent} event */\n const handleMessage = event => {\n const { data } = event;\n if (data && data?.type === 'hypothesis-error') {\n const { context, error } = data;\n callback(\n error instanceof Error ? error : deserializeError(error),\n context\n );\n }\n };\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n}\n\n/**\n * Register a destination frame that {@link sendError} should submit errors to.\n *\n * @param {Window|null} destination\n */\nexport function sendErrorsTo(destination) {\n errorDestination = destination;\n}\n","import { TinyEmitter } from 'tiny-emitter';\n\nimport { captureErrors, sendError } from '../frame-error-capture';\nimport { ListenerCollection } from '../listener-collection';\nimport { isMessage, isMessageEqual, isSourceWindow } from './port-util';\n\n/**\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n * @typedef {import('./port-util').Message} Message\n * @typedef {import('./port-util').Frame} Frame\n * @typedef {'guest-host'|'guest-sidebar'|'notebook-sidebar'|'sidebar-host'} Channel\n */\n\n/**\n * PortProvider creates a `MessageChannel` for communication between two\n * frames.\n *\n * There are 4 types of frames:\n * - `host`: frame where the Hypothesis client is initially loaded.\n * - `guest`: frames with annotatable content. In some instances a `guest`\n * frame can be the same as the `host` frame, in other cases, it is an iframe\n * where either (1) the hypothesis client has been injected or (2) the\n * hypothesis client has been configured to act exclusively as a `guest` (not\n * showing the sidebar).\n * - `notebook`: another hypothesis app that runs in a separate iframe.\n * - `sidebar`: the main hypothesis app. It runs in an iframe on a different\n * origin than the host and is responsible for the communication with the\n * backend (fetching and saving annotations).\n *\n * This layout represents the current arrangement of frames:\n *\n * `host` frame\n * |-> `sidebar` iframe\n * |-> `notebook` iframe\n * |-> [`guest` iframes]\n *\n * Currently, we support communication between the following pairs of frames:\n * - `guest-host`\n * - `guest-sidebar`\n * - `notebook-sidebar`\n * - `sidebar-host`\n *\n * `PortProvider` is used only in the `host` frame. The other frames use the\n * companion class, `PortFinder`. `PortProvider` creates a `MessageChannel`\n * for two frames to communicate with each other. It also listens to requests for\n * particular `MessagePort` and dispatches the corresponding `MessagePort`.\n *\n *\n * PortFinder (non-host frame) | PortProvider (host frame)\n * -----------------------------------------------------------------------------------------------\n * 1. Request `MessagePort` via `window.postMessage` ---> 2. Receive request and create port pair\n * |\n * V\n * 4. Receive requested port <---------------------- 3. Send first port to requesting frame\n * 5. Send second port to other frame\n * (eg. via MessageChannel connection\n * between host and other frame)\n *\n * @implements {Destroyable}\n */\nexport class PortProvider {\n /**\n * Begin listening to port requests from other frames.\n *\n * @param {string} hypothesisAppsOrigin - the origin of the hypothesis apps\n * is use to send the notebook and sidebar ports to only the frames that\n * match the origin.\n */\n constructor(hypothesisAppsOrigin) {\n this._hypothesisAppsOrigin = hypothesisAppsOrigin;\n this._emitter = new TinyEmitter();\n\n /**\n * IDs of port requests that have been handled.\n *\n * This is used to avoid responding to the same request multiple times.\n * Guest frames in particular may send duplicate requests (see comments in\n * PortFinder).\n *\n * @type {Set<string>}\n */\n this._handledRequests = new Set();\n\n // Create the `sidebar-host` channel immediately, while other channels are\n // created on demand\n this._sidebarHostChannel = new MessageChannel();\n\n this._listeners = new ListenerCollection();\n\n /** @type {Array<Partial<Message> & {allowedOrigin: string}>} */\n this._allowedMessages = [\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: '*',\n frame1: 'guest',\n frame2: 'sidebar',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'sidebar',\n frame2: 'host',\n type: 'request',\n },\n {\n allowedOrigin: this._hypothesisAppsOrigin,\n frame1: 'notebook',\n frame2: 'sidebar',\n type: 'request',\n },\n ];\n\n this._listen();\n }\n\n _listen() {\n const errorContext = 'Handling port request';\n const sentErrors = /** @type {Set<string>} */ (new Set());\n\n /** @param {string} message */\n const reportError = message => {\n if (sentErrors.has(message)) {\n // PortFinder will send requests repeatedly until it gets a response or\n // a timeout is reached.\n //\n // Only send errors once to avoid spamming Sentry.\n return;\n }\n sentErrors.add(message);\n sendError(new Error(message), errorContext);\n };\n\n /** @param {MessageEvent} event */\n const handleRequest = event => {\n const { data, origin, source } = event;\n\n if (!isMessage(data) || data.type !== 'request') {\n // If this does not look like a message intended for us, ignore it.\n return;\n }\n\n const { frame1, frame2, requestId } = data;\n const channel = /** @type {Channel} */ (`${frame1}-${frame2}`);\n\n if (!isSourceWindow(source)) {\n reportError(\n `Ignored port request for channel ${channel} from non-Window source`\n );\n return;\n }\n\n const match = this._allowedMessages.find(\n ({ allowedOrigin, ...allowedMessage }) =>\n this._messageMatches({\n allowedMessage,\n allowedOrigin,\n data,\n origin,\n })\n );\n\n if (match === undefined) {\n reportError(\n `Ignored invalid port request for channel ${channel} from ${origin}`\n );\n return;\n }\n\n if (this._handledRequests.has(requestId)) {\n return;\n }\n this._handledRequests.add(requestId);\n\n // Create the channel for these two frames to communicate and send the\n // corresponding ports to them.\n const messageChannel =\n channel === 'sidebar-host'\n ? this._sidebarHostChannel\n : new MessageChannel();\n\n const message = { frame1, frame2, type: 'offer', requestId };\n\n // If the source window has an opaque origin [1], `event.origin` will be\n // the string \"null\". This is not a legal value for the `targetOrigin`\n // parameter to `postMessage`, so remap it to \"*\".\n //\n // [1] https://html.spec.whatwg.org/multipage/origin.html#origin.\n // Documents with opaque origins include file:// URLs and\n // sandboxed iframes.\n const targetOrigin = origin === 'null' ? '*' : origin;\n source.postMessage(message, targetOrigin, [messageChannel.port1]);\n\n if (frame2 === 'sidebar') {\n this._sidebarHostChannel.port2.postMessage(message, [\n messageChannel.port2,\n ]);\n } else if (frame2 === 'host') {\n this._emitter.emit('frameConnected', frame1, messageChannel.port2);\n }\n };\n\n this._listeners.add(\n window,\n 'message',\n captureErrors(handleRequest, errorContext)\n );\n }\n\n /**\n * @param {object} options\n * @param {Partial<Message>} options.allowedMessage - the `data` must match this\n * `Message`.\n * @param {string} options.allowedOrigin - the `origin` must match this\n * value. If `allowedOrigin` is '*', the origin is ignored.\n * @param {unknown} options.data - the data to be compared with `allowedMessage`.\n * @param {string} options.origin - the origin to be compared with\n * `allowedOrigin`.\n */\n _messageMatches({ allowedMessage, allowedOrigin, data, origin }) {\n if (allowedOrigin !== '*' && origin !== allowedOrigin) {\n return false;\n }\n\n return isMessageEqual(data, allowedMessage);\n }\n\n /**\n * @param {'frameConnected'} eventName\n * @param {(source: 'guest'|'sidebar', port: MessagePort) => void} handler - this handler\n * fires when a frame connects to the host frame\n */\n on(eventName, handler) {\n this._emitter.on(eventName, handler);\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n}\n","import { ListenerCollection } from '../listener-collection';\n\n/*\n This module was adapted from `index.js` in https://github.com/substack/frame-rpc.\n\n This software is released under the MIT license:\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nconst VERSION = '1.0.0';\nconst PROTOCOL = 'frame-rpc';\n\n/**\n * Format of messages sent between frames.\n *\n * See https://github.com/substack/frame-rpc#protocol\n *\n * @typedef RequestMessage\n * @prop {unknown[]} arguments\n * @prop {string} method\n * @prop {PROTOCOL} protocol\n * @prop {number} sequence\n * @prop {VERSION} version\n *\n * @typedef ResponseMessage\n * @prop {unknown[]} arguments\n * @prop {PROTOCOL} protocol\n * @prop {number} response\n * @prop {VERSION} version\n *\n * @typedef {RequestMessage|ResponseMessage} Message\n *\n * @typedef {import('../../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Send a PortRPC method call.\n *\n * @param {MessagePort} port\n * @param {string} method\n * @param {unknown[]} [args]\n * @param {number} [sequence] - Sequence number used for replies\n */\nfunction sendCall(port, method, args = [], sequence = -1) {\n port.postMessage(\n /** @type {RequestMessage} */ ({\n protocol: PROTOCOL,\n version: VERSION,\n arguments: args,\n method,\n sequence,\n })\n );\n}\n\n/**\n * Install a message listener that ensures proper delivery of \"close\" notifications\n * from {@link PortRPC}s in Safari <= 15.\n *\n * This must be called in the _parent_ frame of the frame that owns the port.\n * In Hypothesis this means for example that the workaround would be installed\n * in the host frame to ensure delivery of \"close\" notifications from \"guest\"\n * frames.\n *\n * @param {string} [userAgent] - Test seam\n * @return {() => void} - Callback that removes the listener\n */\nexport function installPortCloseWorkaroundForSafari(\n /* istanbul ignore next */\n userAgent = navigator.userAgent\n) {\n if (!shouldUseSafariWorkaround(userAgent)) {\n return () => {};\n }\n\n /** @param {MessageEvent} event */\n const handler = event => {\n if (event.data?.type === 'hypothesisPortClosed' && event.ports[0]) {\n const port = event.ports[0];\n sendCall(port, 'close');\n port.close();\n }\n };\n\n window.addEventListener('message', handler);\n return () => window.removeEventListener('message', handler);\n}\n\n/**\n * Test whether this browser needs the workaround for https://bugs.webkit.org/show_bug.cgi?id=231167.\n *\n * @param {string} userAgent\n */\nfunction shouldUseSafariWorkaround(userAgent) {\n const webkitVersionMatch = userAgent.match(/\\bAppleWebKit\\/([0-9]+)\\b/);\n if (!webkitVersionMatch) {\n return false;\n }\n const version = parseInt(webkitVersionMatch[1]);\n\n // Chrome's user agent contains the token \"AppleWebKit/537.36\", where the\n // version number is frozen. This corresponds to a version of Safari from 2013,\n // which is older than all supported Safari versions.\n if (version <= 537) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Callback type used for RPC method handlers and result callbacks.\n *\n * @typedef {(...args: unknown[]) => void} Callback\n */\n\n/**\n * @param {any} value\n * @return {value is Callback}\n */\nfunction isCallback(value) {\n return typeof value === 'function';\n}\n\n/**\n * PortRPC provides remote procedure calls between frames or workers. It uses\n * the Channel Messaging API [1] as a transport.\n *\n * To communicate between two frames with this class, construct a PortRPC\n * instance in each and register method handlers with {@link on}. Create a\n * {@link MessageChannel} and send one of its two ports to each frame. Then call\n * {@link connect} to make the PortRPC instance in each frame use the corresponding\n * port.\n *\n * In addition to the custom methods that a PortRPC handles, there are several\n * built-in events which are sent automatically:\n *\n * - \"connect\" is sent when a PortRPC connects to a port. This event can\n * be used to confirm that the sending frame has received the port and will\n * send a \"close\" event when it goes away.\n * - \"close\" is sent when a PortRPC is destroyed or the owning frame is\n * unloaded. This event may not be dispatched if the guest frame terminates\n * abnormally (eg. due to an OS process crash).\n *\n * [1] https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API\n *\n * @template {string} OnMethod - Names of RPC methods this client responds to\n * @template {string} CallMethod - Names of RPC methods this client invokes\n * @implements {Destroyable}\n */\nexport class PortRPC {\n /**\n * @param {object} options\n * @param {string} [options.userAgent] - Test seam\n * @param {Window} [options.currentWindow] - Test seam\n */\n constructor({\n userAgent = navigator.userAgent,\n currentWindow = window,\n } = {}) {\n /** @type {MessagePort|null} */\n this._port = null;\n\n /** @type {Map<string, Callback>} */\n this._methods = new Map();\n\n this._sequence = 1;\n\n /** @type {Map<number, Callback>} */\n this._callbacks = new Map();\n\n this._listeners = new ListenerCollection();\n this._listeners.add(currentWindow, 'unload', () => {\n if (this._port) {\n // Send \"close\" notification directly. This works in Chrome, Firefox and\n // Safari >= 16.\n sendCall(this._port, 'close');\n\n // To work around a bug in Safari <= 15 which prevents sending messages\n // while a window is unloading, try transferring the port to the parent frame\n // and re-sending the \"close\" event from there.\n if (\n currentWindow !== currentWindow.parent &&\n shouldUseSafariWorkaround(userAgent)\n ) {\n currentWindow.parent.postMessage(\n { type: 'hypothesisPortClosed' },\n '*',\n [this._port]\n );\n }\n }\n });\n\n /**\n * Method and arguments of pending RPC calls made before a port was connected.\n *\n * @type {Array<[CallMethod, unknown[]]>}\n */\n this._pendingCalls = [];\n\n this._receivedCloseEvent = false;\n }\n\n /**\n * Register a method handler for incoming RPC requests.\n *\n * This can also be used to register a handler for the built-in \"connect\"\n * and \"close\" events.\n *\n * All handlers must be registered before {@link connect} is invoked.\n *\n * @template {function} Handler\n * @param {OnMethod|'close'|'connect'} method\n * @param {Handler} handler\n */\n on(method, handler) {\n if (this._port) {\n throw new Error('Cannot add a method handler after a port is connected');\n }\n this._methods.set(method, /** @type {any} */ (handler));\n }\n\n /**\n * Connect to a MessagePort and process any queued RPC requests.\n *\n * @param {MessagePort} port\n */\n connect(port) {\n this._port = port;\n this._listeners.add(port, 'message', event => this._handle(event));\n port.start();\n sendCall(port, 'connect');\n\n for (let [method, args] of this._pendingCalls) {\n this.call(method, ...args);\n }\n this._pendingCalls = [];\n }\n\n /**\n * Disconnect the RPC channel and close the MessagePort.\n */\n destroy() {\n if (this._port) {\n sendCall(this._port, 'close');\n this._port.close();\n }\n this._destroyed = true;\n this._listeners.removeAll();\n }\n\n /**\n * Send an RPC request via the connected port.\n *\n * If this client is not yet connected to a port, the call will be queued and\n * sent when {@link connect} is called.\n *\n * If the final argument in `args` is a function, it is treated as a callback\n * which is invoked with the response in the form of (error, result) arguments.\n *\n * @param {CallMethod} method\n * @param {unknown[]} args\n */\n call(method, ...args) {\n if (!this._port) {\n this._pendingCalls.push([method, args]);\n }\n\n if (!this._port || this._destroyed) {\n return;\n }\n\n const seq = this._sequence++;\n const finalArg = args[args.length - 1];\n if (isCallback(finalArg)) {\n this._callbacks.set(seq, finalArg);\n args = args.slice(0, -1);\n }\n\n sendCall(this._port, method, args, seq);\n }\n\n /**\n * Validate message\n *\n * @param {MessageEvent} event\n * @return {null|Message}\n */\n _parseMessage({ data }) {\n if (!data || typeof data !== 'object') {\n return null;\n }\n if (data.protocol !== PROTOCOL) {\n return null;\n }\n if (data.version !== VERSION) {\n return null;\n }\n if (!Array.isArray(data.arguments)) {\n return null;\n }\n\n return data;\n }\n\n /**\n * @param {MessageEvent} event\n */\n _handle(event) {\n const msg = this._parseMessage(event);\n const port = this._port;\n\n if (!msg || !port) {\n return;\n }\n\n if ('method' in msg) {\n // Only allow close event to fire once.\n if (msg.method === 'close') {\n if (this._receivedCloseEvent) {\n return;\n } else {\n this._receivedCloseEvent = true;\n }\n }\n\n const handler = this._methods.get(msg.method);\n if (!handler) {\n return;\n }\n\n /** @param {unknown[]} args */\n const callback = (...args) => {\n /** @type {ResponseMessage} */\n const message = {\n arguments: args,\n protocol: PROTOCOL,\n response: msg.sequence,\n version: VERSION,\n };\n\n port.postMessage(message);\n };\n handler(...msg.arguments, callback);\n } else if ('response' in msg) {\n const cb = this._callbacks.get(msg.response);\n this._callbacks.delete(msg.response);\n if (cb) {\n cb(...msg.arguments);\n }\n }\n }\n}\n","/**\n * `Object.assign()`-like helper. Used because this script needs to work\n * in IE 10/11 without polyfills.\n *\n * @param {Record<string, unknown>} dest\n * @param {Record<string, unknown>} src\n */\nfunction assign(dest, src) {\n for (const k in src) {\n if (src.hasOwnProperty(k)) {\n dest[k] = src[k];\n }\n }\n return dest;\n}\n\n/**\n * Return a parsed `js-hypothesis-config` object from the document, or `{}`.\n *\n * Find all `<script class=\"js-hypothesis-config\">` tags in the given document,\n * parse them as JSON, and return the parsed object.\n *\n * If there are no `js-hypothesis-config` tags in the document then return\n * `{}`.\n *\n * If there are multiple `js-hypothesis-config` tags in the document then merge\n * them into a single returned object (when multiple scripts contain the same\n * setting names, scripts further down in the document override those further\n * up).\n *\n * @param {Document|Element} document - The root element to search.\n */\nexport function parseJsonConfig(document) {\n /** @type {Record<string, unknown>} */\n const config = {};\n const settingsElements = document.querySelectorAll(\n 'script.js-hypothesis-config'\n );\n\n for (let i = 0; i < settingsElements.length; i++) {\n let settings;\n try {\n settings = JSON.parse(settingsElements[i].textContent || '');\n } catch (err) {\n console.warn(\n 'Could not parse settings from js-hypothesis-config tags',\n err\n );\n settings = {};\n }\n assign(config, settings);\n }\n\n return config;\n}\n","/**\n * Type conversion methods that coerce incoming configuration values to an\n * expected type or format that other parts of the UI may make assumptions\n * on. This is needed for incoming configuration values that are otherwise\n * not sanitized.\n *\n * Note that if the values passed are plain javascript values (such as ones\n * produced from JSON.parse), then these methods do not throw errors.\n */\n\n/**\n * Returns a boolean\n *\n * @param {any} value - initial value\n * @return {boolean}\n */\nexport function toBoolean(value) {\n if (typeof value === 'string') {\n if (value.trim().toLocaleLowerCase() === 'false') {\n // \"false\", \"False\", \" false\", \"FALSE\" all return false\n return false;\n }\n }\n const numericalVal = Number(value);\n if (!isNaN(numericalVal)) {\n return Boolean(numericalVal);\n }\n // Any non numerical or falsely string should return true, otherwise return false\n return typeof value === 'string';\n}\n\n/**\n * Returns either an integer or NaN\n *\n * @param {any} value - initial value\n * @return {number}\n */\nexport function toInteger(value) {\n // Acts as a simple wrapper\n return parseInt(value);\n}\n\n/**\n * Returns either the value if its an object or an empty object\n *\n * @param {any} value - initial value\n * @return {object}\n */\nexport function toObject(value) {\n if (typeof value === 'object' && value !== null) {\n return value;\n }\n // Don't attempt to fix the values, just ensure type correctness\n return {};\n}\n\n/**\n * Returns the value as a string or an empty string if the\n * value undefined, null or otherwise falsely.\n *\n * @param {any} value - initial value\n * @return {string}\n */\nexport function toString(value) {\n if (value && typeof value.toString === 'function') {\n return value.toString();\n }\n return '';\n}\n\n/**\n * @template T\n * @typedef {import('preact').Ref<T>} Ref\n */\n\n/**\n * Helper for downcasting a ref to a more specific type, where that is safe\n * to do.\n *\n * This is mainly useful to cast a generic `Ref<HTMLElement>` to a more specific\n * element type (eg. `Ref<HTMLDivElement>`) for use with the `ref` prop of a JSX element.\n * Since Preact only writes to the `ref` prop, such a cast is safe.\n *\n * @template T\n * @template {T} U\n * @param {Ref<T>|undefined} ref\n * @return {Ref<U>|undefined}\n */\nexport function downcastRef(ref) {\n return /** @type {Ref<U>|undefined} */ (ref);\n}\n","/**\n * Return the URL of a resource related to the Hypothesis client that has been stored in\n * the page via a `<link type=\"application/annotator+{type}\">` tag.\n *\n * These link tags are generally written to the page by the boot script, which may be executed\n * in a separate JavaScript realm (eg. in the browser extension), and thus can share information\n * with the annotator code via the DOM but not JS globals.\n *\n * @param {Window} window_\n * @param {string} rel - The `rel` attribute to match\n * @param {'javascript'|'html'} type - Type of resource that the link refers to\n * @throws {Error} - If there's no link with the `rel` indicated, or the first\n * matching link has no `href`\n */\nexport function urlFromLinkTag(window_, rel, type) {\n const link = /** @type {HTMLLinkElement} */ (\n window_.document.querySelector(\n `link[type=\"application/annotator+${type}\"][rel=\"${rel}\"]`\n )\n );\n\n if (!link) {\n throw new Error(\n `No application/annotator+${type} (rel=\"${rel}\") link in the document`\n );\n }\n\n if (!link.href) {\n throw new Error(\n `application/annotator+${type} (rel=\"${rel}\") link has no href`\n );\n }\n\n return link.href;\n}\n","import { parseJsonConfig } from '../../boot/parse-json-config';\nimport { toBoolean } from '../../shared/type-coercions';\n\nimport { configFuncSettingsFrom } from './config-func-settings-from';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\n/**\n * @typedef SettingsGetters\n * @prop {string|null} annotations\n * @prop {string|null} query\n * @prop {string|null} group\n * @prop {string} showHighlights\n * @prop {string} clientUrl\n * @prop {string} sidebarAppUrl\n * @prop {string} notebookAppUrl\n * @prop {(name: string) => unknown} hostPageSetting\n */\n\n/**\n * Discard a setting if it is not a string.\n *\n * @param {unknown} value\n */\nfunction checkIfString(value) {\n return typeof value === 'string' ? value : null;\n}\n\n/**\n * @param {Window} window_\n * @return {SettingsGetters}\n */\nexport function settingsFrom(window_) {\n // Prioritize the `window.hypothesisConfig` function over the JSON format\n // Via uses `window.hypothesisConfig` and makes it non-configurable and non-writable.\n // In addition, Via sets the `ignoreOtherConfiguration` option to prevent configuration merging.\n const configFuncSettings = configFuncSettingsFrom(window_);\n\n /** @type {Record<string, unknown>} */\n let jsonConfigs;\n if (toBoolean(configFuncSettings.ignoreOtherConfiguration)) {\n jsonConfigs = {};\n } else {\n jsonConfigs = parseJsonConfig(window_.document);\n }\n\n /**\n * Return the `#annotations:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:<ANNOTATION_ID>` fragment then return\n * the annotation ID extracted from the fragment. Otherwise return `null`.\n *\n * @return {string|null} - The extracted ID, or null.\n */\n function annotations() {\n /** Return the annotations from the URL, or null. */\n function annotationsFromURL() {\n // Annotation IDs are url-safe-base64 identifiers\n // See https://tools.ietf.org/html/rfc4648#page-7\n const annotFragmentMatch = window_.location.href.match(\n /#annotations:([A-Za-z0-9_-]+)$/\n );\n if (annotFragmentMatch) {\n return annotFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.annotations) || annotationsFromURL();\n }\n\n /**\n * Return the `#annotations:group:*` ID from the given URL's fragment.\n *\n * If the URL contains a `#annotations:group:<GROUP_ID>` fragment then return\n * the group ID extracted from the fragment. Otherwise return `null`.\n *\n * @return {string|null} - The extracted ID, or null.\n */\n function group() {\n function groupFromURL() {\n const groupFragmentMatch = window_.location.href.match(\n /#annotations:group:([A-Za-z0-9_-]+)$/\n );\n if (groupFragmentMatch) {\n return groupFragmentMatch[1];\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.group) || groupFromURL();\n }\n\n function showHighlights() {\n const value = hostPageSetting('showHighlights');\n\n switch (value) {\n case 'always':\n case 'never':\n case 'whenSidebarOpen':\n return value;\n case true:\n return 'always';\n case false:\n return 'never';\n default:\n return 'always';\n }\n }\n\n /**\n * Return the config.query setting from the host page or from the URL.\n *\n * If the host page contains a js-hypothesis-config script containing a\n * query setting then return that.\n *\n * Otherwise if the host page's URL has a `#annotations:query:*` (or\n * `#annotations:q:*`) fragment then return the query value from that.\n *\n * Otherwise return null.\n *\n * @return {string|null} - The config.query setting, or null.\n */\n function query() {\n /** Return the query from the URL, or null. */\n function queryFromURL() {\n const queryFragmentMatch = window_.location.href.match(\n /#annotations:(query|q):(.+)$/i\n );\n if (queryFragmentMatch) {\n try {\n return decodeURIComponent(queryFragmentMatch[2]);\n } catch (err) {\n // URI Error should return the page unfiltered.\n }\n }\n return null;\n }\n\n return checkIfString(jsonConfigs.query) || queryFromURL();\n }\n\n /**\n * Returns the first setting value found from the respective sources in order.\n *\n * 1. window.hypothesisConfig()\n * 2. <script class=\"js-hypothesis-config\">\n *\n * If the setting is not found in either source, then return undefined.\n *\n * @param {string} name - Unique name of the setting\n */\n function hostPageSetting(name) {\n if (configFuncSettings.hasOwnProperty(name)) {\n return configFuncSettings[name];\n }\n\n if (jsonConfigs.hasOwnProperty(name)) {\n return jsonConfigs[name];\n }\n\n return undefined;\n }\n\n return {\n get annotations() {\n return annotations();\n },\n get clientUrl() {\n return urlFromLinkTag(window_, 'hypothesis-client', 'javascript');\n },\n get group() {\n return group();\n },\n get notebookAppUrl() {\n return urlFromLinkTag(window_, 'notebook', 'html');\n },\n get showHighlights() {\n return showHighlights();\n },\n get sidebarAppUrl() {\n return urlFromLinkTag(window_, 'sidebar', 'html');\n },\n get query() {\n return query();\n },\n hostPageSetting,\n };\n}\n","/**\n * @typedef HypothesisWindowProps\n * @prop {() => Record<string, unknown>} [hypothesisConfig] - Function that returns configuration\n * for the Hypothesis client\n */\n\n/**\n * Return an object containing config settings from window.hypothesisConfig().\n *\n * Return an object containing config settings returned by the\n * window.hypothesisConfig() function provided by the host page:\n *\n * {\n * fooSetting: 'fooValue',\n * barSetting: 'barValue',\n * ...\n * }\n *\n * If there's no window.hypothesisConfig() function then return {}.\n *\n * @param {Window & HypothesisWindowProps} window_ - The window to search for a hypothesisConfig() function\n * @return {Record<string, unknown>} - Any config settings returned by hypothesisConfig()\n */\nexport function configFuncSettingsFrom(window_) {\n if (!window_.hasOwnProperty('hypothesisConfig')) {\n return {};\n }\n\n if (typeof window_.hypothesisConfig !== 'function') {\n const docs =\n 'https://h.readthedocs.io/projects/client/en/latest/publishers/config/#window.hypothesisConfig';\n console.warn('hypothesisConfig must be a function, see: ' + docs);\n return {};\n }\n\n return window_.hypothesisConfig();\n}\n","import { isBrowserExtension } from './is-browser-extension';\nimport { settingsFrom } from './settings';\nimport { toBoolean } from '../../shared/type-coercions';\nimport { urlFromLinkTag } from './url-from-link-tag';\n\n/**\n * @typedef {import('./settings').SettingsGetters} SettingsGetters\n * @typedef {(settings: SettingsGetters, name: string) => any} ValueGetter\n *\n * @typedef ConfigDefinition\n * @prop {ValueGetter} getValue - Method to retrieve the value from the incoming source\n * @prop {boolean} allowInBrowserExt -\n * Allow this setting to be read in the browser extension. If this is false\n * and browser extension context is true, use `defaultValue` if provided otherwise\n * ignore the config key\n * @prop {any} [defaultValue] - Sets a default if `getValue` returns undefined\n * @prop {(value: any) => any} [coerce] - Transform a value's type, value or both\n *\n * @typedef {Record<string, ConfigDefinition>} ConfigDefinitionMap\n */\n\n/**\n * Named subset of the Hypothesis client configuration that is relevant in\n * a particular context.\n *\n * @typedef {'sidebar'|'notebook'|'annotator'|'all'} Context\n */\n\n/**\n * Returns the configuration keys that are relevant to a particular context.\n *\n * @param {Context} context\n */\nfunction configurationKeys(context) {\n const contexts = {\n annotator: ['clientUrl', 'contentPartner', 'subFrameIdentifier'],\n sidebar: [\n 'appType',\n 'annotations',\n 'branding',\n 'enableExperimentalNewNoteButton',\n 'externalContainerSelector',\n 'focus',\n 'group',\n 'onLayoutChange',\n 'openSidebar',\n 'query',\n 'requestConfigFromFrame',\n 'services',\n 'showHighlights',\n 'sidebarAppUrl',\n 'theme',\n 'usernameUrl',\n ],\n notebook: [\n 'branding',\n 'group',\n 'notebookAppUrl',\n 'requestConfigFromFrame',\n 'services',\n 'theme',\n 'usernameUrl',\n ],\n };\n\n switch (context) {\n case 'annotator':\n return contexts.annotator;\n case 'sidebar':\n return contexts.sidebar;\n case 'notebook':\n return contexts.notebook;\n case 'all':\n // Complete list of configuration keys used for testing.\n return [...contexts.annotator, ...contexts.sidebar, ...contexts.notebook];\n default:\n throw new Error(`Invalid application context used: \"${context}\"`);\n }\n}\n\n/** @type {ValueGetter} */\nfunction getHostPageSetting(settings, name) {\n return settings.hostPageSetting(name);\n}\n\n/**\n * Definitions of configuration keys\n *\n * @type {ConfigDefinitionMap}\n */\nconst configDefinitions = {\n annotations: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.annotations,\n },\n appType: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n branding: {\n defaultValue: null,\n allowInBrowserExt: false,\n getValue: getHostPageSetting,\n },\n // URL of the client's boot script. Used when injecting the client into\n // child iframes.\n clientUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.clientUrl,\n },\n contentPartner: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n enableExperimentalNewNoteButton: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n group: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.group,\n },\n focus: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n theme: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n usernameUrl: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n onLayoutChange: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n openSidebar: {\n allowInBrowserExt: true,\n defaultValue: false,\n coerce: toBoolean,\n getValue: getHostPageSetting,\n },\n query: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.query,\n },\n requestConfigFromFrame: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n services: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n showHighlights: {\n allowInBrowserExt: false,\n defaultValue: 'always',\n getValue: settings => settings.showHighlights,\n },\n notebookAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.notebookAppUrl,\n },\n sidebarAppUrl: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: settings => settings.sidebarAppUrl,\n },\n // Sub-frame identifier given when a frame is being embedded into\n // by a top level client\n subFrameIdentifier: {\n allowInBrowserExt: true,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n externalContainerSelector: {\n allowInBrowserExt: false,\n defaultValue: null,\n getValue: getHostPageSetting,\n },\n};\n\n/**\n * Return the subset of Hypothesis client configuration that is relevant in\n * a particular context.\n *\n * See https://h.readthedocs.io/projects/client/en/latest/publishers/config/\n * for details of all available configuration and the different ways they\n * can be included on the page. In addition to the configuration provided by\n * the embedder, the boot script also passes some additional configuration\n * to the annotator, such as URLs of the various sub-applications and the\n * boot script itself.\n *\n * @param {Context} context\n */\nexport function getConfig(context, window_ = window) {\n const settings = settingsFrom(window_);\n\n /** @type {Record<string, unknown>} */\n const config = {};\n\n // Filter the config based on the application context as some config values\n // may be inappropriate or erroneous for some applications.\n for (let key of configurationKeys(context)) {\n const configDef = configDefinitions[key];\n const hasDefault = configDef.defaultValue !== undefined; // A default could be null\n const isURLFromBrowserExtension = isBrowserExtension(\n urlFromLinkTag(window_, 'sidebar', 'html')\n );\n\n // Only allow certain values in the browser extension context\n if (!configDef.allowInBrowserExt && isURLFromBrowserExtension) {\n // If the value is not allowed here, then set to the default if provided, otherwise ignore\n // the key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Get the value from the configuration source\n const value = configDef.getValue(settings, key);\n if (value === undefined) {\n // If there is no value (e.g. undefined), then set to the default if provided,\n // otherwise ignore the config key:value pair\n if (hasDefault) {\n config[key] = configDef.defaultValue;\n }\n continue;\n }\n\n // Finally, run the value through an optional coerce method\n config[key] = configDef.coerce ? configDef.coerce(value) : value;\n }\n\n return config;\n}\n","/**\n * Return true if the client is from a browser extension.\n *\n * @param {string} url\n * @return {boolean} - Returns true if this instance of the Hypothesis client is one\n * distributed in a browser extension, false if it's one embedded in a\n * website.\n *\n */\nexport function isBrowserExtension(url) {\n return !(url.startsWith('http://') || url.startsWith('https://'));\n}\n","import { useEffect } from 'preact/hooks';\n\n/**\n * Bit flags indicating modifiers required by a shortcut or pressed in a key event.\n *\n * @type {Record<string, number>}\n */\nconst modifiers = {\n alt: 1,\n ctrl: 2,\n meta: 4,\n shift: 8,\n};\n\n/**\n * Match a `shortcut` key sequence against a keydown event.\n *\n * A shortcut key sequence is a string of \"+\"-separated keyboard modifiers and\n * keys. The list may contain zero or more modifiers and must contain exactly\n * one non-modifier key. The key and modifier names are case-insensitive.\n * For example \"Ctrl+Enter\", \"shift+a\". Key names are matched against `KeyboardEvent.key`.\n *\n * @param {KeyboardEvent} event\n * @param {string} shortcut\n * @return {boolean}\n */\nexport function matchShortcut(event, shortcut) {\n const parts = shortcut.split('+').map(p => p.toLowerCase());\n\n let requiredModifiers = 0;\n let requiredKey = null;\n\n for (let part of parts) {\n const modifierFlag = modifiers[part];\n if (modifierFlag) {\n requiredModifiers |= modifierFlag;\n } else if (requiredKey === null) {\n requiredKey = part;\n } else {\n throw new Error('Multiple non-modifier keys specified');\n }\n }\n\n if (!requiredKey) {\n throw new Error(`Invalid shortcut: ${shortcut}`);\n }\n\n const actualModifiers =\n (event.ctrlKey ? modifiers.ctrl : 0) |\n (event.metaKey ? modifiers.meta : 0) |\n (event.altKey ? modifiers.alt : 0) |\n (event.shiftKey ? modifiers.shift : 0);\n\n return (\n actualModifiers === requiredModifiers &&\n event.key.toLowerCase() === requiredKey\n );\n}\n\n/**\n * @typedef ShortcutOptions\n * @prop {HTMLElement} [rootElement] -\n * Element on which the key event listener should be installed. Defaults to\n * `document.body`.\n */\n\n/**\n * Install a shortcut key listener on the document.\n *\n * This can be used directly outside of a component. To use within a Preact\n * component, you probably want the `useShortcut` hook.\n *\n * @param {string} shortcut - Shortcut key sequence. See `matchShortcut`.\n * @param {(e: KeyboardEvent) => void} onPress - A function to call when the shortcut matches\n * @param {ShortcutOptions} [options]\n * @return {() => void} A function that removes the shortcut\n */\nexport function installShortcut(\n shortcut,\n onPress,\n {\n // We use `documentElement` as the root element rather than `document.body`\n // which is used as a root element in some other places because the body\n // element is not keyboard-focusable in XHTML documents in Safari/Chrome.\n // See https://github.com/hypothesis/client/issues/4364.\n rootElement = document.documentElement,\n } = {}\n) {\n /** @param {KeyboardEvent} event */\n const onKeydown = event => {\n if (matchShortcut(event, shortcut)) {\n onPress(event);\n }\n };\n rootElement.addEventListener('keydown', onKeydown);\n return () => rootElement.removeEventListener('keydown', onKeydown);\n}\n\n/**\n * An effect hook that installs a shortcut using `installShortcut` and removes\n * it when the component is unmounted.\n *\n * This provides a convenient way to enable a document-level shortcut while\n * a component is mounted. This differs from adding an `onKeyDown` handler to\n * one of the component's DOM elements in that it doesn't require the component\n * to have focus.\n *\n * To conditionally disable the shortcut, set `shortcut` to `null`.\n *\n * @param {string|null} shortcut -\n * A shortcut key sequence to match or `null` to disable. See `matchShortcut`.\n * @param {(e: KeyboardEvent) => void} onPress - A function to call when the shortcut matches\n * @param {ShortcutOptions} [options]\n */\nexport function useShortcut(shortcut, onPress, { rootElement } = {}) {\n useEffect(() => {\n if (!shortcut) {\n return undefined;\n }\n return installShortcut(shortcut, onPress, { rootElement });\n }, [shortcut, onPress, rootElement]);\n}\n","import classnames from 'classnames';\nimport { LabeledButton, Icon } from '@hypothesis/frontend-shared';\n\nimport { useShortcut } from '../../shared/shortcut';\n\n/**\n * Render an inverted light-on-dark \"pill\" with the given `badgeCount`\n * (annotation count). This is rendered instead of an icon on the toolbar\n * button for \"show\"-ing associated annotations for the current selection.\n *\n * @param {object} props\n * @param {number} props.badgeCount\n */\nfunction NumberIcon({ badgeCount }) {\n return (\n <span\n className={classnames(\n 'rounded px-1 py-0.5',\n 'text-color-text-inverted font-bold bg-grey-7',\n 'dim-bg'\n )}\n >\n {badgeCount}\n </span>\n );\n}\n\n/**\n * Render an arrow pointing up or down from the AdderToolbar. This arrow\n * should point roughly to the end of the user selection in the document.\n *\n * @param {object} props\n * @param {'up'|'down'} props.arrowDirection\n */\nfunction AdderToolbarArrow({ arrowDirection }) {\n return (\n <Icon\n name=\"pointer\"\n classes={classnames(\n // Position the arrow in the horizontal center at the bottom of the\n // container (toolbar). Note: the arrow is pointing up at this point.\n 'absolute left-1/2 -translate-x-1/2',\n // Override `1em` width/height rules in `Icon` to size the arrow as\n // its SVG dimensions dictate\n 'h-auto w-auto z-2',\n 'text-grey-3 fill-white',\n {\n // Down arrow: transform to point the arrow down\n 'rotate-180': arrowDirection === 'down',\n // Up arrow: position vertically above the toolbar\n 'top-0 -translate-y-full': arrowDirection === 'up',\n }\n )}\n />\n );\n}\n\n/**\n * @param {object} props\n * @param {number} [props.badgeCount]\n * @param {string} [props.icon]\n * @param {string} props.label\n * @param {() => void} props.onClick\n * @param {string|null} props.shortcut\n */\nfunction ToolbarButton({ badgeCount, icon, label, onClick, shortcut }) {\n useShortcut(shortcut, onClick);\n\n const title = shortcut ? `${label} (${shortcut})` : label;\n\n return (\n <LabeledButton\n className={classnames(\n 'flex flex-col gap-y-1 items-center py-2.5 px-2',\n 'text-annotator-sm leading-none text-grey-7',\n 'transition-colors duration-200',\n 'dim-item'\n )}\n onClick={onClick}\n title={title}\n >\n {icon && <Icon classes=\"text-annotator-lg\" name={icon} title={title} />}\n {typeof badgeCount === 'number' && <NumberIcon badgeCount={badgeCount} />}\n <span className=\"font-normal\">{label}</span>\n </LabeledButton>\n );\n}\n\n/**\n * Union of possible toolbar commands.\n *\n * @typedef {'annotate'|'highlight'|'show'|'hide'} Command\n */\n\n/**\n * @typedef AdderToolbarProps\n * @prop {'up'|'down'} arrowDirection -\n * Whether the arrow pointing out of the toolbar towards the selected text\n * should appear above the toolbar pointing Up or below the toolbar pointing\n * Down.\n * @prop {boolean} isVisible - Whether to show the toolbar or not.\n * @prop {(c: Command) => void} onCommand - Called when a toolbar button is clicked.\n * @prop {number} [annotationCount] -\n * Number of annotations associated with the selected text.\n * If non-zero, a \"Show\" button is displayed to allow the user to see the\n * annotations that correspond to the selection.\n */\n\n/**\n * The toolbar that is displayed above or below selected text in the document,\n * providing options to create annotations or highlights.\n *\n * @param {AdderToolbarProps} props\n */\nexport default function AdderToolbar({\n arrowDirection,\n isVisible,\n onCommand,\n annotationCount = 0,\n}) {\n // Since the selection toolbar is only shown when there is a selection\n // of static text, we can use a plain key without any modifier as\n // the shortcut. This avoids conflicts with browser/OS shortcuts.\n const annotateShortcut = isVisible ? 'a' : null;\n const highlightShortcut = isVisible ? 'h' : null;\n const showShortcut = isVisible ? 's' : null;\n const hideShortcut = isVisible ? 'Escape' : null;\n\n // Add a shortcut to close the adder. Note, there is no button associated with this\n // shortcut because any outside click will also hide the adder.\n useShortcut(hideShortcut, () => onCommand('hide'));\n\n // nb. The adder is hidden using the `visibility` property rather than `display`\n // so that we can compute its size in order to position it before display.\n return (\n <div\n className={classnames(\n 'AdderToolbar',\n 'absolute select-none bg-white rounded shadow-adder-toolbar',\n // Because `.AdderToolbar` rules reset `all:initial`, we cannot use\n // default border values from Tailwind and have to be explicit about\n // all border attributes\n 'border border-solid border-grey-3',\n // Start at a very low opacity as we're going to fade in in the animation\n 'opacity-5',\n {\n 'animate-adder-pop-up': arrowDirection === 'up' && isVisible,\n 'animate-adder-pop-down': arrowDirection === 'down' && isVisible,\n }\n )}\n dir=\"ltr\"\n style={{\n visibility: isVisible ? 'visible' : 'hidden',\n }}\n >\n <div className=\"flex dim-items-on-hover\">\n <ToolbarButton\n icon=\"annotate\"\n onClick={() => onCommand('annotate')}\n label=\"Annotate\"\n shortcut={annotateShortcut}\n />\n <ToolbarButton\n icon=\"highlight\"\n onClick={() => onCommand('highlight')}\n label=\"Highlight\"\n shortcut={highlightShortcut}\n />\n {annotationCount > 0 && (\n <>\n <div\n className={classnames(\n // Style a vertical separator line\n 'm-1.5 border-r border-grey-4 border-solid'\n )}\n />\n <ToolbarButton\n badgeCount={annotationCount}\n onClick={() => onCommand('show')}\n label=\"Show\"\n shortcut={showShortcut}\n />\n </>\n )}\n </div>\n <AdderToolbarArrow arrowDirection={arrowDirection} />\n </div>\n );\n}\n","/**\n * Helper methods to identify browser versions and os types\n */\n\n/**\n * Returns true when the OS is Mac OS.\n *\n * @param _userAgent {string} - Test seam\n */\nexport const isMacOS = (_userAgent = window.navigator.userAgent) => {\n return _userAgent.indexOf('Mac OS') >= 0;\n};\n\n/**\n * Returns true when device is iOS.\n * https://stackoverflow.com/a/9039885/14463679\n *\n * @param _navigator {{platform: string, userAgent: string}}\n * @param _ontouchend {boolean}\n */\nexport const isIOS = (\n _navigator = window.navigator,\n _ontouchend = 'ontouchend' in document\n) => {\n return (\n [\n 'iPad Simulator',\n 'iPhone Simulator',\n 'iPod Simulator',\n 'iPad',\n 'iPhone',\n 'iPod',\n ].includes(_navigator.platform) ||\n // iPad on iOS 13 detection\n (_navigator.userAgent.includes('Mac') && _ontouchend)\n );\n};\n\n/**\n * Returns true when the device is a touch device such\n * as android or iOS.\n * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer#browser_compatibility\n *\n * @param _window {Window} - Test seam\n */\nexport const isTouchDevice = (_window = window) => {\n return _window.matchMedia('(pointer: coarse)').matches;\n};\n","/**\n * Load stylesheets for annotator UI components into the shadow DOM root.\n *\n * @param {ShadowRoot} shadowRoot\n */\nfunction loadStyles(shadowRoot) {\n // Find the preloaded stylesheet added by the boot script.\n const url = /** @type {HTMLLinkElement|undefined} */ (\n document.querySelector(\n 'link[rel=\"preload\"][href*=\"/build/styles/annotator.css\"]'\n )\n )?.href;\n\n if (!url) {\n return;\n }\n\n const linkEl = document.createElement('link');\n linkEl.rel = 'stylesheet';\n linkEl.href = url;\n shadowRoot.appendChild(linkEl);\n}\n\n/**\n * Create the shadow root for an annotator UI component and load the annotator\n * CSS styles into it.\n *\n * @param {HTMLElement} container - Container element to render the UI into\n * @return {ShadowRoot}\n */\nexport function createShadowRoot(container) {\n const shadowRoot = container.attachShadow({ mode: 'open' });\n loadStyles(shadowRoot);\n\n // @ts-ignore The window doesn't know about the polyfill\n // applyFocusVisiblePolyfill comes from the `focus-visible` package.\n const applyFocusVisible = window.applyFocusVisiblePolyfill;\n if (applyFocusVisible) {\n applyFocusVisible(shadowRoot);\n }\n\n stopEventPropagation(shadowRoot);\n return shadowRoot;\n}\n\n/**\n * Stop bubbling up of several events.\n *\n * This makes the host page a little bit less aware of the annotator activity.\n * It is still possible for the host page to manipulate the events on the capturing\n * face.\n *\n * Another benefit is that click and touchstart typically causes the sidebar to close.\n * By preventing the bubble up of these events, we don't have to individually stop\n * the propagation.\n *\n * @param {HTMLElement|ShadowRoot} element\n */\nfunction stopEventPropagation(element) {\n element.addEventListener('mouseup', event => event.stopPropagation());\n element.addEventListener('mousedown', event => event.stopPropagation());\n element.addEventListener('touchstart', event => event.stopPropagation(), {\n passive: true,\n });\n}\n","import { render } from 'preact';\n\nimport AdderToolbar from './components/AdderToolbar';\nimport { isTouchDevice } from '../shared/user-agent';\nimport { createShadowRoot } from './util/shadow-root';\n\n/**\n * @typedef {1} ArrowPointingDown\n * Show the adder above the selection with an arrow pointing down at the\n * selected text.\n */\nexport const ARROW_POINTING_DOWN = 1;\n\n/**\n * @typedef {2} ArrowPointingUp\n * Show the adder above the selection with an arrow pointing up at the\n * selected text.\n */\nexport const ARROW_POINTING_UP = 2;\n\n/**\n * @typedef {ArrowPointingDown|ArrowPointingUp} ArrowDirection\n * Show the adder above the selection with an arrow pointing up at the\n * selected text.\n */\n\n/**\n * @typedef Target\n * @prop {number} left - Offset from left edge of viewport.\n * @prop {number} top - Offset from top edge of viewport.\n * @prop {ArrowDirection} arrowDirection - Direction of the adder's arrow.\n */\n\n/** @param {number} pixels */\nfunction toPx(pixels) {\n return pixels.toString() + 'px';\n}\n\nconst ARROW_HEIGHT = 10;\n\n// The preferred gap between the end of the text selection and the adder's\n// arrow position.\nconst ARROW_H_MARGIN = 20;\n\n/**\n * Return the closest ancestor of `el` which has been positioned.\n *\n * If no ancestor has been positioned, returns the root element.\n *\n * @param {Element} el\n * @return {Element}\n */\nfunction nearestPositionedAncestor(el) {\n let parentEl = /** @type {Element} */ (el.parentElement);\n while (parentEl.parentElement) {\n if (getComputedStyle(parentEl).position !== 'static') {\n break;\n }\n parentEl = parentEl.parentElement;\n }\n return parentEl;\n}\n\n/**\n * @typedef AdderOptions\n * @prop {() => void} onAnnotate - Callback invoked when \"Annotate\" button is clicked\n * @prop {() => void} onHighlight - Callback invoked when \"Highlight\" button is clicked\n * @prop {(tags: string[]) => void} onShowAnnotations -\n * Callback invoked when \"Show\" button is clicked\n *\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Container for the 'adder' toolbar which provides controls for the user to\n * annotate and highlight the selected text.\n *\n * The toolbar implementation is split between this class, which is\n * the container for the toolbar that positions it on the page and isolates\n * it from the page's styles using shadow DOM, and the `AdderToolbar` Preact\n * component which actually renders the toolbar.\n *\n * @implements {Destroyable}\n */\nexport class Adder {\n /**\n * Create the toolbar's container and hide it.\n *\n * The adder is initially hidden.\n *\n * @param {HTMLElement} element - The DOM element into which the adder will be created\n * @param {AdderOptions} options - Options object specifying `onAnnotate` and `onHighlight`\n * event handlers.\n */\n constructor(element, options) {\n this._outerContainer = document.createElement('hypothesis-adder');\n element.appendChild(this._outerContainer);\n this._shadowRoot = createShadowRoot(this._outerContainer);\n\n // Set initial style\n Object.assign(this._outerContainer.style, {\n // take position out of layout flow initially\n position: 'absolute',\n top: 0,\n left: 0,\n });\n\n this._view = /** @type {Window} */ (element.ownerDocument.defaultView);\n\n this._width = () => {\n const firstChild = /** @type {Element} */ (this._shadowRoot.firstChild);\n return firstChild.getBoundingClientRect().width;\n };\n\n this._height = () => {\n const firstChild = /** @type {Element} */ (this._shadowRoot.firstChild);\n return firstChild.getBoundingClientRect().height;\n };\n\n this._isVisible = false;\n\n /** @type {'up'|'down'} */\n this._arrowDirection = 'up';\n\n this._onAnnotate = options.onAnnotate;\n this._onHighlight = options.onHighlight;\n this._onShowAnnotations = options.onShowAnnotations;\n\n /**\n * Annotation tags associated with the current selection. If non-empty,\n * a \"Show\" button appears in the toolbar. Clicking the button calls the\n * `onShowAnnotations` callback with the current value of `annotationsForSelection`.\n *\n * @type {string[]}\n */\n this.annotationsForSelection = [];\n\n this._render();\n }\n\n /** Hide the adder */\n hide() {\n this._isVisible = false;\n this._render();\n // Reposition the outerContainer because it affects the responsiveness of host page\n // https://github.com/hypothesis/client/issues/3193\n Object.assign(this._outerContainer.style, {\n top: 0,\n left: 0,\n });\n }\n\n destroy() {\n render(null, this._shadowRoot); // First, unload the Preact component\n this._outerContainer.remove();\n }\n\n /**\n * Display the adder in the best position in order to target the\n * selected text in `selectionRect`.\n *\n * @param {DOMRect} selectionRect - The rect of text to target, in viewport\n * coordinates.\n * @param {boolean} isRTLselection - True if the selection was made\n * rigth-to-left, such that the focus point is mosty likely at the\n * top-left edge of `targetRect`.\n */\n show(selectionRect, isRTLselection) {\n const { left, top, arrowDirection } = this._calculateTarget(\n selectionRect,\n isRTLselection\n );\n this._showAt(left, top);\n\n this._isVisible = true;\n this._arrowDirection = arrowDirection === ARROW_POINTING_UP ? 'up' : 'down';\n\n this._render();\n }\n\n /**\n * Determine the best position for the Adder and its pointer-arrow.\n * - Position the pointer-arrow near the end of the selection (where the user's\n * cursor/input is most likely to be)\n * - Position the Adder to center horizontally on the pointer-arrow\n * - Position the Adder below the selection (arrow pointing up) for LTR selections\n * and above (arrow down) for RTL selections\n *\n * @param {DOMRect} selectionRect - The rect of text to target, in viewport\n * coordinates.\n * @param {boolean} isRTLselection - True if the selection was made\n * rigth-to-left, such that the focus point is mosty likely at the\n * top-left edge of `targetRect`.\n * @return {Target}\n */\n _calculateTarget(selectionRect, isRTLselection) {\n // Set the initial arrow direction based on whether the selection was made\n // forwards/upwards or downwards/backwards.\n /** @type {ArrowDirection} */ let arrowDirection;\n if (isRTLselection && !isTouchDevice()) {\n arrowDirection = ARROW_POINTING_DOWN;\n } else {\n // Render the adder below the selection for touch devices due to competing\n // space with the native copy/paste bar that typical (not always) renders above\n // the selection.\n arrowDirection = ARROW_POINTING_UP;\n }\n let top;\n let left;\n\n // Position the adder such that the arrow it is above or below the selection\n // and close to the end.\n const hMargin = Math.min(ARROW_H_MARGIN, selectionRect.width);\n const adderWidth = this._width();\n // Render the adder a little lower on touch devices to provide room for the native\n // selection handles so that the interactions with selection don't compete with the adder.\n const touchScreenOffset = isTouchDevice() ? 10 : 0;\n const adderHeight = this._height();\n if (isRTLselection) {\n left = selectionRect.left - adderWidth / 2 + hMargin;\n } else {\n left =\n selectionRect.left + selectionRect.width - adderWidth / 2 - hMargin;\n }\n\n // Flip arrow direction if adder would appear above the top or below the\n // bottom of the viewport.\n if (\n selectionRect.top - adderHeight < 0 &&\n arrowDirection === ARROW_POINTING_DOWN\n ) {\n arrowDirection = ARROW_POINTING_UP;\n } else if (selectionRect.top + adderHeight > this._view.innerHeight) {\n arrowDirection = ARROW_POINTING_DOWN;\n }\n\n if (arrowDirection === ARROW_POINTING_UP) {\n top =\n selectionRect.top +\n selectionRect.height +\n ARROW_HEIGHT +\n touchScreenOffset;\n } else {\n top = selectionRect.top - adderHeight - ARROW_HEIGHT;\n }\n\n // Constrain the adder to the viewport.\n left = Math.max(left, 0);\n left = Math.min(left, this._view.innerWidth - adderWidth);\n\n top = Math.max(top, 0);\n top = Math.min(top, this._view.innerHeight - adderHeight);\n\n return { top, left, arrowDirection };\n }\n\n /**\n * Find a Z index value that will cause the adder to appear on top of any\n * content in the document when the adder is shown at (left, top).\n *\n * @param {number} left - Horizontal offset from left edge of viewport.\n * @param {number} top - Vertical offset from top edge of viewport.\n * @return {number} - greatest zIndex (default value of 1)\n */\n _findZindex(left, top) {\n if (document.elementsFromPoint === undefined) {\n // In case of not being able to use `document.elementsFromPoint`,\n // default to the large arbitrary number (2^15)\n return 32768;\n }\n\n const adderWidth = this._width();\n const adderHeight = this._height();\n\n // Find the Z index of all the elements in the screen for five positions\n // around the adder (left-top, left-bottom, middle-center, right-top,\n // right-bottom) and use the greatest.\n\n // Unique elements so `getComputedStyle` is called the minimum amount of times.\n const elements = new Set([\n ...document.elementsFromPoint(left, top),\n ...document.elementsFromPoint(left, top + adderHeight),\n ...document.elementsFromPoint(\n left + adderWidth / 2,\n top + adderHeight / 2\n ),\n ...document.elementsFromPoint(left + adderWidth, top),\n ...document.elementsFromPoint(left + adderWidth, top + adderHeight),\n ]);\n\n const zIndexes = [...elements]\n .map(element => +getComputedStyle(element).zIndex)\n .filter(Number.isInteger);\n\n // Make sure the array contains at least one element,\n // otherwise `Math.max(...[])` results in +Infinity\n zIndexes.push(0);\n\n return Math.max(...zIndexes) + 1;\n }\n\n /**\n * Show the adder at the given position and with the arrow pointing in\n * `arrowDirection`.\n *\n * @param {number} left - Horizontal offset from left edge of viewport.\n * @param {number} top - Vertical offset from top edge of viewport.\n */\n _showAt(left, top) {\n // Translate the (left, top) viewport coordinates into positions relative to\n // the adder's nearest positioned ancestor (NPA).\n //\n // Typically the adder is a child of the `<body>` and the NPA is the root\n // `<html>` element. However page styling may make the `<body>` positioned.\n // See https://github.com/hypothesis/client/issues/487.\n const positionedAncestor = nearestPositionedAncestor(this._outerContainer);\n const parentRect = positionedAncestor.getBoundingClientRect();\n\n const zIndex = this._findZindex(left, top);\n\n Object.assign(this._outerContainer.style, {\n left: toPx(left - parentRect.left),\n top: toPx(top - parentRect.top),\n zIndex,\n });\n }\n\n _render() {\n /** @param {import('./components/AdderToolbar').Command} command */\n const handleCommand = command => {\n switch (command) {\n case 'annotate':\n this._onAnnotate();\n this.hide();\n break;\n case 'highlight':\n this._onHighlight();\n this.hide();\n break;\n case 'show':\n this._onShowAnnotations(this.annotationsForSelection);\n break;\n case 'hide':\n this.hide();\n break;\n default:\n break;\n }\n };\n\n render(\n <AdderToolbar\n isVisible={this._isVisible}\n arrowDirection={this._arrowDirection}\n onCommand={handleCommand}\n annotationCount={this.annotationsForSelection.length}\n />,\n this._shadowRoot\n );\n }\n}\n","/**\n * Return the combined length of text nodes contained in `node`.\n *\n * @param {Node} node\n */\nfunction nodeTextLength(node) {\n switch (node.nodeType) {\n case Node.ELEMENT_NODE:\n case Node.TEXT_NODE:\n // nb. `textContent` excludes text in comments and processing instructions\n // when called on a parent element, so we don't need to subtract that here.\n\n return /** @type {string} */ (node.textContent).length;\n default:\n return 0;\n }\n}\n\n/**\n * Return the total length of the text of all previous siblings of `node`.\n *\n * @param {Node} node\n */\nfunction previousSiblingsTextLength(node) {\n let sibling = node.previousSibling;\n let length = 0;\n while (sibling) {\n length += nodeTextLength(sibling);\n sibling = sibling.previousSibling;\n }\n return length;\n}\n\n/**\n * Resolve one or more character offsets within an element to (text node, position)\n * pairs.\n *\n * @param {Element} element\n * @param {number[]} offsets - Offsets, which must be sorted in ascending order\n * @return {{ node: Text, offset: number }[]}\n */\nfunction resolveOffsets(element, ...offsets) {\n let nextOffset = offsets.shift();\n const nodeIter = /** @type {Document} */ (\n element.ownerDocument\n ).createNodeIterator(element, NodeFilter.SHOW_TEXT);\n const results = [];\n\n let currentNode = nodeIter.nextNode();\n let textNode;\n let length = 0;\n\n // Find the text node containing the `nextOffset`th character from the start\n // of `element`.\n while (nextOffset !== undefined && currentNode) {\n textNode = /** @type {Text} */ (currentNode);\n if (length + textNode.data.length > nextOffset) {\n results.push({ node: textNode, offset: nextOffset - length });\n nextOffset = offsets.shift();\n } else {\n currentNode = nodeIter.nextNode();\n length += textNode.data.length;\n }\n }\n\n // Boundary case.\n while (nextOffset !== undefined && textNode && length === nextOffset) {\n results.push({ node: textNode, offset: textNode.data.length });\n nextOffset = offsets.shift();\n }\n\n if (nextOffset !== undefined) {\n throw new RangeError('Offset exceeds text length');\n }\n\n return results;\n}\n\nexport let RESOLVE_FORWARDS = 1;\nexport let RESOLVE_BACKWARDS = 2;\n\n/**\n * Represents an offset within the text content of an element.\n *\n * This position can be resolved to a specific descendant node in the current\n * DOM subtree of the element using the `resolve` method.\n */\nexport class TextPosition {\n /**\n * Construct a `TextPosition` that refers to the text position `offset` within\n * the text content of `element`.\n *\n * @param {Element} element\n * @param {number} offset\n */\n constructor(element, offset) {\n if (offset < 0) {\n throw new Error('Offset is invalid');\n }\n\n /** Element that `offset` is relative to. */\n this.element = element;\n\n /** Character offset from the start of the element's `textContent`. */\n this.offset = offset;\n }\n\n /**\n * Return a copy of this position with offset relative to a given ancestor\n * element.\n *\n * @param {Element} parent - Ancestor of `this.element`\n * @return {TextPosition}\n */\n relativeTo(parent) {\n if (!parent.contains(this.element)) {\n throw new Error('Parent is not an ancestor of current element');\n }\n\n let el = this.element;\n let offset = this.offset;\n while (el !== parent) {\n offset += previousSiblingsTextLength(el);\n el = /** @type {Element} */ (el.parentElement);\n }\n\n return new TextPosition(el, offset);\n }\n\n /**\n * Resolve the position to a specific text node and offset within that node.\n *\n * Throws if `this.offset` exceeds the length of the element's text. In the\n * case where the element has no text and `this.offset` is 0, the `direction`\n * option determines what happens.\n *\n * Offsets at the boundary between two nodes are resolved to the start of the\n * node that begins at the boundary.\n *\n * @param {object} [options]\n * @param {RESOLVE_FORWARDS|RESOLVE_BACKWARDS} [options.direction] -\n * Specifies in which direction to search for the nearest text node if\n * `this.offset` is `0` and `this.element` has no text. If not specified\n * an error is thrown.\n * @return {{ node: Text, offset: number }}\n * @throws {RangeError}\n */\n resolve(options = {}) {\n try {\n return resolveOffsets(this.element, this.offset)[0];\n } catch (err) {\n if (this.offset === 0 && options.direction !== undefined) {\n const tw = document.createTreeWalker(\n this.element.getRootNode(),\n NodeFilter.SHOW_TEXT\n );\n tw.currentNode = this.element;\n const forwards = options.direction === RESOLVE_FORWARDS;\n const text = /** @type {Text|null} */ (\n forwards ? tw.nextNode() : tw.previousNode()\n );\n if (!text) {\n throw err;\n }\n return { node: text, offset: forwards ? 0 : text.data.length };\n } else {\n throw err;\n }\n }\n }\n\n /**\n * Construct a `TextPosition` that refers to the `offset`th character within\n * `node`.\n *\n * @param {Node} node\n * @param {number} offset\n * @return {TextPosition}\n */\n static fromCharOffset(node, offset) {\n switch (node.nodeType) {\n case Node.TEXT_NODE:\n return TextPosition.fromPoint(node, offset);\n case Node.ELEMENT_NODE:\n return new TextPosition(/** @type {Element} */ (node), offset);\n default:\n throw new Error('Node is not an element or text node');\n }\n }\n\n /**\n * Construct a `TextPosition` representing the range start or end point (node, offset).\n *\n * @param {Node} node - Text or Element node\n * @param {number} offset - Offset within the node.\n * @return {TextPosition}\n */\n static fromPoint(node, offset) {\n switch (node.nodeType) {\n case Node.TEXT_NODE: {\n if (offset < 0 || offset > /** @type {Text} */ (node).data.length) {\n throw new Error('Text node offset is out of range');\n }\n\n if (!node.parentElement) {\n throw new Error('Text node has no parent');\n }\n\n // Get the offset from the start of the parent element.\n const textOffset = previousSiblingsTextLength(node) + offset;\n\n return new TextPosition(node.parentElement, textOffset);\n }\n case Node.ELEMENT_NODE: {\n if (offset < 0 || offset > node.childNodes.length) {\n throw new Error('Child node offset is out of range');\n }\n\n // Get the text length before the `offset`th child of element.\n let textOffset = 0;\n for (let i = 0; i < offset; i++) {\n textOffset += nodeTextLength(node.childNodes[i]);\n }\n\n return new TextPosition(/** @type {Element} */ (node), textOffset);\n }\n default:\n throw new Error('Point is not in an element or text node');\n }\n }\n}\n\n/**\n * Represents a region of a document as a (start, end) pair of `TextPosition` points.\n *\n * Representing a range in this way allows for changes in the DOM content of the\n * range which don't affect its text content, without affecting the text content\n * of the range itself.\n */\nexport class TextRange {\n /**\n * Construct an immutable `TextRange` from a `start` and `end` point.\n *\n * @param {TextPosition} start\n * @param {TextPosition} end\n */\n constructor(start, end) {\n this.start = start;\n this.end = end;\n }\n\n /**\n * Return a copy of this range with start and end positions relative to a\n * given ancestor. See `TextPosition.relativeTo`.\n *\n * @param {Element} element\n */\n relativeTo(element) {\n return new TextRange(\n this.start.relativeTo(element),\n this.end.relativeTo(element)\n );\n }\n\n /**\n * Resolve the `TextRange` to a DOM range.\n *\n * The resulting DOM Range will always start and end in a `Text` node.\n * Hence `TextRange.fromRange(range).toRange()` can be used to \"shrink\" a\n * range to the text it contains.\n *\n * May throw if the `start` or `end` positions cannot be resolved to a range.\n *\n * @return {Range}\n */\n toRange() {\n let start;\n let end;\n\n if (\n this.start.element === this.end.element &&\n this.start.offset <= this.end.offset\n ) {\n // Fast path for start and end points in same element.\n [start, end] = resolveOffsets(\n this.start.element,\n this.start.offset,\n this.end.offset\n );\n } else {\n start = this.start.resolve({ direction: RESOLVE_FORWARDS });\n end = this.end.resolve({ direction: RESOLVE_BACKWARDS });\n }\n\n const range = new Range();\n range.setStart(start.node, start.offset);\n range.setEnd(end.node, end.offset);\n return range;\n }\n\n /**\n * Convert an existing DOM `Range` to a `TextRange`\n *\n * @param {Range} range\n * @return {TextRange}\n */\n static fromRange(range) {\n const start = TextPosition.fromPoint(\n range.startContainer,\n range.startOffset\n );\n const end = TextPosition.fromPoint(range.endContainer, range.endOffset);\n return new TextRange(start, end);\n }\n\n /**\n * Return a `TextRange` from the `start`th to `end`th characters in `root`.\n *\n * @param {Element} root\n * @param {number} start\n * @param {number} end\n */\n static fromOffsets(root, start, end) {\n return new TextRange(\n new TextPosition(root, start),\n new TextPosition(root, end)\n );\n }\n}\n","/**\n * CSS selector that will match the placeholder within a page/tile container.\n */\nconst placeholderSelector = '.annotator-placeholder';\n\n/**\n * Create or return a placeholder element for anchoring.\n *\n * In document viewers such as PDF.js which only render a subset of long\n * documents at a time, it may not be possible to anchor annotations to the\n * actual text in pages which are off-screen. For these non-rendered pages,\n * a \"placeholder\" element is created in the approximate X/Y location (eg.\n * middle of the page) where the content will appear. Any highlights for that\n * page are then rendered inside the placeholder.\n *\n * When the viewport is scrolled to the non-rendered page, the placeholder\n * is removed and annotations are re-anchored to the real content.\n *\n * @param {HTMLElement} container - The container element for the page or tile\n * which is not rendered.\n */\nexport function createPlaceholder(container) {\n let placeholder = container.querySelector(placeholderSelector);\n if (placeholder) {\n return placeholder;\n }\n placeholder = document.createElement('span');\n placeholder.classList.add('annotator-placeholder');\n placeholder.textContent = 'Loading annotations...';\n container.appendChild(placeholder);\n return placeholder;\n}\n\n/**\n * Return true if a page/tile container has a placeholder.\n *\n * @param {HTMLElement} container\n */\nexport function hasPlaceholder(container) {\n return container.querySelector(placeholderSelector) !== null;\n}\n\n/**\n * Remove the placeholder element in `container`, if present.\n *\n * @param {HTMLElement} container\n */\nexport function removePlaceholder(container) {\n container.querySelector(placeholderSelector)?.remove();\n}\n\n/**\n * Return true if `node` is inside a placeholder element created with `createPlaceholder`.\n *\n * This is typically used to test if a highlight element associated with an\n * anchor is inside a placeholder.\n *\n * @param {Node} node\n */\nexport function isInPlaceholder(node) {\n if (!node.parentElement) {\n return false;\n }\n return node.parentElement.closest(placeholderSelector) !== null;\n}\n","/**\n * Returns true if the start point of a selection occurs after the end point,\n * in document order.\n *\n * @param {Selection} selection\n */\nexport function isSelectionBackwards(selection) {\n if (selection.focusNode === selection.anchorNode) {\n return selection.focusOffset < selection.anchorOffset;\n }\n\n const range = selection.getRangeAt(0);\n // Does not work correctly on iOS when selecting nodes backwards.\n // https://bugs.webkit.org/show_bug.cgi?id=220523\n return range.startContainer === selection.focusNode;\n}\n\n/**\n * Returns true if any part of `node` lies within `range`.\n *\n * @param {Range} range\n * @param {Node} node\n */\nexport function isNodeInRange(range, node) {\n try {\n const length = node.nodeValue?.length ?? node.childNodes.length;\n return (\n // Check start of node is before end of range.\n range.comparePoint(node, 0) <= 0 &&\n // Check end of node is after start of range.\n range.comparePoint(node, length) >= 0\n );\n } catch (e) {\n // `comparePoint` may fail if the `range` and `node` do not share a common\n // ancestor or `node` is a doctype.\n return false;\n }\n}\n\n/**\n * Iterate over all Node(s) which overlap `range` in document order and invoke\n * `callback` for each of them.\n *\n * @param {Range} range\n * @param {(n: Node) => void} callback\n */\nexport function forEachNodeInRange(range, callback) {\n const root = range.commonAncestorContainer;\n const nodeIter = /** @type {Document} */ (\n root.ownerDocument\n ).createNodeIterator(root, NodeFilter.SHOW_ALL);\n\n let currentNode;\n while ((currentNode = nodeIter.nextNode())) {\n if (isNodeInRange(range, currentNode)) {\n callback(currentNode);\n }\n }\n}\n\n/**\n * Returns the bounding rectangles of non-whitespace text nodes in `range`.\n *\n * @param {Range} range\n * @return {Array<DOMRect>} Array of bounding rects in viewport coordinates.\n */\nexport function getTextBoundingBoxes(range) {\n const whitespaceOnly = /^\\s*$/;\n const textNodes = /** @type {Text[]} */ ([]);\n forEachNodeInRange(range, node => {\n if (\n node.nodeType === Node.TEXT_NODE &&\n !(/** @type {string} */ (node.textContent).match(whitespaceOnly))\n ) {\n textNodes.push(/** @type {Text} */ (node));\n }\n });\n\n /** @type {DOMRect[]} */\n let rects = [];\n textNodes.forEach(node => {\n const nodeRange = node.ownerDocument.createRange();\n nodeRange.selectNodeContents(node);\n if (node === range.startContainer) {\n nodeRange.setStart(node, range.startOffset);\n }\n if (node === range.endContainer) {\n nodeRange.setEnd(node, range.endOffset);\n }\n if (nodeRange.collapsed) {\n // If the range ends at the start of this text node or starts at the end\n // of this node then do not include it.\n return;\n }\n\n // Measure the range and translate from viewport to document coordinates\n const viewportRects = Array.from(nodeRange.getClientRects());\n nodeRange.detach();\n rects = rects.concat(viewportRects);\n });\n return rects;\n}\n\n/**\n * Returns the rectangle, in viewport coordinates, for the line of text\n * containing the focus point of a Selection.\n *\n * Returns null if the selection is empty.\n *\n * @param {Selection} selection\n * @return {DOMRect|null}\n */\nexport function selectionFocusRect(selection) {\n if (selection.isCollapsed) {\n return null;\n }\n const textBoxes = getTextBoundingBoxes(selection.getRangeAt(0));\n if (textBoxes.length === 0) {\n return null;\n }\n\n if (isSelectionBackwards(selection)) {\n return textBoxes[0];\n } else {\n return textBoxes[textBoxes.length - 1];\n }\n}\n\n/**\n * Retrieve a set of items associated with nodes in a given range.\n *\n * An `item` can be any data that the caller wishes to compute from or associate\n * with a node. Only unique items, as determined by `Object.is`, are returned.\n *\n * @template T\n * @param {Range} range\n * @param {(n: Node) => T} itemForNode - Callback returning the item for a given node\n * @return {NonNullable<T>[]} items\n */\nexport function itemsForRange(range, itemForNode) {\n /** @type {Set<Node>} */\n const checkedNodes = new Set();\n /** @type {Set<NonNullable<T>>} */\n const items = new Set();\n\n forEachNodeInRange(range, node => {\n /** @type {Node|null} */\n let current = node;\n while (current) {\n if (checkedNodes.has(current)) {\n break;\n }\n checkedNodes.add(current);\n\n const item = /** @type {NonNullable<T>|null|undefined} */ (\n itemForNode(current)\n );\n if (item !== null && item !== undefined) {\n items.add(item);\n }\n\n current = current.parentNode;\n }\n });\n\n return [...items];\n}\n","import { isInPlaceholder } from './anchoring/placeholder';\nimport { isNodeInRange } from './range-util';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\n/**\n * Return the canvas element underneath a highlight element in a PDF page's\n * text layer.\n *\n * Returns `null` if the highlight is not above a PDF canvas.\n *\n * @param {HTMLElement} highlightEl -\n * A `<hypothesis-highlight>` element in the page's text layer\n * @return {HTMLCanvasElement|null}\n */\nfunction getPDFCanvas(highlightEl) {\n // This code assumes that PDF.js renders pages with a structure like:\n //\n // <div class=\"page\">\n // <div class=\"canvasWrapper\">\n // <canvas></canvas> <!-- The rendered PDF page -->\n // </div>\n // <div class=\"textLayer\">\n // <!-- Transparent text layer with text spans used to enable text selection -->\n // </div>\n // </div>\n //\n // It also assumes that the `highlightEl` element is somewhere under\n // the `.textLayer` div.\n\n const pageEl = highlightEl.closest('.page');\n if (!pageEl) {\n return null;\n }\n\n const canvasEl = pageEl.querySelector('.canvasWrapper > canvas');\n if (!canvasEl) {\n return null;\n }\n\n return /** @type {HTMLCanvasElement} */ (canvasEl);\n}\n\n/**\n * Draw highlights in an SVG layer overlaid on top of a PDF.js canvas.\n *\n * The created SVG elements are stored in the `svgHighlight` property of\n * each `HighlightElement`.\n *\n * @param {HighlightElement[]} highlightEls -\n * An element that wraps the highlighted text in the transparent text layer\n * above the PDF.\n */\nfunction drawHighlightsAbovePDFCanvas(highlightEls) {\n if (highlightEls.length === 0) {\n return;\n }\n\n // Get the <canvas> for the PDF page containing the highlight. We assume all\n // the highlights are on the same page.\n const canvasEl = getPDFCanvas(highlightEls[0]);\n if (!canvasEl || !canvasEl.parentElement) {\n return;\n }\n\n /** @type {SVGElement|null} */\n let svgHighlightLayer = canvasEl.parentElement.querySelector(\n '.hypothesis-highlight-layer'\n );\n\n if (!svgHighlightLayer) {\n // Create SVG layer. This must be in the same stacking context as\n // the canvas so that CSS `mix-blend-mode` can be used to control how SVG\n // content blends with the canvas below.\n svgHighlightLayer = document.createElementNS(SVG_NAMESPACE, 'svg');\n svgHighlightLayer.setAttribute('class', 'hypothesis-highlight-layer');\n canvasEl.parentElement.appendChild(svgHighlightLayer);\n\n // Overlay SVG layer above canvas.\n canvasEl.parentElement.style.position = 'relative';\n\n const svgStyle = svgHighlightLayer.style;\n svgStyle.position = 'absolute';\n svgStyle.left = '0';\n svgStyle.top = '0';\n svgStyle.width = '100%';\n svgStyle.height = '100%';\n\n // Use multiply blending so that highlights drawn on top of text darken it\n // rather than making it lighter. This improves contrast and thus readability\n // of highlighted text, especially for overlapping highlights.\n //\n // This choice optimizes for the common case of dark text on a light background.\n svgStyle.mixBlendMode = 'multiply';\n }\n\n const canvasRect = canvasEl.getBoundingClientRect();\n const highlightRects = highlightEls.map(highlightEl => {\n const highlightRect = highlightEl.getBoundingClientRect();\n\n // Create SVG element for the current highlight element.\n const rect = document.createElementNS(SVG_NAMESPACE, 'rect');\n rect.setAttribute('x', (highlightRect.left - canvasRect.left).toString());\n rect.setAttribute('y', (highlightRect.top - canvasRect.top).toString());\n rect.setAttribute('width', highlightRect.width.toString());\n rect.setAttribute('height', highlightRect.height.toString());\n rect.setAttribute('class', 'hypothesis-svg-highlight');\n\n // Make the highlight in the text layer transparent.\n highlightEl.classList.add('is-transparent');\n\n // Associate SVG element with highlight for use by `removeHighlights`.\n highlightEl.svgHighlight = rect;\n\n return rect;\n });\n\n svgHighlightLayer.append(...highlightRects);\n}\n\n/**\n * Additional properties added to text highlight HTML elements.\n *\n * @typedef HighlightProps\n * @prop {SVGElement} [svgHighlight]\n */\n\n/**\n * @typedef {HTMLElement & HighlightProps} HighlightElement\n */\n\n/**\n * Return text nodes which are entirely inside `range`.\n *\n * If a range starts or ends part-way through a text node, the node is split\n * and the part inside the range is returned.\n *\n * @param {Range} range\n * @return {Text[]}\n */\nfunction wholeTextNodesInRange(range) {\n if (range.collapsed) {\n // Exit early for an empty range to avoid an edge case that breaks the algorithm\n // below. Splitting a text node at the start of an empty range can leave the\n // range ending in the left part rather than the right part.\n return [];\n }\n\n /** @type {Node|null} */\n let root = range.commonAncestorContainer;\n if (root.nodeType !== Node.ELEMENT_NODE) {\n // If the common ancestor is not an element, set it to the parent element to\n // ensure that the loop below visits any text nodes generated by splitting\n // the common ancestor.\n //\n // Note that `parentElement` may be `null`.\n root = root.parentElement;\n }\n if (!root) {\n // If there is no root element then we won't be able to insert highlights,\n // so exit here.\n return [];\n }\n\n const textNodes = [];\n const nodeIter = /** @type {Document} */ (\n root.ownerDocument\n ).createNodeIterator(\n root,\n NodeFilter.SHOW_TEXT // Only return `Text` nodes.\n );\n let node;\n while ((node = nodeIter.nextNode())) {\n if (!isNodeInRange(range, node)) {\n continue;\n }\n let text = /** @type {Text} */ (node);\n\n if (text === range.startContainer && range.startOffset > 0) {\n // Split `text` where the range starts. The split will create a new `Text`\n // node which will be in the range and will be visited in the next loop iteration.\n text.splitText(range.startOffset);\n continue;\n }\n\n if (text === range.endContainer && range.endOffset < text.data.length) {\n // Split `text` where the range ends, leaving it as the part in the range.\n text.splitText(range.endOffset);\n }\n\n textNodes.push(text);\n }\n\n return textNodes;\n}\n\n/**\n * Wraps the DOM Nodes within the provided range with a highlight\n * element of the specified class and returns the highlight Elements.\n *\n * @param {Range} range - Range to be highlighted\n * @param {string} cssClass - A CSS class to use for the highlight\n * @return {HighlightElement[]} - Elements wrapping text in `normedRange` to add a highlight effect\n */\nexport function highlightRange(range, cssClass = 'hypothesis-highlight') {\n const textNodes = wholeTextNodesInRange(range);\n\n // Check if this range refers to a placeholder for not-yet-rendered content in\n // a PDF. These highlights should be invisible.\n const inPlaceholder = textNodes.length > 0 && isInPlaceholder(textNodes[0]);\n\n // Group text nodes into spans of adjacent nodes. If a group of text nodes are\n // adjacent, we only need to create one highlight element for the group.\n let textNodeSpans = /** @type {Text[][]} */ ([]);\n let prevNode = /** @type {Node|null} */ (null);\n let currentSpan = null;\n\n textNodes.forEach(node => {\n if (prevNode && prevNode.nextSibling === node) {\n currentSpan.push(node);\n } else {\n currentSpan = [node];\n textNodeSpans.push(currentSpan);\n }\n prevNode = node;\n });\n\n // Filter out text node spans that consist only of white space. This avoids\n // inserting highlight elements in places that can only contain a restricted\n // subset of nodes such as table rows and lists.\n const whitespace = /^\\s*$/;\n textNodeSpans = textNodeSpans.filter(span =>\n // Check for at least one text node with non-space content.\n span.some(node => !whitespace.test(node.data))\n );\n\n // Wrap each text node span with a `<hypothesis-highlight>` element.\n const highlights = /** @type {HighlightElement[]} */ ([]);\n textNodeSpans.forEach(nodes => {\n // A custom element name is used here rather than `<span>` to reduce the\n // likelihood of highlights being hidden by page styling.\n\n /** @type {HighlightElement} */\n const highlightEl = document.createElement('hypothesis-highlight');\n highlightEl.className = cssClass;\n\n const parent = /** @type {Node} */ (nodes[0].parentNode);\n parent.replaceChild(highlightEl, nodes[0]);\n nodes.forEach(node => highlightEl.appendChild(node));\n\n highlights.push(highlightEl);\n });\n\n // For PDF highlights, create the highlight effect by using an SVG placed\n // above the page's canvas rather than CSS `background-color` on the highlight\n // element. This enables more control over blending of the highlight with the\n // content below.\n //\n // Drawing these SVG highlights involves measuring the `<hypothesis-highlight>`\n // elements, so we create them only after those elements have all been created\n // to reduce the number of forced reflows. We also skip creating them for\n // unrendered pages for performance reasons.\n if (!inPlaceholder) {\n drawHighlightsAbovePDFCanvas(highlights);\n }\n\n return highlights;\n}\n\n/**\n * Replace a child `node` with `replacements`.\n *\n * nb. This is like `ChildNode.replaceWith` but it works in older browsers.\n *\n * @param {ChildNode} node\n * @param {Node[]} replacements\n */\nfunction replaceWith(node, replacements) {\n const parent = /** @type {Node} */ (node.parentNode);\n replacements.forEach(r => parent.insertBefore(r, node));\n node.remove();\n}\n\n/**\n * Remove all highlights under a given root element.\n *\n * @param {HTMLElement} root\n */\nexport function removeAllHighlights(root) {\n const highlights = Array.from(root.querySelectorAll('hypothesis-highlight'));\n removeHighlights(/** @type {HighlightElement[]} */ (highlights));\n}\n\n/**\n * Remove highlights from a range previously highlighted with `highlightRange`.\n *\n * @param {HighlightElement[]} highlights - The highlight elements returned by `highlightRange`\n */\nexport function removeHighlights(highlights) {\n for (let h of highlights) {\n if (h.parentNode) {\n const children = Array.from(h.childNodes);\n replaceWith(h, children);\n }\n\n if (h.svgHighlight) {\n h.svgHighlight.remove();\n }\n }\n}\n\n/**\n * Set whether the given highlight elements should appear \"focused\".\n *\n * A highlight can be displayed in a different (\"focused\") style to indicate\n * that it is current in some other context - for example the user has selected\n * the corresponding annotation in the sidebar.\n *\n * @param {HighlightElement[]} highlights\n * @param {boolean} focused\n */\nexport function setHighlightsFocused(highlights, focused) {\n highlights.forEach(h => {\n // In PDFs the visible highlight is created by an SVG element, so the focused\n // effect is applied to that. In other documents the effect is applied to the\n // `<hypothesis-highlight>` element.\n if (h.svgHighlight) {\n h.svgHighlight.classList.toggle('is-focused', focused);\n\n // Ensure that focused highlights are drawn above un-focused highlights\n // on the same page.\n //\n // SVG elements are rendered in document order so to achieve this we need\n // to move the element to be the last child of its parent.\n if (focused) {\n const parent = /** @type {SVGElement} */ (h.svgHighlight.parentNode);\n parent.append(h.svgHighlight);\n }\n } else {\n h.classList.toggle('hypothesis-highlight-focused', focused);\n }\n });\n}\n\n/**\n * Set whether highlights under the given root element should be visible.\n *\n * @param {HTMLElement} root\n * @param {boolean} visible\n */\nexport function setHighlightsVisible(root, visible) {\n const showHighlightsClass = 'hypothesis-highlights-always-on';\n root.classList.toggle(showHighlightsClass, visible);\n}\n\n/**\n * Get the highlight elements that contain the given node.\n *\n * @param {Node} node\n * @return {HighlightElement[]}\n */\nexport function getHighlightsContainingNode(node) {\n let el =\n node.nodeType === Node.ELEMENT_NODE\n ? /** @type {Element} */ (node)\n : node.parentElement;\n\n const highlights = [];\n\n while (el) {\n if (el.classList.contains('hypothesis-highlight')) {\n highlights.push(/** @type {HighlightElement} */ (el));\n }\n el = el.parentElement;\n }\n\n return highlights;\n}\n\n/**\n * Subset of `DOMRect` interface.\n *\n * @typedef Rect\n * @prop {number} top\n * @prop {number} left\n * @prop {number} bottom\n * @prop {number} right\n */\n\n/**\n * Get the bounding client rectangle of a collection in viewport coordinates.\n * Unfortunately, Chrome has issues ([1]) with Range.getBoundingClient rect or we\n * could just use that.\n *\n * [1] https://bugs.chromium.org/p/chromium/issues/detail?id=324437\n *\n * @param {HTMLElement[]} collection\n * @return {Rect}\n */\nexport function getBoundingClientRect(collection) {\n // Reduce the client rectangles of the highlights to a bounding box\n const rects = collection.map(\n n => /** @type {Rect} */ (n.getBoundingClientRect())\n );\n return rects.reduce((acc, r) => ({\n top: Math.min(acc.top, r.top),\n left: Math.min(acc.left, r.left),\n bottom: Math.max(acc.bottom, r.bottom),\n right: Math.max(acc.right, r.right),\n }));\n}\n","import { ListenerCollection } from '../shared/listener-collection';\n\nimport { computeAnchorPositions } from './util/buckets';\n\n/**\n * @typedef {import('../shared/messaging').PortRPC<HostToGuestEvent, GuestToHostEvent>} HostRPC\n * @typedef {import('../types/annotator').Anchor} Anchor\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n */\n\n/**\n * Communicate to the host frame when:\n *\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n *\n * @implements {Destroyable}\n */\nexport class BucketBarClient {\n /**\n * @param {object} options\n * @param {Element} options.contentContainer - The scrollable container element for the\n * document content. All of the highlights that the bucket bar's buckets point\n * at should be contained within this element.\n * @param {HostRPC} options.hostRPC\n */\n constructor({ contentContainer, hostRPC }) {\n this._hostRPC = hostRPC;\n this._updatePending = false;\n /** @type {Anchor[]} */\n this._anchors = [];\n this._listeners = new ListenerCollection();\n\n this._listeners.add(window, 'resize', () => this.update());\n this._listeners.add(window, 'scroll', () => this.update());\n this._listeners.add(contentContainer, 'scroll', () => this.update());\n }\n\n destroy() {\n this._listeners.removeAll();\n }\n\n /**\n * Notifies the BucketBar in the host frame when:\n * 1. The set of anchors has been changed (due to annotations being added or removed)\n * 2. The position of anchors relative to the viewport of the guest has changed\n *\n * Updates are debounced to reduce the overhead of gathering and sending anchor\n * position data across frames.\n *\n * @param {Anchor[]} [anchors] - pass this option when anchors are added or\n * deleted\n */\n update(anchors) {\n if (anchors) {\n this._anchors = anchors;\n }\n\n if (this._updatePending) {\n return;\n }\n\n this._updatePending = true;\n requestAnimationFrame(() => {\n const positions = computeAnchorPositions(this._anchors);\n this._hostRPC.call('anchorsChanged', positions);\n this._updatePending = false;\n });\n }\n}\n","import { getBoundingClientRect } from '../highlighter';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').AnchorPosition} AnchorPosition\n */\n\n/**\n * @typedef Bucket\n * @prop {Set<string>} tags - The annotation tags in this bucket\n * @prop {number} position - The vertical pixel offset where this bucket should\n * appear in the bucket bar\n */\n\n/**\n * @typedef BucketSet\n * @prop {Bucket} above - A single bucket containing all the annotation\n * tags whose anchors are offscreen upwards\n * @prop {Bucket} below - A single bucket containing all the annotation\n * tags which anchors are offscreen downwards\n * @prop {Bucket[]} buckets - On-screen buckets\n */\n\n/**\n * @typedef WorkingBucket\n * @prop {Set<string>} tags - The annotation tags in this bucket\n * @prop {number} position - The computed position (offset) for this bucket,\n * based on the current anchors. This is centered between `top` and `bottom`\n * @prop {number} top - The uppermost (lowest) vertical offset for the anchors\n * in this bucket — the lowest `top` position value, akin to the top offset of\n * a theoretical box drawn around all of the anchor highlights in this bucket\n * @prop {number} bottom - The bottommost (highest) vertical offset for the\n * anchors in this bucket — the highest `top` position value, akin to the\n * bottom of a theoretical box drawn around all of the anchor highlights in\n * this bucket\n */\n\n// Only anchors with top offsets between `BUCKET_TOP_THRESHOLD` and\n// `window.innerHeight - BUCKET_BOTTOM_THRESHOLD` are considered \"on-screen\"\n// and will be bucketed. This is to account for bucket-bar tool buttons (top\n// and the height of the bottom navigation bucket (bottom)\nconst BUCKET_TOP_THRESHOLD = 137;\nconst BUCKET_BOTTOM_THRESHOLD = 22;\n// Generated buckets of annotation anchor highlights should be spaced by\n// at least this amount, in pixels\nconst BUCKET_GAP_SIZE = 60;\n\n/**\n * Find the closest valid anchor in `anchors` that is offscreen in the direction\n * indicated.\n *\n * @param {Anchor[]} anchors\n * @param {'up'|'down'} direction\n * @return {Anchor|null} - The closest anchor or `null` if no valid anchor found\n */\nexport function findClosestOffscreenAnchor(anchors, direction) {\n let closestAnchor = null;\n let closestTop = 0;\n\n for (let anchor of anchors) {\n if (!anchor.highlights?.length) {\n continue;\n }\n\n const top = getBoundingClientRect(anchor.highlights).top;\n\n // Verify that the anchor is offscreen in the direction we're headed\n if (direction === 'up' && top >= BUCKET_TOP_THRESHOLD) {\n // We're headed up but the anchor is already below the\n // visible top of the bucket bar: it's not our guy\n continue;\n } else if (\n direction === 'down' &&\n top <= window.innerHeight - BUCKET_BOTTOM_THRESHOLD\n ) {\n // We're headed down but this anchor is already above\n // the usable bottom of the screen: it's not our guy\n continue;\n }\n\n if (\n !closestAnchor ||\n (direction === 'up' && top > closestTop) ||\n (direction === 'down' && top < closestTop)\n ) {\n // This anchor is either:\n // - The first anchor we've encountered off-screen in the direction\n // we're headed, or\n // - Closer to the screen than the previous `closestAnchor`\n closestAnchor = anchor;\n closestTop = top;\n }\n }\n\n return closestAnchor;\n}\n\n/**\n * Compute the top and bottom positions for the set of anchors' highlights, sorted\n * vertically, from top to bottom.\n *\n * @param {Anchor[]} anchors\n * @return {AnchorPosition[]}\n */\nexport function computeAnchorPositions(anchors) {\n /** @type {AnchorPosition[]} */\n const positions = [];\n\n anchors.forEach(({ annotation, highlights }) => {\n if (!highlights?.length) {\n return;\n }\n\n const { top, bottom } = getBoundingClientRect(highlights);\n\n if (top >= bottom) {\n // Empty rect. The highlights may be disconnected from the document or hidden.\n return;\n }\n\n positions.push({\n tag: annotation.$tag,\n top,\n bottom,\n });\n });\n\n // Sort anchors vertically from top to bottom\n positions.sort((anchor1, anchor2) => anchor1.top - anchor2.top);\n\n return positions;\n}\n\n/**\n * Compute buckets\n *\n * @param {AnchorPosition[]} anchorPositions\n * @return {BucketSet}\n */\nexport function computeBuckets(anchorPositions) {\n /** @type {Set<string>} */\n const aboveTags = new Set();\n /** @type {Set<string>} */\n const belowTags = new Set();\n /** @type {Bucket[]} */\n const buckets = [];\n\n // Hold current working anchors and positions as we build each bucket\n /** @type {WorkingBucket|null} */\n let currentBucket = null;\n\n /**\n * Create a new working bucket based on the provided `AnchorPosition`\n *\n * @param {AnchorPosition} anchorPosition\n * @return {WorkingBucket}\n */\n function newBucket({ bottom, tag, top }) {\n const anchorHeight = bottom - top;\n const bucketPosition = top + anchorHeight / 2;\n return {\n bottom,\n position: bucketPosition,\n tags: new Set([tag]),\n top,\n };\n }\n\n // Build buckets from position information\n anchorPositions.forEach(aPos => {\n if (aPos.top < BUCKET_TOP_THRESHOLD) {\n aboveTags.add(aPos.tag);\n return;\n } else if (aPos.top > window.innerHeight - BUCKET_BOTTOM_THRESHOLD) {\n belowTags.add(aPos.tag);\n return;\n }\n\n if (!currentBucket) {\n // We've encountered our first on-screen anchor position:\n // We'll need a bucket!\n currentBucket = newBucket(aPos);\n return;\n }\n // We want to contain overlapping highlights and those near each other\n // within a shared bucket\n const isContainedWithin =\n aPos.top > currentBucket.top && aPos.bottom < currentBucket.bottom;\n\n // The new anchor's position is far enough below the bottom of the current\n // bucket to justify starting a new bucket\n const isLargeGap = aPos.top - currentBucket.bottom > BUCKET_GAP_SIZE;\n\n if (isLargeGap && !isContainedWithin) {\n // We need to start a new bucket; push the working bucket and create\n // a new bucket\n buckets.push(currentBucket);\n currentBucket = newBucket(aPos);\n } else {\n // We'll add this anchor to the current working bucket and update\n // offset properties accordingly.\n // We can be confident that `aPos.top` is >= `currentBucket.top` because\n // AnchorPositions are sorted by their `top` offset — meaning that\n // `currentBucket.top` still accurately represents the `top` offset of\n // the virtual rectangle enclosing all anchors in this bucket. But\n // let's check to see if the bottom is larger/lower:\n const updatedBottom =\n aPos.bottom > currentBucket.bottom ? aPos.bottom : currentBucket.bottom;\n const updatedHeight = updatedBottom - currentBucket.top;\n\n currentBucket.tags.add(aPos.tag);\n currentBucket.bottom = updatedBottom;\n currentBucket.position = currentBucket.top + updatedHeight / 2;\n }\n });\n\n if (currentBucket) {\n buckets.push(currentBucket);\n }\n\n // Add an upper \"navigation\" bucket with offscreen-above anchors\n const above = {\n tags: aboveTags,\n position: BUCKET_TOP_THRESHOLD,\n };\n\n // Add a lower \"navigation\" bucket with offscreen-below anchors\n const below = {\n tags: belowTags,\n position: window.innerHeight - BUCKET_BOTTOM_THRESHOLD,\n };\n\n return {\n above,\n below,\n buckets,\n };\n}\n","/** @type {Set<string>} */\nlet shownWarnings = new Set();\n\n/**\n * Log a warning if it has not already been reported.\n *\n * This is useful to avoid spamming the console if a warning is emitted in a\n * context that may be called frequently.\n *\n * @param {...unknown} args -\n * Arguments to forward to `console.warn`. The arguments `toString()` values\n * are concatenated into a string key which is used to determine if the warning\n * has been logged before.\n */\nexport function warnOnce(...args) {\n const key = args.join();\n if (shownWarnings.has(key)) {\n return;\n }\n console.warn(...args);\n shownWarnings.add(key);\n}\n\nwarnOnce.reset = () => {\n shownWarnings.clear();\n};\n","import { TinyEmitter } from 'tiny-emitter';\n\nimport { warnOnce } from '../shared/warn-once';\n\n/**\n * @typedef {import('../types/annotator').FeatureFlags} IFeatureFlags\n */\n\n/**\n * List of feature flags that annotator code tests for.\n *\n * @type {string[]}\n */\nconst annotatorFlags = ['html_side_by_side'];\n\n/**\n * An observable container of feature flags.\n *\n * @implements {IFeatureFlags}\n */\nexport class FeatureFlags extends TinyEmitter {\n /**\n * @param {string[]} knownFlags - Test seam. This is a list of known flags\n * used to catch mistakes where code checks for an obsolete feature flag.\n */\n constructor(knownFlags = annotatorFlags) {\n super();\n\n /**\n * Map of flag name to enabled state.\n *\n * @type {Map<string, boolean>}\n */\n this._flags = new Map();\n this._knownFlags = knownFlags;\n }\n\n /**\n * Update the stored flags and notify observers via a \"flagsChanged\" event.\n *\n * @param {Record<string, boolean>} flags\n */\n update(flags) {\n this._flags.clear();\n for (let [flag, on] of Object.entries(flags)) {\n this._flags.set(flag, on);\n }\n this.emit('flagsChanged');\n }\n\n /**\n * Test if a feature flag is enabled.\n *\n * This will return false if the feature flags have not yet been received from\n * the backend. Code that uses a feature flag should handle subsequent changes\n * to the flag's state by listening for the \"flagsChanged\" event.\n *\n * @param {string} flag\n * @return {boolean}\n */\n flagEnabled(flag) {\n if (!this._knownFlags.includes(flag)) {\n warnOnce('Looked up unknown feature', flag);\n return false;\n }\n return this._flags.get(flag) ?? false;\n }\n\n /**\n * Return the state of all feature flags.\n *\n * To test whether an individual flag is enabled, use {@link flagEnabled}\n * instead.\n */\n allFlags() {\n return Object.fromEntries(this._flags);\n }\n}\n","/**\n * Implementation of Myers' online approximate string matching algorithm [1],\n * with additional optimizations suggested by [2].\n *\n * This has O((k/w) * n) expected-time where `n` is the length of the\n * text, `k` is the maximum number of errors allowed (always <= the pattern\n * length) and `w` is the word size. Because JS only supports bitwise operations\n * on 32 bit integers, `w` is 32.\n *\n * As far as I am aware, there aren't any online algorithms which are\n * significantly better for a wide range of input parameters. The problem can be\n * solved faster using \"filter then verify\" approaches which first filter out\n * regions of the text that cannot match using a \"cheap\" check and then verify\n * the remaining potential matches. The verify step requires an algorithm such\n * as this one however.\n *\n * The algorithm's approach is essentially to optimize the classic dynamic\n * programming solution to the problem by computing columns of the matrix in\n * word-sized chunks (ie. dealing with 32 chars of the pattern at a time) and\n * avoiding calculating regions of the matrix where the minimum error count is\n * guaranteed to exceed the input threshold.\n *\n * The paper consists of two parts, the first describes the core algorithm for\n * matching patterns <= the size of a word (implemented by `advanceBlock` here).\n * The second uses the core algorithm as part of a larger block-based algorithm\n * to handle longer patterns.\n *\n * [1] G. Myers, “A Fast Bit-Vector Algorithm for Approximate String Matching\n * Based on Dynamic Programming,” vol. 46, no. 3, pp. 395–415, 1999.\n *\n * [2] Šošić, M. (2014). An simd dynamic programming c/c++ library (Doctoral\n * dissertation, Fakultet Elektrotehnike i računarstva, Sveučilište u Zagrebu).\n */\nfunction reverse(s) {\n return s.split(\"\").reverse().join(\"\");\n}\n/**\n * Given the ends of approximate matches for `pattern` in `text`, find\n * the start of the matches.\n *\n * @param findEndFn - Function for finding the end of matches in\n * text.\n * @return Matches with the `start` property set.\n */\nfunction findMatchStarts(text, pattern, matches) {\n const patRev = reverse(pattern);\n return matches.map((m) => {\n // Find start of each match by reversing the pattern and matching segment\n // of text and searching for an approx match with the same number of\n // errors.\n const minStart = Math.max(0, m.end - pattern.length - m.errors);\n const textRev = reverse(text.slice(minStart, m.end));\n // If there are multiple possible start points, choose the one that\n // maximizes the length of the match.\n const start = findMatchEnds(textRev, patRev, m.errors).reduce((min, rm) => {\n if (m.end - rm.end < min) {\n return m.end - rm.end;\n }\n return min;\n }, m.end);\n return {\n start,\n end: m.end,\n errors: m.errors,\n };\n });\n}\n/**\n * Return 1 if a number is non-zero or zero otherwise, without using\n * conditional operators.\n *\n * This should get inlined into `advanceBlock` below by the JIT.\n *\n * Adapted from https://stackoverflow.com/a/3912218/434243\n */\nfunction oneIfNotZero(n) {\n return ((n | -n) >> 31) & 1;\n}\n/**\n * Block calculation step of the algorithm.\n *\n * From Fig 8. on p. 408 of [1], additionally optimized to replace conditional\n * checks with bitwise operations as per Section 4.2.3 of [2].\n *\n * @param ctx - The pattern context object\n * @param peq - The `peq` array for the current character (`ctx.peq.get(ch)`)\n * @param b - The block level\n * @param hIn - Horizontal input delta ∈ {1,0,-1}\n * @return Horizontal output delta ∈ {1,0,-1}\n */\nfunction advanceBlock(ctx, peq, b, hIn) {\n let pV = ctx.P[b];\n let mV = ctx.M[b];\n const hInIsNegative = hIn >>> 31; // 1 if hIn < 0 or 0 otherwise.\n const eq = peq[b] | hInIsNegative;\n // Step 1: Compute horizontal deltas.\n const xV = eq | mV;\n const xH = (((eq & pV) + pV) ^ pV) | eq;\n let pH = mV | ~(xH | pV);\n let mH = pV & xH;\n // Step 2: Update score (value of last row of this block).\n const hOut = oneIfNotZero(pH & ctx.lastRowMask[b]) -\n oneIfNotZero(mH & ctx.lastRowMask[b]);\n // Step 3: Update vertical deltas for use when processing next char.\n pH <<= 1;\n mH <<= 1;\n mH |= hInIsNegative;\n pH |= oneIfNotZero(hIn) - hInIsNegative; // set pH[0] if hIn > 0\n pV = mH | ~(xV | pH);\n mV = pH & xV;\n ctx.P[b] = pV;\n ctx.M[b] = mV;\n return hOut;\n}\n/**\n * Find the ends and error counts for matches of `pattern` in `text`.\n *\n * Only the matches with the lowest error count are reported. Other matches\n * with error counts <= maxErrors are discarded.\n *\n * This is the block-based search algorithm from Fig. 9 on p.410 of [1].\n */\nfunction findMatchEnds(text, pattern, maxErrors) {\n if (pattern.length === 0) {\n return [];\n }\n // Clamp error count so we can rely on the `maxErrors` and `pattern.length`\n // rows being in the same block below.\n maxErrors = Math.min(maxErrors, pattern.length);\n const matches = [];\n // Word size.\n const w = 32;\n // Index of maximum block level.\n const bMax = Math.ceil(pattern.length / w) - 1;\n // Context used across block calculations.\n const ctx = {\n P: new Uint32Array(bMax + 1),\n M: new Uint32Array(bMax + 1),\n lastRowMask: new Uint32Array(bMax + 1),\n };\n ctx.lastRowMask.fill(1 << 31);\n ctx.lastRowMask[bMax] = 1 << (pattern.length - 1) % w;\n // Dummy \"peq\" array for chars in the text which do not occur in the pattern.\n const emptyPeq = new Uint32Array(bMax + 1);\n // Map of UTF-16 character code to bit vector indicating positions in the\n // pattern that equal that character.\n const peq = new Map();\n // Version of `peq` that only stores mappings for small characters. This\n // allows faster lookups when iterating through the text because a simple\n // array lookup can be done instead of a hash table lookup.\n const asciiPeq = [];\n for (let i = 0; i < 256; i++) {\n asciiPeq.push(emptyPeq);\n }\n // Calculate `ctx.peq` - a map of character values to bitmasks indicating\n // positions of that character within the pattern, where each bit represents\n // a position in the pattern.\n for (let c = 0; c < pattern.length; c += 1) {\n const val = pattern.charCodeAt(c);\n if (peq.has(val)) {\n // Duplicate char in pattern.\n continue;\n }\n const charPeq = new Uint32Array(bMax + 1);\n peq.set(val, charPeq);\n if (val < asciiPeq.length) {\n asciiPeq[val] = charPeq;\n }\n for (let b = 0; b <= bMax; b += 1) {\n charPeq[b] = 0;\n // Set all the bits where the pattern matches the current char (ch).\n // For indexes beyond the end of the pattern, always set the bit as if the\n // pattern contained a wildcard char in that position.\n for (let r = 0; r < w; r += 1) {\n const idx = b * w + r;\n if (idx >= pattern.length) {\n continue;\n }\n const match = pattern.charCodeAt(idx) === val;\n if (match) {\n charPeq[b] |= 1 << r;\n }\n }\n }\n }\n // Index of last-active block level in the column.\n let y = Math.max(0, Math.ceil(maxErrors / w) - 1);\n // Initialize maximum error count at bottom of each block.\n const score = new Uint32Array(bMax + 1);\n for (let b = 0; b <= y; b += 1) {\n score[b] = (b + 1) * w;\n }\n score[bMax] = pattern.length;\n // Initialize vertical deltas for each block.\n for (let b = 0; b <= y; b += 1) {\n ctx.P[b] = ~0;\n ctx.M[b] = 0;\n }\n // Process each char of the text, computing the error count for `w` chars of\n // the pattern at a time.\n for (let j = 0; j < text.length; j += 1) {\n // Lookup the bitmask representing the positions of the current char from\n // the text within the pattern.\n const charCode = text.charCodeAt(j);\n let charPeq;\n if (charCode < asciiPeq.length) {\n // Fast array lookup.\n charPeq = asciiPeq[charCode];\n }\n else {\n // Slower hash table lookup.\n charPeq = peq.get(charCode);\n if (typeof charPeq === \"undefined\") {\n charPeq = emptyPeq;\n }\n }\n // Calculate error count for blocks that we definitely have to process for\n // this column.\n let carry = 0;\n for (let b = 0; b <= y; b += 1) {\n carry = advanceBlock(ctx, charPeq, b, carry);\n score[b] += carry;\n }\n // Check if we also need to compute an additional block, or if we can reduce\n // the number of blocks processed for the next column.\n if (score[y] - carry <= maxErrors &&\n y < bMax &&\n (charPeq[y + 1] & 1 || carry < 0)) {\n // Error count for bottom block is under threshold, increase the number of\n // blocks processed for this column & next by 1.\n y += 1;\n ctx.P[y] = ~0;\n ctx.M[y] = 0;\n let maxBlockScore;\n if (y === bMax) {\n const remainder = pattern.length % w;\n maxBlockScore = remainder === 0 ? w : remainder;\n }\n else {\n maxBlockScore = w;\n }\n score[y] =\n score[y - 1] +\n maxBlockScore -\n carry +\n advanceBlock(ctx, charPeq, y, carry);\n }\n else {\n // Error count for bottom block exceeds threshold, reduce the number of\n // blocks processed for the next column.\n while (y > 0 && score[y] >= maxErrors + w) {\n y -= 1;\n }\n }\n // If error count is under threshold, report a match.\n if (y === bMax && score[y] <= maxErrors) {\n if (score[y] < maxErrors) {\n // Discard any earlier, worse matches.\n matches.splice(0, matches.length);\n }\n matches.push({\n start: -1,\n end: j + 1,\n errors: score[y],\n });\n // Because `search` only reports the matches with the lowest error count,\n // we can \"ratchet down\" the max error threshold whenever a match is\n // encountered and thereby save a small amount of work for the remainder\n // of the text.\n maxErrors = score[y];\n }\n }\n return matches;\n}\n/**\n * Search for matches for `pattern` in `text` allowing up to `maxErrors` errors.\n *\n * Returns the start, and end positions and error counts for each lowest-cost\n * match. Only the \"best\" matches are returned.\n */\nexport default function search(text, pattern, maxErrors) {\n const matches = findMatchEnds(text, pattern, maxErrors);\n return findMatchStarts(text, pattern, matches);\n}\n","import approxSearch from 'approx-string-match';\n\n/**\n * @typedef {import('approx-string-match').Match} StringMatch\n */\n\n/**\n * @typedef Match\n * @prop {number} start - Start offset of match in text\n * @prop {number} end - End offset of match in text\n * @prop {number} score -\n * Score for the match between 0 and 1.0, where 1.0 indicates a perfect match\n * for the quote and context.\n */\n\n/**\n * Find the best approximate matches for `str` in `text` allowing up to `maxErrors` errors.\n *\n * @param {string} text\n * @param {string} str\n * @param {number} maxErrors\n * @return {StringMatch[]}\n */\nfunction search(text, str, maxErrors) {\n // Do a fast search for exact matches. The `approx-string-match` library\n // doesn't currently incorporate this optimization itself.\n let matchPos = 0;\n let exactMatches = [];\n while (matchPos !== -1) {\n matchPos = text.indexOf(str, matchPos);\n if (matchPos !== -1) {\n exactMatches.push({\n start: matchPos,\n end: matchPos + str.length,\n errors: 0,\n });\n matchPos += 1;\n }\n }\n if (exactMatches.length > 0) {\n return exactMatches;\n }\n\n // If there are no exact matches, do a more expensive search for matches\n // with errors.\n return approxSearch(text, str, maxErrors);\n}\n\n/**\n * Compute a score between 0 and 1.0 for the similarity between `text` and `str`.\n *\n * @param {string} text\n * @param {string} str\n */\nfunction textMatchScore(text, str) {\n // `search` will return no matches if either the text or pattern is empty,\n // otherwise it will return at least one match if the max allowed error count\n // is at least `str.length`.\n if (str.length === 0 || text.length === 0) {\n return 0.0;\n }\n\n const matches = search(text, str, str.length);\n\n // prettier-ignore\n return 1 - (matches[0].errors / str.length);\n}\n\n/**\n * Find the best approximate match for `quote` in `text`.\n *\n * Returns `null` if no match exceeding the minimum quality threshold was found.\n *\n * @param {string} text - Document text to search\n * @param {string} quote - String to find within `text`\n * @param {object} context -\n * Context in which the quote originally appeared. This is used to choose the\n * best match.\n * @param {string} [context.prefix] - Expected text before the quote\n * @param {string} [context.suffix] - Expected text after the quote\n * @param {number} [context.hint] - Expected offset of match within text\n * @return {Match|null}\n */\nexport function matchQuote(text, quote, context = {}) {\n if (quote.length === 0) {\n return null;\n }\n\n // Choose the maximum number of errors to allow for the initial search.\n // This choice involves a tradeoff between:\n //\n // - Recall (proportion of \"good\" matches found)\n // - Precision (proportion of matches found which are \"good\")\n // - Cost of the initial search and of processing the candidate matches [1]\n //\n // [1] Specifically, the expected-time complexity of the initial search is\n // `O((maxErrors / 32) * text.length)`. See `approx-string-match` docs.\n const maxErrors = Math.min(256, quote.length / 2);\n\n // Find closest matches for `quote` in `text` based on edit distance.\n const matches = search(text, quote, maxErrors);\n\n if (matches.length === 0) {\n return null;\n }\n\n /**\n * Compute a score between 0 and 1.0 for a match candidate.\n *\n * @param {StringMatch} match\n */\n const scoreMatch = match => {\n const quoteWeight = 50; // Similarity of matched text to quote.\n const prefixWeight = 20; // Similarity of text before matched text to `context.prefix`.\n const suffixWeight = 20; // Similarity of text after matched text to `context.suffix`.\n const posWeight = 2; // Proximity to expected location. Used as a tie-breaker.\n\n const quoteScore = 1 - match.errors / quote.length;\n\n const prefixScore = context.prefix\n ? textMatchScore(\n text.slice(\n Math.max(0, match.start - context.prefix.length),\n match.start\n ),\n context.prefix\n )\n : 1.0;\n const suffixScore = context.suffix\n ? textMatchScore(\n text.slice(match.end, match.end + context.suffix.length),\n context.suffix\n )\n : 1.0;\n\n let posScore = 1.0;\n if (typeof context.hint === 'number') {\n const offset = Math.abs(match.start - context.hint);\n posScore = 1.0 - offset / text.length;\n }\n\n const rawScore =\n quoteWeight * quoteScore +\n prefixWeight * prefixScore +\n suffixWeight * suffixScore +\n posWeight * posScore;\n const maxScore = quoteWeight + prefixWeight + suffixWeight + posWeight;\n const normalizedScore = rawScore / maxScore;\n\n return normalizedScore;\n };\n\n // Rank matches based on similarity of actual and expected surrounding text\n // and actual/expected offset in the document text.\n const scoredMatches = matches.map(m => ({\n start: m.start,\n end: m.end,\n score: scoreMatch(m),\n }));\n\n // Choose match with highest score.\n scoredMatches.sort((a, b) => b.score - a.score);\n return scoredMatches[0];\n}\n","/**\n * Get the node name for use in generating an xpath expression.\n *\n * @param {Node} node\n */\nfunction getNodeName(node) {\n const nodeName = node.nodeName.toLowerCase();\n let result = nodeName;\n if (nodeName === '#text') {\n result = 'text()';\n }\n return result;\n}\n\n/**\n * Get the index of the node as it appears in its parent's child list\n *\n * @param {Node} node\n */\nfunction getNodePosition(node) {\n let pos = 0;\n /** @type {Node|null} */\n let tmp = node;\n while (tmp) {\n if (tmp.nodeName === node.nodeName) {\n pos += 1;\n }\n tmp = tmp.previousSibling;\n }\n return pos;\n}\n\n/** @param {Node} node */\nfunction getPathSegment(node) {\n const name = getNodeName(node);\n const pos = getNodePosition(node);\n return `${name}[${pos}]`;\n}\n\n/**\n * A simple XPath generator which can generate XPaths of the form\n * /tag[index]/tag[index].\n *\n * @param {Node} node - The node to generate a path to\n * @param {Node} root - Root node to which the returned path is relative\n */\nexport function xpathFromNode(node, root) {\n let xpath = '';\n\n /** @type {Node|null} */\n let elem = node;\n while (elem !== root) {\n if (!elem) {\n throw new Error('Node is not a descendant of root');\n }\n xpath = getPathSegment(elem) + '/' + xpath;\n elem = elem.parentNode;\n }\n xpath = '/' + xpath;\n xpath = xpath.replace(/\\/$/, ''); // Remove trailing slash\n\n return xpath;\n}\n\n/**\n * Return the `index`'th immediate child of `element` whose tag name is\n * `nodeName` (case insensitive).\n *\n * @param {Element} element\n * @param {string} nodeName\n * @param {number} index\n */\nfunction nthChildOfType(element, nodeName, index) {\n nodeName = nodeName.toUpperCase();\n\n let matchIndex = -1;\n for (let i = 0; i < element.children.length; i++) {\n const child = element.children[i];\n if (child.nodeName.toUpperCase() === nodeName) {\n ++matchIndex;\n if (matchIndex === index) {\n return child;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Evaluate a _simple XPath_ relative to a `root` element and return the\n * matching element.\n *\n * A _simple XPath_ is a sequence of one or more `/tagName[index]` strings.\n *\n * Unlike `document.evaluate` this function:\n *\n * - Only supports simple XPaths\n * - Is not affected by the document's _type_ (HTML or XML/XHTML)\n * - Ignores element namespaces when matching element names in the XPath against\n * elements in the DOM tree\n * - Is case insensitive for all elements, not just HTML elements\n *\n * The matching element is returned or `null` if no such element is found.\n * An error is thrown if `xpath` is not a simple XPath.\n *\n * @param {string} xpath\n * @param {Element} root\n * @return {Element|null}\n */\nfunction evaluateSimpleXPath(xpath, root) {\n const isSimpleXPath =\n xpath.match(/^(\\/[A-Za-z0-9-]+(\\[[0-9]+\\])?)+$/) !== null;\n if (!isSimpleXPath) {\n throw new Error('Expression is not a simple XPath');\n }\n\n const segments = xpath.split('/');\n let element = root;\n\n // Remove leading empty segment. The regex above validates that the XPath\n // has at least two segments, with the first being empty and the others non-empty.\n segments.shift();\n\n for (let segment of segments) {\n let elementName;\n let elementIndex;\n\n const separatorPos = segment.indexOf('[');\n if (separatorPos !== -1) {\n elementName = segment.slice(0, separatorPos);\n\n const indexStr = segment.slice(separatorPos + 1, segment.indexOf(']'));\n elementIndex = parseInt(indexStr) - 1;\n if (elementIndex < 0) {\n return null;\n }\n } else {\n elementName = segment;\n elementIndex = 0;\n }\n\n const child = nthChildOfType(element, elementName, elementIndex);\n if (!child) {\n return null;\n }\n\n element = child;\n }\n\n return element;\n}\n\n/**\n * Finds an element node using an XPath relative to `root`\n *\n * Example:\n * node = nodeFromXPath('/main/article[1]/p[3]', document.body)\n *\n * @param {string} xpath\n * @param {Element} [root]\n * @return {Node|null}\n */\nexport function nodeFromXPath(xpath, root = document.body) {\n try {\n return evaluateSimpleXPath(xpath, root);\n } catch (err) {\n return document.evaluate(\n '.' + xpath,\n root,\n\n // nb. The `namespaceResolver` and `result` arguments are optional in the spec\n // but required in Edge Legacy.\n null /* namespaceResolver */,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null /* result */\n ).singleNodeValue;\n }\n}\n","/**\n * This module exports a set of classes for converting between DOM `Range`\n * objects and different types of selectors. It is mostly a thin wrapper around a\n * set of anchoring libraries. It serves two main purposes:\n *\n * 1. Providing a consistent interface across different types of anchors.\n * 2. Insulating the rest of the code from API changes in the underlying anchoring\n * libraries.\n */\n\nimport { matchQuote } from './match-quote';\nimport { TextRange, TextPosition } from './text-range';\nimport { nodeFromXPath, xpathFromNode } from './xpath';\n\n/**\n * @typedef {import('../../types/api').RangeSelector} RangeSelector\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n */\n\n/**\n * Converts between `RangeSelector` selectors and `Range` objects.\n */\nexport class RangeAnchor {\n /**\n * @param {Node} root - A root element from which to anchor.\n * @param {Range} range - A range describing the anchor.\n */\n constructor(root, range) {\n this.root = root;\n this.range = range;\n }\n\n /**\n * @param {Node} root - A root element from which to anchor.\n * @param {Range} range - A range describing the anchor.\n */\n static fromRange(root, range) {\n return new RangeAnchor(root, range);\n }\n\n /**\n * Create an anchor from a serialized `RangeSelector` selector.\n *\n * @param {Element} root - A root element from which to anchor.\n * @param {RangeSelector} selector\n */\n static fromSelector(root, selector) {\n const startContainer = nodeFromXPath(selector.startContainer, root);\n if (!startContainer) {\n throw new Error('Failed to resolve startContainer XPath');\n }\n\n const endContainer = nodeFromXPath(selector.endContainer, root);\n if (!endContainer) {\n throw new Error('Failed to resolve endContainer XPath');\n }\n\n const startPos = TextPosition.fromCharOffset(\n startContainer,\n selector.startOffset\n );\n const endPos = TextPosition.fromCharOffset(\n endContainer,\n selector.endOffset\n );\n\n const range = new TextRange(startPos, endPos).toRange();\n return new RangeAnchor(root, range);\n }\n\n toRange() {\n return this.range;\n }\n\n /**\n * @return {RangeSelector}\n */\n toSelector() {\n // \"Shrink\" the range so that it tightly wraps its text. This ensures more\n // predictable output for a given text selection.\n const normalizedRange = TextRange.fromRange(this.range).toRange();\n\n const textRange = TextRange.fromRange(normalizedRange);\n const startContainer = xpathFromNode(textRange.start.element, this.root);\n const endContainer = xpathFromNode(textRange.end.element, this.root);\n\n return {\n type: 'RangeSelector',\n startContainer,\n startOffset: textRange.start.offset,\n endContainer,\n endOffset: textRange.end.offset,\n };\n }\n}\n\n/**\n * Converts between `TextPositionSelector` selectors and `Range` objects.\n */\nexport class TextPositionAnchor {\n /**\n * @param {Element} root\n * @param {number} start\n * @param {number} end\n */\n constructor(root, start, end) {\n this.root = root;\n this.start = start;\n this.end = end;\n }\n\n /**\n * @param {Element} root\n * @param {Range} range\n */\n static fromRange(root, range) {\n const textRange = TextRange.fromRange(range).relativeTo(root);\n return new TextPositionAnchor(\n root,\n textRange.start.offset,\n textRange.end.offset\n );\n }\n /**\n * @param {Element} root\n * @param {TextPositionSelector} selector\n */\n static fromSelector(root, selector) {\n return new TextPositionAnchor(root, selector.start, selector.end);\n }\n\n /**\n * @return {TextPositionSelector}\n */\n toSelector() {\n return {\n type: 'TextPositionSelector',\n start: this.start,\n end: this.end,\n };\n }\n\n toRange() {\n return TextRange.fromOffsets(this.root, this.start, this.end).toRange();\n }\n}\n\n/**\n * @typedef QuoteMatchOptions\n * @prop {number} [hint] - Expected position of match in text. See `matchQuote`.\n */\n\n/**\n * Converts between `TextQuoteSelector` selectors and `Range` objects.\n */\nexport class TextQuoteAnchor {\n /**\n * @param {Element} root - A root element from which to anchor.\n * @param {string} exact\n * @param {object} context\n * @param {string} [context.prefix]\n * @param {string} [context.suffix]\n */\n constructor(root, exact, context = {}) {\n this.root = root;\n this.exact = exact;\n this.context = context;\n }\n\n /**\n * Create a `TextQuoteAnchor` from a range.\n *\n * Will throw if `range` does not contain any text nodes.\n *\n * @param {Element} root\n * @param {Range} range\n */\n static fromRange(root, range) {\n const text = /** @type {string} */ (root.textContent);\n const textRange = TextRange.fromRange(range).relativeTo(root);\n\n const start = textRange.start.offset;\n const end = textRange.end.offset;\n\n // Number of characters around the quote to capture as context. We currently\n // always use a fixed amount, but it would be better if this code was aware\n // of logical boundaries in the document (paragraph, article etc.) to avoid\n // capturing text unrelated to the quote.\n //\n // In regular prose the ideal content would often be the surrounding sentence.\n // This is a natural unit of meaning which enables displaying quotes in\n // context even when the document is not available. We could use `Intl.Segmenter`\n // for this when available.\n const contextLen = 32;\n\n return new TextQuoteAnchor(root, text.slice(start, end), {\n prefix: text.slice(Math.max(0, start - contextLen), start),\n suffix: text.slice(end, Math.min(text.length, end + contextLen)),\n });\n }\n\n /**\n * @param {Element} root\n * @param {TextQuoteSelector} selector\n */\n static fromSelector(root, selector) {\n const { prefix, suffix } = selector;\n return new TextQuoteAnchor(root, selector.exact, { prefix, suffix });\n }\n\n /**\n * @return {TextQuoteSelector}\n */\n toSelector() {\n return {\n type: 'TextQuoteSelector',\n exact: this.exact,\n prefix: this.context.prefix,\n suffix: this.context.suffix,\n };\n }\n\n /**\n * @param {QuoteMatchOptions} [options]\n */\n toRange(options = {}) {\n return this.toPositionAnchor(options).toRange();\n }\n\n /**\n * @param {QuoteMatchOptions} [options]\n */\n toPositionAnchor(options = {}) {\n const text = /** @type {string} */ (this.root.textContent);\n const match = matchQuote(text, this.exact, {\n ...this.context,\n hint: options.hint,\n });\n if (!match) {\n throw new Error('Quote not found');\n }\n return new TextPositionAnchor(this.root, match.start, match.end);\n }\n}\n","import { RangeAnchor, TextPositionAnchor, TextQuoteAnchor } from './types';\n\n/**\n * @typedef {import('../../types/api').RangeSelector} RangeSelector\n * @typedef {import('../../types/api').Selector} Selector\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n */\n\n/**\n * @param {RangeAnchor|TextPositionAnchor|TextQuoteAnchor} anchor\n * @param {object} [options]\n * @param {number} [options.hint]\n */\nasync function querySelector(anchor, options = {}) {\n return anchor.toRange(options);\n}\n\n/**\n * Anchor a set of selectors.\n *\n * This function converts a set of selectors into a document range.\n * It encapsulates the core anchoring algorithm, using the selectors alone or\n * in combination to establish the best anchor within the document.\n *\n * @param {Element} root - The root element of the anchoring context.\n * @param {Selector[]} selectors - The selectors to try.\n * @param {object} [options]\n * @param {number} [options.hint]\n */\nexport function anchor(root, selectors, options = {}) {\n let position = /** @type {TextPositionSelector|null} */ (null);\n let quote = /** @type {TextQuoteSelector|null} */ (null);\n let range = /** @type {RangeSelector|null} */ (null);\n\n // Collect all the selectors\n for (let selector of selectors) {\n switch (selector.type) {\n case 'TextPositionSelector':\n position = selector;\n options.hint = position.start; // TextQuoteAnchor hint\n break;\n case 'TextQuoteSelector':\n quote = selector;\n break;\n case 'RangeSelector':\n range = selector;\n break;\n }\n }\n\n /**\n * Assert the quote matches the stored quote, if applicable\n * @param {Range} range\n */\n const maybeAssertQuote = range => {\n if (quote?.exact && range.toString() !== quote.exact) {\n throw new Error('quote mismatch');\n } else {\n return range;\n }\n };\n\n // From a default of failure, we build up catch clauses to try selectors in\n // order, from simple to complex.\n /** @type {Promise<Range>} */\n let promise = Promise.reject('unable to anchor');\n\n if (range) {\n // Const binding assures TS that it won't be re-assigned when callback runs.\n const range_ = range;\n promise = promise.catch(() => {\n let anchor = RangeAnchor.fromSelector(root, range_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (position) {\n const position_ = position;\n promise = promise.catch(() => {\n let anchor = TextPositionAnchor.fromSelector(root, position_);\n return querySelector(anchor, options).then(maybeAssertQuote);\n });\n }\n\n if (quote) {\n const quote_ = quote;\n promise = promise.catch(() => {\n let anchor = TextQuoteAnchor.fromSelector(root, quote_);\n return querySelector(anchor, options);\n });\n }\n\n return promise;\n}\n\n/**\n * @param {Element} root\n * @param {Range} range\n */\nexport function describe(root, range) {\n const types = [RangeAnchor, TextPositionAnchor, TextQuoteAnchor];\n const result = [];\n for (let type of types) {\n try {\n const anchor = type.fromRange(root, range);\n result.push(anchor.toSelector());\n } catch (error) {\n continue;\n }\n }\n return result;\n}\n","/**\n * Return a normalized version of a URI.\n *\n * This makes it absolute and strips the fragment identifier.\n *\n * @param {string} uri - Relative or absolute URL\n * @param {string} [base] - Base URL to resolve relative to. Defaults to\n * the document's base URL.\n */\nexport function normalizeURI(uri, base = document.baseURI) {\n const absUrl = new URL(uri, base).href;\n\n // Remove the fragment identifier.\n // This is done on the serialized URL rather than modifying `url.hash` due to\n // a bug in Safari.\n // See https://github.com/hypothesis/h/issues/3471#issuecomment-226713750\n return absUrl.toString().replace(/#.*/, '');\n}\n","/*\n ** Adapted from:\n ** https://github.com/openannotation/annotator/blob/v1.2.x/src/plugin/document.coffee\n **\n ** Annotator v1.2.10\n ** https://github.com/openannotation/annotator\n **\n ** Copyright 2015, the Annotator project contributors.\n ** Dual licensed under the MIT and GPLv3 licenses.\n ** https://github.com/openannotation/annotator/blob/master/LICENSE\n */\n\n/**\n * nb. The `DocumentMetadata` type is renamed to avoid a conflict with the\n * `DocumentMetadata` class below.\n *\n * @typedef {import('../../types/annotator').DocumentMetadata} Metadata\n */\n\nimport { normalizeURI } from '../util/url';\n\n/**\n * @typedef Link\n * @prop {string} link.href\n * @prop {string} [link.rel]\n * @prop {string} [link.type]\n */\n\n/**\n * Extension of the `Metadata` type with non-optional fields for `dc`, `eprints` etc.\n *\n * @typedef HTMLDocumentMetadata\n * @prop {string} title\n * @prop {Link[]} link\n * @prop {Record<string, string[]>} dc\n * @prop {Record<string, string[]>} eprints\n * @prop {Record<string, string[]>} facebook\n * @prop {Record<string, string[]>} highwire\n * @prop {Record<string, string[]>} prism\n * @prop {Record<string, string[]>} twitter\n * @prop {string} [favicon]\n * @prop {string} [documentFingerprint]\n */\n\n/**\n * HTMLMetadata reads metadata/links from the current HTML document.\n */\nexport class HTMLMetadata {\n /**\n * @param {object} [options]\n * @param {Document} [options.document]\n */\n constructor(options = {}) {\n this.document = options.document || document;\n }\n\n /**\n * Returns the primary URI for the document being annotated\n *\n * @return {string}\n */\n uri() {\n let uri = decodeURIComponent(this._getDocumentHref());\n\n // Use the `link[rel=canonical]` element's href as the URL if present.\n const links = this._getLinks();\n for (let link of links) {\n if (link.rel === 'canonical') {\n uri = link.href;\n }\n }\n\n return uri;\n }\n\n /**\n * Return metadata for the current page.\n *\n * @return {HTMLDocumentMetadata}\n */\n getDocumentMetadata() {\n /** @type {HTMLDocumentMetadata} */\n const metadata = {\n title: document.title,\n link: [],\n\n dc: this._getMetaTags('name', 'dc.'),\n eprints: this._getMetaTags('name', 'eprints.'),\n facebook: this._getMetaTags('property', 'og:'),\n highwire: this._getMetaTags('name', 'citation_'),\n prism: this._getMetaTags('name', 'prism.'),\n twitter: this._getMetaTags('name', 'twitter:'),\n };\n\n const favicon = this._getFavicon();\n if (favicon) {\n metadata.favicon = favicon;\n }\n\n metadata.title = this._getTitle(metadata);\n metadata.link = this._getLinks(metadata);\n\n const dcLink = metadata.link.find(link => link.href.startsWith('urn:x-dc'));\n if (dcLink) {\n metadata.documentFingerprint = dcLink.href;\n }\n\n return metadata;\n }\n\n /**\n * Return an array of all the `content` values of `<meta>` tags on the page\n * where the value of the attribute begins with `<prefix>`.\n *\n * @param {string} attribute\n * @param {string} prefix - it is interpreted as a regex\n * @return {Record<string,string[]>}\n */\n _getMetaTags(attribute, prefix) {\n /** @type {Record<string,string[]>} */\n const tags = {};\n for (let meta of Array.from(this.document.querySelectorAll('meta'))) {\n const name = meta.getAttribute(attribute);\n const { content } = meta;\n if (name && content) {\n const match = name.match(RegExp(`^${prefix}(.+)$`, 'i'));\n if (match) {\n const key = match[1].toLowerCase();\n if (tags[key]) {\n tags[key].push(content);\n } else {\n tags[key] = [content];\n }\n }\n }\n }\n return tags;\n }\n\n /** @param {HTMLDocumentMetadata} metadata */\n _getTitle(metadata) {\n if (metadata.highwire.title) {\n return metadata.highwire.title[0];\n } else if (metadata.eprints.title) {\n return metadata.eprints.title[0];\n } else if (metadata.prism.title) {\n return metadata.prism.title[0];\n } else if (metadata.facebook.title) {\n return metadata.facebook.title[0];\n } else if (metadata.twitter.title) {\n return metadata.twitter.title[0];\n } else if (metadata.dc.title) {\n return metadata.dc.title[0];\n } else {\n return this.document.title;\n }\n }\n\n /**\n * Get document URIs from `<link>` and `<meta>` elements on the page.\n *\n * @param {Pick<HTMLDocumentMetadata, 'highwire'|'dc'>} [metadata] -\n * Dublin Core and Highwire metadata parsed from `<meta>` tags.\n * @return {Link[]}\n */\n _getLinks(metadata = { dc: {}, highwire: {} }) {\n /** @type {Link[]} */\n const links = [{ href: this._getDocumentHref() }];\n\n // Extract links from `<link>` tags with certain `rel` values.\n const linkElements = Array.from(this.document.querySelectorAll('link'));\n for (let link of linkElements) {\n if (\n !['alternate', 'canonical', 'bookmark', 'shortlink'].includes(link.rel)\n ) {\n continue;\n }\n\n if (link.rel === 'alternate') {\n // Ignore RSS feed links.\n if (link.type && link.type.match(/^application\\/(rss|atom)\\+xml/)) {\n continue;\n }\n // Ignore alternate languages.\n if (link.hreflang) {\n continue;\n }\n }\n\n try {\n const href = this._absoluteUrl(link.href);\n links.push({ href, rel: link.rel, type: link.type });\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n\n // Look for links in scholar metadata\n for (let name of Object.keys(metadata.highwire)) {\n const values = metadata.highwire[name];\n if (name === 'pdf_url') {\n for (let url of values) {\n try {\n links.push({\n href: this._absoluteUrl(url),\n type: 'application/pdf',\n });\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n\n // Kind of a hack to express DOI identifiers as links but it's a\n // convenient place to look them up later, and somewhat sane since\n // they don't have a type.\n if (name === 'doi') {\n for (let doi of values) {\n if (doi.slice(0, 4) !== 'doi:') {\n doi = `doi:${doi}`;\n }\n links.push({ href: doi });\n }\n }\n }\n\n // Look for links in Dublin Core data\n for (let name of Object.keys(metadata.dc)) {\n const values = metadata.dc[name];\n if (name === 'identifier') {\n for (let id of values) {\n if (id.slice(0, 4) === 'doi:') {\n links.push({ href: id });\n }\n }\n }\n }\n\n // Look for a link to identify the resource in Dublin Core metadata\n const dcRelationValues = metadata.dc['relation.ispartof'];\n const dcIdentifierValues = metadata.dc.identifier;\n if (dcRelationValues && dcIdentifierValues) {\n const dcUrnRelationComponent =\n dcRelationValues[dcRelationValues.length - 1];\n const dcUrnIdentifierComponent =\n dcIdentifierValues[dcIdentifierValues.length - 1];\n const dcUrn =\n 'urn:x-dc:' +\n encodeURIComponent(dcUrnRelationComponent) +\n '/' +\n encodeURIComponent(dcUrnIdentifierComponent);\n links.push({ href: dcUrn });\n }\n\n return links;\n }\n\n _getFavicon() {\n let favicon = null;\n for (let link of Array.from(this.document.querySelectorAll('link'))) {\n if (['shortcut icon', 'icon'].includes(link.rel)) {\n try {\n favicon = this._absoluteUrl(link.href);\n } catch (e) {\n // Ignore URIs which cannot be parsed.\n }\n }\n }\n return favicon;\n }\n\n /**\n * Convert a possibly relative URI to an absolute one. This will throw an\n * exception if the URL cannot be parsed.\n *\n * @param {string} url\n */\n _absoluteUrl(url) {\n return normalizeURI(url, this.document.baseURI);\n }\n\n // Get the true URI record when it's masked via a different protocol.\n // This happens when an href is set with a uri using the 'blob:' protocol\n // but the document can set a different uri through a <base> tag.\n _getDocumentHref() {\n const { href } = this.document.location;\n const allowedSchemes = ['http:', 'https:', 'file:'];\n\n // Use the current document location if it has a recognized scheme.\n const scheme = new URL(href).protocol;\n if (allowedSchemes.includes(scheme)) {\n return href;\n }\n\n // Otherwise, try using the location specified by the <base> element.\n if (\n this.document.baseURI &&\n allowedSchemes.includes(new URL(this.document.baseURI).protocol)\n ) {\n return this.document.baseURI;\n }\n\n // Fall back to returning the document URI, even though the scheme is not\n // in the allowed list.\n return href;\n }\n}\n","/**\n * Return the intersection of two rects.\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function intersectRects(rectA, rectB) {\n const left = Math.max(rectA.left, rectB.left);\n const right = Math.min(rectA.right, rectB.right);\n const top = Math.max(rectA.top, rectB.top);\n const bottom = Math.min(rectA.bottom, rectB.bottom);\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return `true` if a rect is _empty_.\n *\n * An empty rect is defined as one with zero or negative width/height, eg.\n * as returned by `new DOMRect()` or `Element.getBoundingClientRect()` for a\n * hidden element.\n *\n * @param {DOMRect} rect\n */\nexport function rectIsEmpty(rect) {\n return rect.width <= 0 || rect.height <= 0;\n}\n\n/**\n * Return true if the 1D lines a-b and c-d overlap (ie. the length of their\n * intersection is non-zero).\n *\n * For example, the following lines overlap:\n *\n * a----b\n * c------d\n *\n * The inputs must be normalized such that b >= a and d >= c.\n *\n * @param {number} a\n * @param {number} b\n * @param {number} c\n * @param {number} d\n */\nfunction linesOverlap(a, b, c, d) {\n const maxStart = Math.max(a, c);\n const minEnd = Math.min(b, d);\n return maxStart < minEnd;\n}\n\n/**\n * Return true if the intersection of `rectB` and `rectA` is non-empty.\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function rectIntersects(rectA, rectB) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n linesOverlap(rectA.left, rectA.right, rectB.left, rectB.right) &&\n linesOverlap(rectA.top, rectA.bottom, rectB.top, rectB.bottom)\n );\n}\n\n/**\n * Return true if `rectB` is fully contained within `rectA`\n *\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n */\nexport function rectContains(rectA, rectB) {\n if (rectIsEmpty(rectA) || rectIsEmpty(rectB)) {\n return false;\n }\n\n return (\n rectB.left >= rectA.left &&\n rectB.right <= rectA.right &&\n rectB.top >= rectA.top &&\n rectB.bottom <= rectA.bottom\n );\n}\n\n/**\n * Return true if two rects overlap vertically.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function rectsOverlapVertically(a, b) {\n return linesOverlap(a.top, a.bottom, b.top, b.bottom);\n}\n\n/**\n * Return true if two rects overlap horizontally.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function rectsOverlapHorizontally(a, b) {\n return linesOverlap(a.left, a.right, b.left, b.right);\n}\n\n/**\n * Return the union of two rects.\n *\n * The union of an empty rect (see {@link rectIsEmpty}) with a non-empty rect is\n * defined to be the non-empty rect. The union of two empty rects is an empty\n * rect.\n *\n * @param {DOMRect} a\n * @param {DOMRect} b\n */\nexport function unionRects(a, b) {\n if (rectIsEmpty(a)) {\n return b;\n } else if (rectIsEmpty(b)) {\n return a;\n }\n\n const left = Math.min(a.left, b.left);\n const top = Math.min(a.top, b.top);\n const right = Math.max(a.right, b.right);\n const bottom = Math.max(a.bottom, b.bottom);\n\n return new DOMRect(left, top, right - left, bottom - top);\n}\n\n/**\n * Return the point at the center of a rect.\n *\n * @param {DOMRect} rect\n */\nexport function rectCenter(rect) {\n return new DOMPoint(\n (rect.left + rect.right) / 2,\n (rect.top + rect.bottom) / 2\n );\n}\n","import { rectContains, rectIntersects } from '../util/geometry';\n\n/**\n * CSS selectors used to find elements that are considered potentially part\n * of the main content of a page.\n */\nconst contentSelectors = [\n 'p',\n\n // Paragraphs in VitalSource \"Great Book\" format ebooks.\n '.para',\n];\n\n/**\n * Attempt to guess the region of the page that contains the main content.\n *\n * @param {Element} root\n * @return {{ left: number, right: number }|null} -\n * The left/right content margins or `null` if they could not be determined\n */\nexport function guessMainContentArea(root) {\n // Maps of (margin X coord, votes) for margin positions.\n\n /** @type {Map<number,number>} */\n const leftMarginVotes = new Map();\n\n /** @type {Map<number,number>} */\n const rightMarginVotes = new Map();\n\n // Gather data about the paragraphs of text in the document.\n //\n // In future we might want to expand this to consider other text containers,\n // since some pages, especially eg. in ebooks, may not have any paragraphs\n // (eg. instead they may only contain tables or lists or headings).\n const contentSelector = contentSelectors.join(',');\n const paragraphs = Array.from(root.querySelectorAll(contentSelector))\n .map(p => {\n // Gather some data about them.\n const rect = p.getBoundingClientRect();\n const textLength = /** @type {string} */ (p.textContent).length;\n return { rect, textLength };\n })\n .filter(({ rect }) => {\n // Filter out hidden paragraphs\n return rect.width > 0 && rect.height > 0;\n })\n // Select the paragraphs containing the most text.\n .sort((a, b) => b.textLength - a.textLength)\n .slice(0, 15);\n\n // Let these paragraphs \"vote\" for what the left and right margins of the\n // main content area in the document are.\n paragraphs.forEach(({ rect }) => {\n let leftVotes = leftMarginVotes.get(rect.left) ?? 0;\n leftVotes += 1;\n leftMarginVotes.set(rect.left, leftVotes);\n\n let rightVotes = rightMarginVotes.get(rect.right) ?? 0;\n rightVotes += 1;\n rightMarginVotes.set(rect.right, rightVotes);\n });\n\n // Find the margin values with the most votes.\n if (leftMarginVotes.size === 0 || rightMarginVotes.size === 0) {\n return null;\n }\n\n const leftMargin = [...leftMarginVotes.entries()].sort((a, b) => b[1] - a[1]);\n const rightMargin = [...rightMarginVotes.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n\n const [leftPos] = leftMargin[0];\n const [rightPos] = rightMargin[0];\n\n return { left: leftPos, right: rightPos };\n}\n\n/** @type {Range} */\nlet textRectRange;\n\n/**\n * Return the viewport-relative rect occupied by part of a text node.\n *\n * @param {Text} text\n * @param {number} start\n * @param {number} end\n */\nfunction textRect(text, start = 0, end = text.data.length) {\n if (!textRectRange) {\n // Allocate a range only on the first call to avoid the overhead of\n // constructing and maintaining a large number of live ranges.\n textRectRange = document.createRange();\n }\n textRectRange.setStart(text, start);\n textRectRange.setEnd(text, end);\n return textRectRange.getBoundingClientRect();\n}\n\n/** @param {Element} element */\nfunction hasFixedPosition(element) {\n switch (getComputedStyle(element).position) {\n case 'fixed':\n case 'sticky':\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Return the bounding rect that contains the element's content. Unlike\n * `Element.getBoundingClientRect`, this includes content which overflows\n * the element's specified size.\n *\n * @param {Element} element\n */\nfunction elementContentRect(element) {\n const rect = element.getBoundingClientRect();\n rect.x -= element.scrollLeft;\n rect.y -= element.scrollTop;\n rect.height = Math.max(rect.height, element.scrollHeight);\n rect.width = Math.max(rect.width, element.scrollWidth);\n return rect;\n}\n\n/**\n * Yield all the text node descendants of `root` that intersect `rect`.\n *\n * @param {Element} root\n * @param {DOMRect} rect\n * @param {(el: Element) => boolean} shouldVisit - Optional filter that determines\n * whether to visit a subtree\n * @return {Generator<Text>}\n */\nfunction* textNodesInRect(root, rect, shouldVisit = () => true) {\n /** @type {Node|null} */\n let node = root.firstChild;\n while (node) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = /** @type {Element} */ (node);\n const contentIntersectsRect = rectIntersects(\n elementContentRect(element),\n rect\n );\n\n // Only examine subtrees which are visible.\n if (shouldVisit(element) && contentIntersectsRect) {\n yield* textNodesInRect(element, rect, shouldVisit);\n }\n } else if (node.nodeType === Node.TEXT_NODE) {\n const text = /** @type {Text} */ (node);\n\n // Skip over text nodes which are entirely outside the viewport or empty.\n if (rectIntersects(textRect(text), rect)) {\n yield text;\n }\n }\n node = node.nextSibling;\n }\n}\n\n/**\n * Find content within an element to use as an anchor when applying a layout\n * change to the document.\n *\n * @param {Element} root\n * @param {DOMRect} viewport\n * @return {Range|null} - Range to use as an anchor or `null` if a suitable\n * range could not be found\n */\nfunction getScrollAnchor(root, viewport) {\n // Range representing the content whose position within the viewport we will\n // try to maintain after running the callback.\n let anchorRange = /** @type {Range|null} */ (null);\n\n // Find the first word (non-whitespace substring of a text node) that is fully\n // visible in the viewport.\n\n // Text inside fixed-position elements is ignored because its position won't\n // be affected by a layout change and so it makes a poor scroll anchor.\n /** @param {Element} el */\n const shouldVisit = el => !hasFixedPosition(el);\n\n textNodeLoop: for (let textNode of textNodesInRect(\n root,\n viewport,\n shouldVisit\n )) {\n let textLen = 0;\n\n // Visit all the non-whitespace substrings of the text node.\n for (let word of textNode.data.split(/\\b/)) {\n if (/\\S/.test(word)) {\n const start = textLen;\n const end = textLen + word.length;\n const wordBox = textRect(textNode, start, end);\n if (rectContains(viewport, wordBox)) {\n anchorRange = document.createRange();\n anchorRange.setStart(textNode, start);\n anchorRange.setEnd(textNode, end);\n break textNodeLoop;\n }\n }\n\n textLen += word.length;\n }\n }\n\n return anchorRange;\n}\n\n/**\n * Apply a layout change to the document and preserve the scroll position.\n *\n * This utility selects part of the content in the viewport as an _anchor_\n * and tries to preserve the position of this content within the viewport\n * after the callback is invoked.\n *\n * @param {() => void} callback - Callback that will apply the layout change\n * @param {Element} [scrollRoot]\n * @param {DOMRect} [viewport] - Area to consider \"in the viewport\". Defaults to\n * the viewport of the current window.\n * @return {number} - Amount by which the scroll position was adjusted to keep\n * the anchored content in view\n */\nexport function preserveScrollPosition(\n callback,\n /* istanbul ignore next */\n scrollRoot = document.documentElement,\n /* istanbul ignore next */\n viewport = new DOMRect(0, 0, window.innerWidth, window.innerHeight)\n) {\n const anchor = getScrollAnchor(scrollRoot, viewport);\n if (!anchor) {\n callback();\n return 0;\n }\n\n const anchorTop = anchor.getBoundingClientRect().top;\n callback();\n const newAnchorTop = anchor.getBoundingClientRect().top;\n\n // Determine how far we scrolled as a result of the layout change.\n // This will be positive if the anchor element moved down or negative if it moved up.\n const scrollDelta = newAnchorTop - anchorTop;\n scrollRoot.scrollTop += scrollDelta;\n\n return scrollDelta;\n}\n","var COMPLETE = 'complete',\r\n CANCELED = 'canceled';\r\n\r\nfunction raf(task){\r\n if('requestAnimationFrame' in window){\r\n return window.requestAnimationFrame(task);\r\n }\r\n\r\n setTimeout(task, 16);\r\n}\r\n\r\nfunction setElementScroll(element, x, y){\r\n Math.max(0, x);\r\n Math.max(0, y);\r\n\r\n if(element.self === element){\r\n element.scrollTo(x, y);\r\n }else{\r\n element.scrollLeft = x;\r\n element.scrollTop = y;\r\n }\r\n}\r\n\r\nfunction getTargetScrollLocation(scrollSettings, parent){\r\n var align = scrollSettings.align,\r\n target = scrollSettings.target,\r\n targetPosition = target.getBoundingClientRect(),\r\n parentPosition,\r\n x,\r\n y,\r\n differenceX,\r\n differenceY,\r\n targetWidth,\r\n targetHeight,\r\n leftAlign = align && align.left != null ? align.left : 0.5,\r\n topAlign = align && align.top != null ? align.top : 0.5,\r\n leftOffset = align && align.leftOffset != null ? align.leftOffset : 0,\r\n topOffset = align && align.topOffset != null ? align.topOffset : 0,\r\n leftScalar = leftAlign,\r\n topScalar = topAlign;\r\n\r\n if(scrollSettings.isWindow(parent)){\r\n targetWidth = Math.min(targetPosition.width, parent.innerWidth);\r\n targetHeight = Math.min(targetPosition.height, parent.innerHeight);\r\n x = targetPosition.left + parent.pageXOffset - parent.innerWidth * leftScalar + targetWidth * leftScalar;\r\n y = targetPosition.top + parent.pageYOffset - parent.innerHeight * topScalar + targetHeight * topScalar;\r\n x -= leftOffset;\r\n y -= topOffset;\r\n x = scrollSettings.align.lockX ? parent.pageXOffset : x;\r\n y = scrollSettings.align.lockY ? parent.pageYOffset : y;\r\n differenceX = x - parent.pageXOffset;\r\n differenceY = y - parent.pageYOffset;\r\n }else{\r\n targetWidth = targetPosition.width;\r\n targetHeight = targetPosition.height;\r\n parentPosition = parent.getBoundingClientRect();\r\n var offsetLeft = targetPosition.left - (parentPosition.left - parent.scrollLeft);\r\n var offsetTop = targetPosition.top - (parentPosition.top - parent.scrollTop);\r\n x = offsetLeft + (targetWidth * leftScalar) - parent.clientWidth * leftScalar;\r\n y = offsetTop + (targetHeight * topScalar) - parent.clientHeight * topScalar;\r\n x -= leftOffset;\r\n y -= topOffset;\r\n x = Math.max(Math.min(x, parent.scrollWidth - parent.clientWidth), 0);\r\n y = Math.max(Math.min(y, parent.scrollHeight - parent.clientHeight), 0);\r\n x = scrollSettings.align.lockX ? parent.scrollLeft : x;\r\n y = scrollSettings.align.lockY ? parent.scrollTop : y;\r\n differenceX = x - parent.scrollLeft;\r\n differenceY = y - parent.scrollTop;\r\n }\r\n\r\n return {\r\n x: x,\r\n y: y,\r\n differenceX: differenceX,\r\n differenceY: differenceY\r\n };\r\n}\r\n\r\nfunction animate(parent){\r\n var scrollSettings = parent._scrollSettings;\r\n\r\n if(!scrollSettings){\r\n return;\r\n }\r\n\r\n var maxSynchronousAlignments = scrollSettings.maxSynchronousAlignments;\r\n\r\n var location = getTargetScrollLocation(scrollSettings, parent),\r\n time = Date.now() - scrollSettings.startTime,\r\n timeValue = Math.min(1 / scrollSettings.time * time, 1);\r\n\r\n if(scrollSettings.endIterations >= maxSynchronousAlignments){\r\n setElementScroll(parent, location.x, location.y);\r\n parent._scrollSettings = null;\r\n return scrollSettings.end(COMPLETE);\r\n }\r\n\r\n var easeValue = 1 - scrollSettings.ease(timeValue);\r\n\r\n setElementScroll(parent,\r\n location.x - location.differenceX * easeValue,\r\n location.y - location.differenceY * easeValue\r\n );\r\n\r\n if(time >= scrollSettings.time){\r\n scrollSettings.endIterations++;\r\n // Align ancestor synchronously\r\n scrollSettings.scrollAncestor && animate(scrollSettings.scrollAncestor);\r\n animate(parent);\r\n return;\r\n }\r\n\r\n raf(animate.bind(null, parent));\r\n}\r\n\r\nfunction defaultIsWindow(target){\r\n return target.self === target\r\n}\r\n\r\nfunction transitionScrollTo(target, parent, settings, scrollAncestor, callback){\r\n var idle = !parent._scrollSettings,\r\n lastSettings = parent._scrollSettings,\r\n now = Date.now(),\r\n cancelHandler,\r\n passiveOptions = { passive: true };\r\n\r\n if(lastSettings){\r\n lastSettings.end(CANCELED);\r\n }\r\n\r\n function end(endType){\r\n parent._scrollSettings = null;\r\n\r\n if(parent.parentElement && parent.parentElement._scrollSettings){\r\n parent.parentElement._scrollSettings.end(endType);\r\n }\r\n\r\n if(settings.debug){\r\n console.log('Scrolling ended with type', endType, 'for', parent)\r\n }\r\n\r\n callback(endType);\r\n if(cancelHandler){\r\n parent.removeEventListener('touchstart', cancelHandler, passiveOptions);\r\n parent.removeEventListener('wheel', cancelHandler, passiveOptions);\r\n }\r\n }\r\n\r\n var maxSynchronousAlignments = settings.maxSynchronousAlignments;\r\n\r\n if(maxSynchronousAlignments == null){\r\n maxSynchronousAlignments = 3;\r\n }\r\n\r\n parent._scrollSettings = {\r\n startTime: now,\r\n endIterations: 0,\r\n target: target,\r\n time: settings.time,\r\n ease: settings.ease,\r\n align: settings.align,\r\n isWindow: settings.isWindow || defaultIsWindow,\r\n maxSynchronousAlignments: maxSynchronousAlignments,\r\n end: end,\r\n scrollAncestor\r\n };\r\n\r\n if(!('cancellable' in settings) || settings.cancellable){\r\n cancelHandler = end.bind(null, CANCELED);\r\n parent.addEventListener('touchstart', cancelHandler, passiveOptions);\r\n parent.addEventListener('wheel', cancelHandler, passiveOptions);\r\n }\r\n\r\n if(idle){\r\n animate(parent);\r\n }\r\n\r\n return cancelHandler\r\n}\r\n\r\nfunction defaultIsScrollable(element){\r\n return (\r\n 'pageXOffset' in element ||\r\n (\r\n element.scrollHeight !== element.clientHeight ||\r\n element.scrollWidth !== element.clientWidth\r\n ) &&\r\n getComputedStyle(element).overflow !== 'hidden'\r\n );\r\n}\r\n\r\nfunction defaultValidTarget(){\r\n return true;\r\n}\r\n\r\nfunction findParentElement(el){\r\n if (el.assignedSlot) {\r\n return findParentElement(el.assignedSlot);\r\n }\r\n\r\n if (el.parentElement) {\r\n if(el.parentElement.tagName === 'BODY'){\r\n return el.parentElement.ownerDocument.defaultView || el.parentElement.ownerDocument.ownerWindow;\r\n }\r\n return el.parentElement;\r\n }\r\n\r\n if (el.getRootNode){\r\n var parent = el.getRootNode()\r\n if(parent.nodeType === 11) {\r\n return parent.host;\r\n }\r\n }\r\n}\r\n\r\nmodule.exports = function(target, settings, callback){\r\n if(!target){\r\n return;\r\n }\r\n\r\n if(typeof settings === 'function'){\r\n callback = settings;\r\n settings = null;\r\n }\r\n\r\n if(!settings){\r\n settings = {};\r\n }\r\n\r\n settings.time = isNaN(settings.time) ? 1000 : settings.time;\r\n settings.ease = settings.ease || function(v){return 1 - Math.pow(1 - v, v / 2);};\r\n settings.align = settings.align || {};\r\n\r\n var parent = findParentElement(target),\r\n parents = 1;\r\n\r\n function done(endType){\r\n parents--;\r\n if(!parents){\r\n callback && callback(endType);\r\n }\r\n }\r\n\r\n var validTarget = settings.validTarget || defaultValidTarget;\r\n var isScrollable = settings.isScrollable;\r\n\r\n if(settings.debug){\r\n console.log('About to scroll to', target)\r\n\r\n if(!parent){\r\n console.error('Target did not have a parent, is it mounted in the DOM?')\r\n }\r\n }\r\n\r\n var scrollingElements = [];\r\n\r\n while(parent){\r\n if(settings.debug){\r\n console.log('Scrolling parent node', parent)\r\n }\r\n\r\n if(validTarget(parent, parents) && (isScrollable ? isScrollable(parent, defaultIsScrollable) : defaultIsScrollable(parent))){\r\n parents++;\r\n scrollingElements.push(parent);\r\n }\r\n\r\n parent = findParentElement(parent);\r\n\r\n if(!parent){\r\n done(COMPLETE)\r\n break;\r\n }\r\n }\r\n\r\n return scrollingElements.reduce((cancel, parent, index) => transitionScrollTo(target, parent, settings, scrollingElements[index + 1], done), null);\r\n};\r\n","import scrollIntoView from 'scroll-into-view';\n\n/**\n * Return a promise that resolves on the next animation frame.\n */\nfunction nextAnimationFrame() {\n return new Promise(resolve => {\n requestAnimationFrame(resolve);\n });\n}\n\n/**\n * Linearly interpolate between two values.\n *\n * @param {number} a\n * @param {number} b\n * @param {number} fraction - Value in [0, 1]\n */\nfunction interpolate(a, b, fraction) {\n return a + fraction * (b - a);\n}\n\n/**\n * Return the offset of `element` from the top of a positioned ancestor `parent`.\n *\n * @param {HTMLElement} element\n * @param {HTMLElement} parent - Positioned ancestor of `element`\n * @return {number}\n */\nexport function offsetRelativeTo(element, parent) {\n let offset = 0;\n while (element !== parent && parent.contains(element)) {\n offset += element.offsetTop;\n element = /** @type {HTMLElement} */ (element.offsetParent);\n }\n return offset;\n}\n\n/**\n * Scroll `element` until its `scrollTop` offset reaches a target value.\n *\n * @param {Element} element - Container element to scroll\n * @param {number} offset - Target value for the scroll offset\n * @param {object} options\n * @param {number} [options.maxDuration]\n * @return {Promise<void>} - A promise that resolves once the scroll animation\n * is complete\n */\nexport async function scrollElement(\n element,\n offset,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 } = {}\n) {\n const startOffset = element.scrollTop;\n const endOffset = offset;\n const scrollStart = Date.now();\n\n // Choose a scroll duration proportional to the scroll distance, but capped\n // to avoid it being too slow.\n const pixelsPerMs = 3;\n const scrollDuration = Math.min(\n Math.abs(endOffset - startOffset) / pixelsPerMs,\n maxDuration\n );\n\n let scrollFraction = 0.0;\n while (scrollFraction < 1.0) {\n await nextAnimationFrame();\n scrollFraction = Math.min(1.0, (Date.now() - scrollStart) / scrollDuration);\n element.scrollTop = interpolate(startOffset, endOffset, scrollFraction);\n }\n}\n\n/**\n * Smoothly scroll an element into view.\n *\n * @param {HTMLElement} element\n * @param {object} options\n * @param {number} [options.maxDuration]\n */\nexport async function scrollElementIntoView(\n element,\n /* istanbul ignore next - defaults are overridden in tests */\n { maxDuration = 500 } = {}\n) {\n // Make the body's `tagName` return an upper-case string in XHTML documents\n // like it does in HTML documents. This is a workaround for\n // `scrollIntoView`'s detection of the <body> element. See\n // https://github.com/KoryNunn/scroll-into-view/issues/101.\n const body = element.closest('body');\n if (body && body.tagName !== 'BODY') {\n Object.defineProperty(body, 'tagName', {\n value: 'BODY',\n configurable: true,\n });\n }\n\n await new Promise(resolve =>\n scrollIntoView(element, { time: maxDuration }, resolve)\n );\n}\n","import { anchor, describe } from '../anchoring/html';\n\nimport { HTMLMetadata } from './html-metadata';\nimport {\n guessMainContentArea,\n preserveScrollPosition,\n} from './html-side-by-side';\nimport { scrollElementIntoView } from '../util/scroll';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').FeatureFlags} FeatureFlags\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n */\n\n// When activating side-by-side mode, make sure there is at least this amount\n// of space (in pixels) left for the document's content. Any narrower and the\n// content line lengths and scale are too short to be readable.\nconst MIN_HTML_WIDTH = 480;\n\n/**\n * Document type integration for ordinary web pages.\n *\n * This integration is used for web pages and applications that are not handled\n * by a more specific integration (eg. for PDFs).\n *\n * @implements {Integration}\n */\nexport class HTMLIntegration {\n /**\n * @param {object} options\n * @param {FeatureFlags} options.features\n * @param {HTMLElement} [options.container]\n */\n constructor({ features, container = document.body }) {\n this.features = features;\n this.container = container;\n this.anchor = anchor;\n this.describe = describe;\n\n this._htmlMeta = new HTMLMetadata();\n\n /** Whether to attempt to resize the document to fit alongside sidebar. */\n this._sideBySideEnabled = this.features.flagEnabled('html_side_by_side');\n\n /**\n * Whether the document is currently being resized to fit alongside an\n * open sidebar.\n */\n this._sideBySideActive = false;\n\n /** @type {SidebarLayout|null} */\n this._lastLayout = null;\n\n this._flagsChanged = () => {\n const sideBySide = features.flagEnabled('html_side_by_side');\n if (sideBySide !== this._sideBySideEnabled) {\n this._sideBySideEnabled = sideBySide;\n\n // `fitSideBySide` is normally called by Guest when the sidebar layout\n // changes. When the feature flag changes, we need to re-run the method.\n if (this._lastLayout) {\n this.fitSideBySide(this._lastLayout);\n }\n }\n };\n this.features.on('flagsChanged', this._flagsChanged);\n }\n\n canAnnotate() {\n return true;\n }\n\n destroy() {\n this.features.off('flagsChanged', this._flagsChanged);\n }\n\n contentContainer() {\n return this.container;\n }\n\n /**\n * @param {SidebarLayout} layout\n */\n fitSideBySide(layout) {\n this._lastLayout = layout;\n\n const maximumWidthToFit = window.innerWidth - layout.width;\n const active =\n this._sideBySideEnabled &&\n layout.expanded &&\n maximumWidthToFit >= MIN_HTML_WIDTH;\n\n if (active) {\n // nb. We call `_activateSideBySide` regardless of whether side-by-side\n // is already active because the sidebar width might be different.\n this._activateSideBySide(layout.width);\n } else if (this._sideBySideActive) {\n this._deactivateSideBySide();\n }\n this._sideBySideActive = active;\n return active;\n }\n\n /**\n * Resize the document content after side-by-side mode is activated.\n *\n * @param {number} sidebarWidth\n */\n _activateSideBySide(sidebarWidth) {\n // When side-by-side mode is activated, what we want to achieve is that the\n // main content of the page is fully visible alongside the sidebar, with\n // as much space given to the main content as possible. A challenge is that\n // we don't know how the page will respond to reducing the width of the body.\n //\n // - The content might have margins which automatically get reduced as the\n // available width is reduced. For example a blog post with a fixed-width\n // article in the middle and `margin: auto` for both margins.\n //\n // In this scenario we'd want to reduce the document width by the full\n // width of the sidebar.\n //\n // - There might be sidebars to the left and/or right of the main content\n // which cause the main content to be squashed when the width is reduced.\n // For example a news website with a column of ads on the right.\n //\n // In this scenario we'd want to not reduce the document width or reduce\n // it by a smaller amount and let the Hypothesis sidebar cover up the\n // document's sidebar, leaving as much space as possible to the content.\n //\n // Therefore what we do is to initially reduce the width of the document by\n // the full width of the sidebar, then we use heuristics to analyze the\n // resulting page layout and determine whether there is significant \"free space\"\n // (ie. anything that is not the main content of the document, such as ads or\n // links to related stories) to the right of the main content. If there is,\n // we make the document wider again to allow more space for the main content.\n //\n // These heuristics assume a typical \"article\" page with one central block\n // of content. If we can't find the \"main content\" then we just assume that\n // everything on the page is potentially content that the user might want\n // to annotate and so try to keep it all visible.\n\n // nb. 12px padding is a multiple of the 4px grid unit in our design system.\n const padding = 12;\n const rightMargin = sidebarWidth + padding;\n\n /** @param {HTMLElement} element */\n const computeLeftMargin = element =>\n parseInt(window.getComputedStyle(element).marginLeft, 10);\n\n preserveScrollPosition(() => {\n // nb. Adjusting the body size this way relies on the page not setting a\n // width on the body. For sites that do this won't work.\n\n // Remove any margins we've previously set\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n\n // Keep track of what left margin would be naturally without right margin set\n const beforeBodyLeft = computeLeftMargin(document.body);\n\n document.body.style.marginRight = `${rightMargin}px`;\n\n const contentArea = guessMainContentArea(document.body);\n if (contentArea) {\n // Check if we can give the main content more space by letting the\n // sidebar overlap stuff in the document to the right of the main content.\n const freeSpace = Math.max(\n 0,\n window.innerWidth - rightMargin - contentArea.right\n );\n if (freeSpace > 0) {\n const adjustedMargin = Math.max(0, rightMargin - freeSpace);\n document.body.style.marginRight = `${adjustedMargin}px`;\n }\n\n // Changes to right margin can affect left margin in cases where body\n // has `margin:auto`. It's OK to move the body to the left to make\n // space, but avoid moving it to the right.\n // See https://github.com/hypothesis/client/issues/4280\n const afterBodyLeft = computeLeftMargin(document.body);\n if (afterBodyLeft > beforeBodyLeft) {\n document.body.style.marginLeft = `${beforeBodyLeft}px`;\n }\n\n // If the main content appears to be right up against the edge of the\n // window, add padding for readability.\n if (contentArea.left < padding) {\n document.body.style.marginLeft = `${padding}px`;\n }\n } else {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n }\n });\n }\n\n /**\n * Undo the effects of `activateSideBySide`.\n */\n _deactivateSideBySide() {\n preserveScrollPosition(() => {\n document.body.style.marginLeft = '';\n document.body.style.marginRight = '';\n });\n }\n\n async getMetadata() {\n return this._htmlMeta.getDocumentMetadata();\n }\n\n async uri() {\n return this._htmlMeta.uri();\n }\n\n /**\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n const highlight = anchor.highlights?.[0];\n if (!highlight) {\n return;\n }\n await scrollElementIntoView(highlight);\n }\n}\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n result = wait - timeSinceLastCall;\n\n return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","/**\n * Find the smallest offset in `str` which contains at least `count` chars\n * that match `filter` before it.\n *\n * @param {string} str\n * @param {number} count\n * @param {(char: string) => boolean} filter\n * @param {number} [startPos]\n */\nfunction advance(str, count, filter, startPos = 0) {\n let pos = startPos;\n while (pos < str.length && count > 0) {\n if (filter(str[pos])) {\n --count;\n }\n ++pos;\n }\n return pos;\n}\n\n/**\n * Count characters which match `filter` in `str`.\n *\n * @param {string} str\n * @param {(char: string) => boolean} filter\n * @param {number} startPos\n * @param {number} endPos\n */\nfunction countChars(str, filter, startPos, endPos) {\n let count = 0;\n for (let pos = startPos; pos < endPos; pos++) {\n if (filter(str[pos])) {\n ++count;\n }\n }\n return count;\n}\n\n/**\n * Translate a (start, end) pair of offsets for an \"input\" string into\n * corresponding offsets in an \"output\" string.\n *\n * Positions in the input and output strings are related by counting\n * the number of \"important\" characters before them, as determined by a\n * filter function.\n *\n * An example usage would be to find equivalent positions in two strings which\n * contain the same text content except for the addition or removal of\n * whitespace at arbitrary locations in the output string.\n *\n * Where there are multiple possible offsets in the output string that\n * correspond to the input offsets, the largest start offset and smallest end\n * offset are chosen. In other words, leading and trailing ignored characters\n * are trimmed from the output.\n *\n * @example\n * // The input offsets (1, 3) select the substring \"bc\" in the \"input\" argument.\n * // The returned offsets select the substring \"b c\" in the \"output\" argument.\n * translateOffsets('abcd', ' a b c d ', 1, 3, char => char !== ' ')\n *\n * @param {string} input\n * @param {string} output\n * @param {number} start - Start offset in `input`\n * @param {number} end - End offset in `input`\n * @param {(ch: string) => boolean} filter - Filter function that returns true\n * if a character should be counted when relating positions between `input`\n * and `output`.\n * @return {[number, number]} - Start and end offsets in `output`\n */\nexport function translateOffsets(input, output, start, end, filter) {\n const beforeStartCount = countChars(input, filter, 0, start);\n const startToEndCount = countChars(input, filter, start, end);\n\n // Find smallest offset in `output` with same number of non-ignored characters\n // before it as before `start` in the input. This offset might correspond to\n // an ignored character.\n let outputStart = advance(output, beforeStartCount, filter);\n\n // Increment this offset until it points to a non-ignored character. This\n // \"trims\" leading ignored characters from the result.\n while (outputStart < output.length && !filter(output[outputStart])) {\n ++outputStart;\n }\n\n // Find smallest offset in `output` with same number of non-ignored characters\n // before it as before `end` in the input.\n const outputEnd = advance(output, startToEndCount, filter, outputStart);\n\n return [outputStart, outputEnd];\n}\n","/* global PDFViewerApplication */\n\nimport { warnOnce } from '../../shared/warn-once';\nimport { translateOffsets } from '../util/normalize';\nimport { matchQuote } from './match-quote';\nimport { createPlaceholder } from './placeholder';\nimport { TextPosition, TextRange } from './text-range';\nimport { TextQuoteAnchor } from './types';\n\n/**\n * @typedef {import('../../types/api').TextPositionSelector} TextPositionSelector\n * @typedef {import('../../types/api').TextQuoteSelector} TextQuoteSelector\n * @typedef {import('../../types/api').Selector} Selector\n *\n * @typedef {import('../../types/pdfjs').PDFPageView} PDFPageView\n * @typedef {import('../../types/pdfjs').PDFViewer} PDFViewer\n */\n\n/**\n * @typedef PDFTextRange\n * @prop {number} pageIndex\n * @prop {object} anchor\n * @prop {number} anchor.start - Start character offset within the page's text\n * @prop {number} anchor.end - End character offset within the page's text\n */\n\n/**\n * Enum values for page rendering states (IRenderableView#renderingState)\n * in PDF.js. Taken from web/pdf_rendering_queue.js in the PDF.js library.\n *\n * Reproduced here because this enum is not exported consistently across\n * different versions of PDF.js\n */\nexport const RenderingStates = {\n INITIAL: 0,\n RUNNING: 1,\n PAUSED: 2,\n FINISHED: 3,\n};\n\n// Caches for performance.\n\n/**\n * Map of page index to page text content.\n *\n * @type {Map<number, Promise<string>>}\n */\nconst pageTextCache = new Map();\n\n/**\n * A cache that maps a `{quote}:{offset}` key to a specific\n * location in the document.\n *\n * The components of the key come from an annotation's selectors. This is used\n * to speed up re-anchoring an annotation that was previously anchored in the\n * current session.\n *\n * @type {Map<string, PDFTextRange>}\n */\nconst quotePositionCache = new Map();\n\n/**\n * Return a cache key for lookups in `quotePositionCache`.\n *\n * @param {string} quote\n * @param {number} [pos] - Offset in document text\n */\nfunction quotePositionCacheKey(quote, pos) {\n return `${quote}:${pos}`;\n}\n\n/**\n * Return offset of `node` among its siblings\n *\n * @param {Node} node\n */\nfunction getSiblingIndex(node) {\n let index = 0;\n while (node.previousSibling) {\n ++index;\n node = node.previousSibling;\n }\n return index;\n}\n\n/**\n * Return the text layer element of the PDF page containing `node`.\n *\n * @param {Node|Element} node\n * @return {Element|null}\n */\nfunction getNodeTextLayer(node) {\n const el = 'closest' in node ? node : node.parentElement;\n return el?.closest('.textLayer') ?? null;\n}\n\n/**\n * Get the PDF.js viewer application.\n *\n * @return {PDFViewer}\n */\nfunction getPDFViewer() {\n // @ts-ignore - TS doesn't know about PDFViewerApplication global.\n return PDFViewerApplication.pdfViewer;\n}\n\n/**\n * Returns the view into which a PDF page is drawn.\n *\n * If called while the PDF document is still loading, this will delay until\n * the document loading has progressed far enough for a `PDFPageView` and its\n * associated `PDFPage` to be ready.\n *\n * @param {number} pageIndex\n * @return {Promise<PDFPageView>}\n */\nasync function getPageView(pageIndex) {\n const pdfViewer = getPDFViewer();\n let pageView = pdfViewer.getPageView(pageIndex);\n\n if (!pageView || !pageView.pdfPage) {\n // If the document is still loading, wait for the `pagesloaded` event.\n //\n // Note that loading happens in several stages. Initially the page view\n // objects do not exist (`pageView` will be nullish), then after the\n // \"pagesinit\" event, the page view exists but it does not have a `pdfPage`\n // property set, then finally after the \"pagesloaded\" event, it will have\n // a \"pdfPage\" property.\n pageView = await new Promise(resolve => {\n const onPagesLoaded = () => {\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.off('pagesloaded', onPagesLoaded);\n } else {\n document.removeEventListener('pagesloaded', onPagesLoaded);\n }\n\n resolve(pdfViewer.getPageView(pageIndex));\n };\n\n if (pdfViewer.eventBus) {\n pdfViewer.eventBus.on('pagesloaded', onPagesLoaded);\n } else {\n // Old PDF.js versions (< 1.6.210) use DOM events.\n document.addEventListener('pagesloaded', onPagesLoaded);\n }\n });\n }\n\n return /** @type {PDFPageView} */ (pageView);\n}\n\n/**\n * Return true if the document has selectable text.\n */\nexport async function documentHasText() {\n const viewer = getPDFViewer();\n let hasText = false;\n for (let i = 0; i < viewer.pagesCount; i++) {\n const pageText = await getPageTextContent(i);\n if (pageText.trim().length > 0) {\n hasText = true;\n break;\n }\n }\n return hasText;\n}\n\n/**\n * Return the text of a given PDF page.\n *\n * The text returned by this function should match the `textContent` of the text\n * layer element that PDF.js creates for rendered pages, with the exception\n * that differences in whitespace are tolerated.\n *\n * @param {number} pageIndex\n * @return {Promise<string>}\n */\nfunction getPageTextContent(pageIndex) {\n // If we already have or are fetching the text for this page, return the\n // existing result.\n const cachedText = pageTextCache.get(pageIndex);\n if (cachedText) {\n return cachedText;\n }\n\n const getPageText = async () => {\n const pageView = await getPageView(pageIndex);\n const textContent = await pageView.pdfPage.getTextContent({\n // Deprecated option, set for compatibility with older PDF.js releases.\n normalizeWhitespace: true,\n });\n return textContent.items.map(it => it.str).join('');\n };\n\n // This function synchronously populates the cache with a promise so that\n // multiple calls don't call `PDFPageProxy.getTextContent` twice.\n const pageText = getPageText();\n pageTextCache.set(pageIndex, pageText);\n return pageText;\n}\n\n/**\n * Find the offset within the document's text at which a page begins.\n *\n * @param {number} pageIndex\n * @return {Promise<number>} - Offset of page's text within document text\n */\nasync function getPageOffset(pageIndex) {\n const viewer = getPDFViewer();\n if (pageIndex >= viewer.pagesCount) {\n /* istanbul ignore next - This should never be triggered */\n throw new Error('Invalid page index');\n }\n let offset = 0;\n for (let i = 0; i < pageIndex; i++) {\n const text = await getPageTextContent(i);\n offset += text.length;\n }\n return offset;\n}\n\n/**\n * @typedef PageOffset\n * @prop {number} index - Page index\n * @prop {number} offset - Character offset of start of page within document text\n * @prop {string} text - Text of page\n */\n\n/**\n * Find the page containing a text offset within the document.\n *\n * If the offset is invalid (less than 0 or greater than the length of the document)\n * then the nearest (first or last) page is returned.\n *\n * @param {number} offset\n * @return {Promise<PageOffset>}\n */\nasync function findPageByOffset(offset) {\n const viewer = getPDFViewer();\n\n let pageStartOffset = 0;\n let pageEndOffset = 0;\n let text = '';\n\n for (let i = 0; i < viewer.pagesCount; i++) {\n text = await getPageTextContent(i);\n pageStartOffset = pageEndOffset;\n pageEndOffset += text.length;\n\n if (pageEndOffset >= offset) {\n return { index: i, offset: pageStartOffset, text };\n }\n }\n\n // If the offset is beyond the end of the document, just pretend it was on\n // the last page.\n return { index: viewer.pagesCount - 1, offset: pageStartOffset, text };\n}\n\n/**\n * Return true if `char` is an ASCII space.\n *\n * This is more efficient than `/\\s/.test(char)` but does not handle Unicode\n * spaces.\n *\n * @param {string} char\n */\nfunction isSpace(char) {\n switch (char) {\n case ' ':\n case '\\f':\n case '\\n':\n case '\\r':\n case '\\t':\n case '\\v':\n case '\\u00a0': // nbsp\n return true;\n default:\n return false;\n }\n}\n\n/** @param {string} char */\nconst isNotSpace = char => !isSpace(char);\n\n/**\n * Locate the DOM Range which a position selector refers to.\n *\n * If the page is off-screen it may be in an unrendered state, in which case\n * the text layer will not have been created. In that case a placeholder\n * DOM element is created and the returned range refers to that placeholder.\n * In that case, the selector will need to be re-anchored when the page is\n * scrolled into view.\n *\n * @param {number} pageIndex - The PDF page index\n * @param {number} start - Character offset within the page's text\n * @param {number} end - Character offset within the page's text\n * @return {Promise<Range>}\n */\nasync function anchorByPosition(pageIndex, start, end) {\n const [page, pageText] = await Promise.all([\n getPageView(pageIndex),\n getPageTextContent(pageIndex),\n ]);\n\n if (\n page.renderingState === RenderingStates.FINISHED &&\n page.textLayer &&\n page.textLayer.renderingDone\n ) {\n // The page has been rendered. Locate the position in the text layer.\n //\n // We allow for differences in whitespace between the text returned by\n // `getPageTextContent` and the text layer content. Any other differences\n // will cause mis-anchoring.\n\n const root = page.textLayer.textLayerDiv;\n const textLayerStr = /** @type {string} */ (root.textContent);\n\n const [textLayerStart, textLayerEnd] = translateOffsets(\n pageText,\n textLayerStr,\n start,\n end,\n isNotSpace\n );\n\n const textLayerQuote = stripSpaces(\n textLayerStr.slice(textLayerStart, textLayerEnd)\n );\n const pageTextQuote = stripSpaces(pageText.slice(start, end));\n if (textLayerQuote !== pageTextQuote) {\n warnOnce(\n 'Text layer text does not match page text. Highlights will be mis-aligned.'\n );\n }\n\n const startPos = new TextPosition(root, textLayerStart);\n const endPos = new TextPosition(root, textLayerEnd);\n return new TextRange(startPos, endPos).toRange();\n }\n\n // The page has not been rendered yet. Create a placeholder element and\n // anchor to that instead.\n const placeholder = createPlaceholder(page.div);\n const range = document.createRange();\n range.setStartBefore(placeholder);\n range.setEndAfter(placeholder);\n return range;\n}\n\n/**\n * Return a string with spaces stripped.\n *\n * This function optimizes for performance of stripping the main space chars\n * that PDF.js generates over handling all kinds of whitespace that could\n * occur in a string.\n *\n * @param {string} str\n */\nfunction stripSpaces(str) {\n let stripped = '';\n for (let i = 0; i < str.length; i++) {\n const char = str[i];\n if (isSpace(char)) {\n continue;\n }\n stripped += char;\n }\n return stripped;\n}\n\n/**\n * Search for a quote in the given pages.\n *\n * When comparing quote selectors to document text, ASCII whitespace characters\n * are ignored. This is because text extracted from a PDF by different PDF\n * viewers, including different versions of PDF.js, can often differ in the\n * whitespace between characters and words. For a long time PDF.js in particular\n * had issues where it would often produce extra spaces between characters that\n * should not be there or omit spaces between words.\n *\n * @param {TextQuoteSelector} quoteSelector\n * @param {number} [positionHint] - Expected start offset of quote\n * @return {Promise<Range>} Location of quote\n */\nasync function anchorQuote(quoteSelector, positionHint) {\n // Determine which pages to search and in what order. If we have a position\n // hint we'll try to use that. Otherwise we'll just search all pages in order.\n const pageCount = getPDFViewer().pagesCount;\n const pageIndexes = Array(pageCount)\n .fill(0)\n .map((_, i) => i);\n\n let expectedPageIndex;\n let expectedOffsetInPage;\n\n if (positionHint) {\n const { index, offset } = await findPageByOffset(positionHint);\n expectedPageIndex = index;\n expectedOffsetInPage = positionHint - offset;\n\n // Sort pages by distance from the page where we expect to find the quote,\n // based on the position hint.\n pageIndexes.sort((a, b) => {\n const distA = Math.abs(a - index);\n const distB = Math.abs(b - index);\n return distA - distB;\n });\n }\n\n // Search pages for the best match, ignoring whitespace differences.\n const strippedPrefix =\n quoteSelector.prefix !== undefined\n ? stripSpaces(quoteSelector.prefix)\n : undefined;\n const strippedSuffix =\n quoteSelector.suffix !== undefined\n ? stripSpaces(quoteSelector.suffix)\n : undefined;\n const strippedQuote = stripSpaces(quoteSelector.exact);\n\n let bestMatch;\n for (let page of pageIndexes) {\n const text = await getPageTextContent(page);\n const strippedText = stripSpaces(text);\n\n // Determine expected offset of quote in current page based on position hint.\n let strippedHint;\n if (expectedPageIndex !== undefined && expectedOffsetInPage !== undefined) {\n if (page < expectedPageIndex) {\n strippedHint = strippedText.length; // Prefer matches closer to end of page.\n } else if (page === expectedPageIndex) {\n // Translate expected offset in whitespace-inclusive version of page\n // text into offset in whitespace-stripped version of page text.\n [strippedHint] = translateOffsets(\n text,\n strippedText,\n expectedOffsetInPage,\n expectedOffsetInPage,\n isNotSpace\n );\n } else {\n strippedHint = 0; // Prefer matches closer to start of page.\n }\n }\n\n const match = matchQuote(strippedText, strippedQuote, {\n prefix: strippedPrefix,\n suffix: strippedSuffix,\n hint: strippedHint,\n });\n\n if (!match) {\n continue;\n }\n\n if (!bestMatch || match.score > bestMatch.match.score) {\n // Translate match offset from whitespace-stripped version of page text\n // back to original text.\n const [start, end] = translateOffsets(\n strippedText,\n text,\n match.start,\n match.end,\n isNotSpace\n );\n bestMatch = {\n page,\n match: {\n start,\n end,\n score: match.score,\n },\n };\n\n // If we find a very good match, stop early.\n //\n // There is a tradeoff here between optimizing search performance and\n // ensuring that we have found the best match in the document.\n //\n // The current heuristics are that we require an exact match for the quote\n // and either the preceding or following context. The context matching\n // helps to avoid incorrectly stopping the search early if the quote is\n // a word or phrase that is common in the document.\n const exactQuoteMatch =\n strippedText.slice(match.start, match.end) === strippedQuote;\n\n const exactPrefixMatch =\n strippedPrefix !== undefined &&\n strippedText.slice(\n Math.max(0, match.start - strippedPrefix.length),\n match.start\n ) === strippedPrefix;\n\n const exactSuffixMatch =\n strippedSuffix !== undefined &&\n strippedText.slice(match.end, strippedSuffix.length) === strippedSuffix;\n\n const hasContext =\n strippedPrefix !== undefined || strippedSuffix !== undefined;\n\n if (\n exactQuoteMatch &&\n (exactPrefixMatch || exactSuffixMatch || !hasContext)\n ) {\n break;\n }\n }\n }\n\n if (bestMatch) {\n const { page, match } = bestMatch;\n\n // If we found a match, optimize future anchoring of this selector in the\n // same session by caching the match location.\n if (positionHint) {\n const cacheKey = quotePositionCacheKey(quoteSelector.exact, positionHint);\n quotePositionCache.set(cacheKey, {\n pageIndex: page,\n anchor: match,\n });\n }\n\n // Convert the (start, end) position match into a DOM range.\n return anchorByPosition(page, match.start, match.end);\n }\n\n throw new Error('Quote not found');\n}\n\n/**\n * Anchor a set of selectors to a DOM Range.\n *\n * `selectors` must include a `TextQuoteSelector` and may include other selector\n * types.\n *\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n * @return {Promise<Range>}\n */\nexport async function anchor(root, selectors) {\n const quote = /** @type {TextQuoteSelector|undefined} */ (\n selectors.find(s => s.type === 'TextQuoteSelector')\n );\n\n // The quote selector is required in order to check that text position\n // selector results are still valid.\n if (!quote) {\n throw new Error('No quote selector found');\n }\n\n const position = /** @type {TextPositionSelector|undefined} */ (\n selectors.find(s => s.type === 'TextPositionSelector')\n );\n\n if (position) {\n // If we have a position selector, try using that first as it is the fastest\n // anchoring method.\n try {\n const { index, offset, text } = await findPageByOffset(position.start);\n const start = position.start - offset;\n const end = position.end - offset;\n\n const matchedText = text.substring(start, end);\n if (quote.exact !== matchedText) {\n throw new Error('quote mismatch');\n }\n\n const range = await anchorByPosition(index, start, end);\n return range;\n } catch {\n // Fall back to quote selector\n }\n\n // If anchoring with the position failed, check for a cached quote-based\n // match using the quote + position as a cache key.\n try {\n const cacheKey = quotePositionCacheKey(quote.exact, position.start);\n const cachedPos = quotePositionCache.get(cacheKey);\n if (cachedPos) {\n const { pageIndex, anchor } = cachedPos;\n const range = await anchorByPosition(\n pageIndex,\n anchor.start,\n anchor.end\n );\n return range;\n }\n } catch {\n // Fall back to uncached quote selector match\n }\n }\n\n return anchorQuote(quote, position?.start);\n}\n\n/**\n * Prepare a DOM range for generating selectors and find the containing text layer.\n *\n * @param {Range} range\n * @return {[Range, Element]}\n * @throws If the range cannot be annotated\n */\nfunction getTextLayerForRange(range) {\n // \"Shrink\" the range so that the start and endpoints are at offsets within\n // text nodes rather than any containing nodes.\n try {\n range = TextRange.fromRange(range).toRange();\n } catch {\n throw new Error('Selection does not contain text');\n }\n\n const startTextLayer = getNodeTextLayer(range.startContainer);\n const endTextLayer = getNodeTextLayer(range.endContainer);\n\n if (!startTextLayer || !endTextLayer) {\n throw new Error('Selection is outside page text');\n }\n\n if (startTextLayer !== endTextLayer) {\n throw new Error('Selecting across page breaks is not supported');\n }\n\n return [range, startTextLayer];\n}\n\n/**\n * Return true if selectors can be generated for a range using `describe`.\n *\n * This function is faster than calling `describe` if the selectors are not\n * required.\n *\n * @param {Range} range\n */\nexport function canDescribe(range) {\n try {\n getTextLayerForRange(range);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Convert a DOM Range object into a set of selectors.\n *\n * Converts a DOM `Range` object into a `[position, quote]` tuple of selectors\n * which can be saved with an annotation and later passed to `anchor` to\n * convert the selectors back to a `Range`.\n *\n * @param {HTMLElement} root - The root element\n * @param {Range} range\n * @return {Promise<Selector[]>}\n */\nexport async function describe(root, range) {\n const [textRange, textLayer] = getTextLayerForRange(range);\n\n const startPos = TextPosition.fromPoint(\n textRange.startContainer,\n textRange.startOffset\n ).relativeTo(textLayer);\n\n const endPos = TextPosition.fromPoint(\n textRange.endContainer,\n textRange.endOffset\n ).relativeTo(textLayer);\n\n const startPageIndex = getSiblingIndex(\n /** @type {Node} */ (textLayer.parentNode)\n );\n const pageOffset = await getPageOffset(startPageIndex);\n\n /** @type {TextPositionSelector} */\n const position = {\n type: 'TextPositionSelector',\n start: pageOffset + startPos.offset,\n end: pageOffset + endPos.offset,\n };\n\n const quote = TextQuoteAnchor.fromRange(root, textRange).toSelector();\n\n return [position, quote];\n}\n\n/**\n * Clear this module's internal caches.\n *\n * This exists mainly as a helper for use in tests.\n */\nexport function purgeCache() {\n pageTextCache.clear();\n quotePositionCache.clear();\n}\n","/**\n * Render banners at the top of a document in a stacked column.\n *\n * @param {object} props\n * @param {import(\"preact\").ComponentChildren} props.children\n */\nexport default function Banners({ children }) {\n return <div className=\"flex flex-col\">{children}</div>;\n}\n","import { Icon, LabeledButton, Link } from '@hypothesis/frontend-shared';\n\n/**\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n */\n\n/**\n * A banner that informs the user about the provider of the document.\n *\n * @param {object} props\n * @param {ContentPartner} props.provider\n * @param {() => void} props.onClose\n */\nexport default function ContentPartnerBanner({ provider, onClose }) {\n return (\n <div className=\"flex items-center border-b gap-x-4 px-2 py-1 bg-white text-annotator-lg\">\n {provider === 'jstor' && (\n <>\n <Link href=\"https://jstor.org\" target=\"_blank\">\n <Icon\n classes=\"w-[97px] h-[25px]\"\n name=\"jstor\"\n title=\"Document hosted by JSTOR\"\n />\n </Link>\n <div className=\"grow\">\n Document hosted by <b>JSTOR</b>\n </div>\n </>\n )}\n <div className=\"text-annotator-base\">\n <LabeledButton onClick={onClose} data-testid=\"close-button\">\n Close\n </LabeledButton>\n </div>\n </div>\n );\n}\n","import { Icon, Link } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * A banner shown at the top of the PDF viewer if the PDF cannot be annotated\n * by Hypothesis.\n */\nexport default function WarningBanner() {\n return (\n <div className=\"bg-white\">\n <div\n className={classnames(\n 'flex items-center gap-x-2',\n 'border border-yellow-notice bg-yellow-notice/10 text-annotator-base'\n )}\n >\n <div className=\"bg-yellow-notice text-white p-2\">\n <Icon name=\"caution\" classes=\"text-annotator-xl\" />\n </div>\n <div>\n <strong>This PDF does not contain selectable text:</strong>{' '}\n <Link\n target=\"_blank\"\n href=\"https://web.hypothes.is/help/how-to-ocr-optimize-pdfs/\"\n >\n Learn how to fix this\n </Link>{' '}\n in order to annotate with Hypothesis.\n </div>\n </div>\n </div>\n );\n}\n","import { normalizeURI } from '../util/url';\n\n/**\n * @typedef {import('../../types/pdfjs').PDFViewerApplication} PDFViewerApplication\n */\n\n/**\n * @typedef Link\n * @prop {string} href\n */\n\n/**\n * @typedef Metadata\n * @prop {string} title - The document title\n * @prop {Link[]} link - Array of URIs associated with this document\n * @prop {string} documentFingerprint - The fingerprint of this PDF. This is\n * referred to as the \"File Identifier\" in the PDF spec. It may be a hash of\n * part of the content if the PDF file does not have a File Identifier.\n *\n * PDFs may have two file identifiers. The first is the \"original\" identifier\n * which is not supposed to change if the file is updated and the second\n * one is the \"last modified\" identifier. This property is the original\n * identifier.\n */\n\n/**\n * Wait for a PDFViewerApplication to be initialized.\n *\n * @param {PDFViewerApplication} app\n * @return {Promise<void>}\n */\nfunction pdfViewerInitialized(app) {\n // `initializedPromise` was added in PDF.js v2.4.456.\n // See https://github.com/mozilla/pdf.js/pull/11607. In earlier versions the\n // `initialized` property can be queried.\n if (app.initializedPromise) {\n return app.initializedPromise;\n } else if (app.initialized) {\n return Promise.resolve();\n } else {\n // PDF.js < v2.4.456. The recommended approach is to listen for a `localized`\n // DOM event, but this assumes that PDF.js has been configured to publish\n // events to the DOM. Here we simply poll `app.initialized` because it is\n // easier.\n return new Promise(resolve => {\n const timeout = setInterval(() => {\n if (app.initialized) {\n clearTimeout(timeout);\n resolve();\n }\n }, 5);\n });\n }\n}\n\n/**\n * PDFMetadata extracts metadata about a loading/loaded PDF document from a\n * PDF.js PDFViewerApplication object.\n *\n * @example\n * // Invoke in a PDF.js viewer, before or after the PDF has finished loading.\n * const meta = new PDFMetadata(window.PDFViewerApplication)\n * meta.getUri().then(uri => {\n * // Do something with the URL of the PDF.\n * })\n */\nexport class PDFMetadata {\n /**\n * Construct a `PDFMetadata` that returns URIs/metadata associated with a\n * given PDF viewer.\n *\n * @param {PDFViewerApplication} app - The `PDFViewerApplication` global from PDF.js\n */\n constructor(app) {\n /** @type {Promise<PDFViewerApplication>} */\n this._loaded = pdfViewerInitialized(app).then(() => {\n // Check if document has already loaded.\n if (app.downloadComplete) {\n return app;\n }\n\n return new Promise(resolve => {\n const finish = () => {\n if (app.eventBus) {\n app.eventBus.off('documentload', finish);\n app.eventBus.off('documentloaded', finish);\n } else {\n window.removeEventListener('documentload', finish);\n }\n resolve(app);\n };\n\n // Listen for \"documentloaded\" event which signals that the document\n // has been downloaded and the first page has been rendered.\n if (app.eventBus) {\n // PDF.js >= v1.6.210 dispatch events via an internal event bus.\n // PDF.js < v2.5.207 also dispatches events to the DOM.\n\n // `documentloaded` is the preferred event in PDF.js >= v2.0.943.\n // See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2\n app.eventBus.on('documentloaded', finish);\n\n // `documentload` is dispatched by PDF.js < v2.1.266.\n app.eventBus.on('documentload', finish);\n } else {\n // PDF.js < v1.6.210 dispatches events only to the DOM.\n window.addEventListener('documentload', finish);\n }\n });\n });\n }\n\n /**\n * Return the URI of the PDF.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n *\n * @return {Promise<string>}\n */\n getUri() {\n return this._loaded.then(app => {\n let uri = getPDFURL(app);\n if (!uri) {\n uri = fingerprintToURN(getFingerprint(app));\n }\n return uri;\n });\n }\n\n /**\n * Returns metadata about the document.\n *\n * If the PDF is currently loading, the returned promise resolves once loading\n * is complete.\n *\n * @return {Promise<Metadata>}\n */\n async getMetadata() {\n const app = await this._loaded;\n const {\n info: documentInfo,\n contentDispositionFilename,\n metadata,\n } = await app.pdfDocument.getMetadata();\n\n const documentFingerprint = getFingerprint(app);\n const url = getPDFURL(app);\n\n // Return the title metadata embedded in the PDF if available, otherwise\n // fall back to values from the `Content-Disposition` header or URL.\n //\n // PDFs contain two embedded metadata sources, the metadata stream and\n // the document info dictionary. Per the specification, the metadata stream\n // is preferred if available.\n //\n // This logic is similar to how PDF.js sets `document.title`.\n let title;\n if (metadata?.has('dc:title') && metadata.get('dc:title') !== 'Untitled') {\n title = /** @type {string} */ (metadata.get('dc:title'));\n } else if (documentInfo?.Title) {\n title = documentInfo.Title;\n } else if (contentDispositionFilename) {\n title = contentDispositionFilename;\n } else if (url) {\n title = filenameFromURL(url);\n } else {\n title = '';\n }\n\n const link = [{ href: fingerprintToURN(documentFingerprint) }];\n if (url) {\n link.push({ href: url });\n }\n\n return {\n title,\n link,\n documentFingerprint,\n };\n }\n}\n\n/**\n * Get the fingerprint/file identifier of the currently loaded PDF.\n *\n * @param {PDFViewerApplication} app\n */\nfunction getFingerprint(app) {\n if (Array.isArray(app.pdfDocument.fingerprints)) {\n return app.pdfDocument.fingerprints[0];\n } else {\n return /** @type {string} */ (app.pdfDocument.fingerprint);\n }\n}\n\n/**\n * Generate a URI from a PDF fingerprint suitable for storing as the main\n * or associated URI of an annotation.\n *\n * @param {string} fingerprint\n */\nfunction fingerprintToURN(fingerprint) {\n return `urn:x-pdf:${fingerprint}`;\n}\n\n/**\n * @param {PDFViewerApplication} app\n * @return {string|null} - Valid URL string or `null`\n */\nfunction getPDFURL(app) {\n if (!app.url) {\n return null;\n }\n\n const url = normalizeURI(app.url);\n\n // Local file:// URLs should not be saved in document metadata.\n // Entries in document.link should be URIs. In the case of\n // local files, omit the URL.\n if (url.indexOf('file://') !== 0) {\n return url;\n }\n\n return null;\n}\n\n/**\n * Return the last component of the path part of a URL.\n *\n * @param {string} url - A valid URL string\n * @return {string}\n */\nfunction filenameFromURL(url) {\n const parsed = new URL(url);\n const pathSegments = parsed.pathname.split('/');\n return pathSegments[pathSegments.length - 1];\n}\n","import debounce from 'lodash.debounce';\nimport { render } from 'preact';\n\nimport { ListenerCollection } from '../../shared/listener-collection';\nimport {\n RenderingStates,\n anchor,\n canDescribe,\n describe,\n documentHasText,\n} from '../anchoring/pdf';\nimport { isInPlaceholder, removePlaceholder } from '../anchoring/placeholder';\nimport Banners from '../components/Banners';\nimport ContentPartnerBanner from '../components/ContentPartnerBanner';\nimport WarningBanner from '../components/WarningBanner';\nimport { createShadowRoot } from '../util/shadow-root';\nimport { offsetRelativeTo, scrollElement } from '../util/scroll';\n\nimport { PDFMetadata } from './pdf-metadata';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').AnnotationData} AnnotationData\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n * @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../../types/api').Selector} Selector\n * @typedef {import('../../types/pdfjs').PDFViewerApplication} PDFViewerApplication\n */\n\n// The viewport and controls for PDF.js start breaking down below about 670px\n// of available space, so only render PDF and sidebar side-by-side if there\n// is enough room. Otherwise, allow sidebar to overlap PDF\nconst MIN_PDF_WIDTH = 680;\n\n/**\n * Return true if `anchor` is in an un-rendered page.\n *\n * @param {Anchor} anchor\n */\nfunction anchorIsInPlaceholder(anchor) {\n const highlight = anchor.highlights?.[0];\n return highlight && isInPlaceholder(highlight);\n}\n\n/** @param {number} ms */\nfunction delay(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Is the current document the PDF.js viewer application?\n */\nexport function isPDF() {\n // @ts-ignore - TS doesn't know about PDFViewerApplication global.\n return typeof PDFViewerApplication !== 'undefined';\n}\n\n/**\n * Integration that works with PDF.js\n * @implements {Integration}\n */\nexport class PDFIntegration {\n /**\n * @param {Annotator} annotator\n * @param {object} options\n * @param {ContentPartner} [options.contentPartner] - If set, show branding\n * for the given content partner in a banner above the PDF viewer.\n * @param {number} [options.reanchoringMaxWait] - Max time to wait for\n * re-anchoring to complete when scrolling to an un-rendered page.\n */\n constructor(annotator, options = {}) {\n this.annotator = annotator;\n\n const window_ = /** @type {HypothesisWindow} */ (window);\n\n // Assume this class is only used if we're in the PDF.js viewer.\n const pdfViewerApp = /** @type {PDFViewerApplication} */ (\n window_.PDFViewerApplication\n );\n\n this.pdfViewer = pdfViewerApp.pdfViewer;\n this.pdfViewer.viewer.classList.add('has-transparent-text-layer');\n\n // Get the element that contains all of the PDF.js UI. This is typically\n // `document.body`.\n this.pdfContainer = pdfViewerApp.appConfig?.appContainer ?? document.body;\n\n this.pdfMetadata = new PDFMetadata(pdfViewerApp);\n\n this.observer = new MutationObserver(debounce(() => this._update(), 100));\n this.observer.observe(this.pdfViewer.viewer, {\n attributes: true,\n attributeFilter: ['data-loaded'],\n childList: true,\n subtree: true,\n });\n\n /**\n * Amount of time to wait for re-anchoring to complete when scrolling to\n * an anchor in a not-yet-rendered page.\n */\n this._reanchoringMaxWait = options.reanchoringMaxWait ?? 3000;\n\n /**\n * Banners shown at the top of the PDF viewer.\n *\n * @type {HTMLElement|null}\n */\n this._banner = null;\n\n /** State indicating which banners to show above the PDF viewer. */\n this._bannerState = {\n /**\n * Branding for a content provider.\n *\n * @type {ContentPartner|null}\n */\n contentPartner: options.contentPartner ?? null,\n /** Warning that the current PDF does not have selectable text. */\n noTextWarning: false,\n };\n this._updateBannerState(this._bannerState);\n this._checkForSelectableText();\n\n // Hide annotation layer when the user is making a selection. The annotation\n // layer appears above the invisible text layer and can interfere with text\n // selection. See https://github.com/hypothesis/client/issues/1464.\n this._updateAnnotationLayerVisibility = () => {\n const selection = /** @type {Selection} */ (window_.getSelection());\n\n // Add CSS class to indicate whether there is a selection. Annotation\n // layers are then hidden by a CSS rule in `pdfjs-overrides.scss`.\n this.pdfViewer.viewer.classList.toggle(\n 'is-selecting',\n !selection.isCollapsed\n );\n };\n\n this._listeners = new ListenerCollection();\n this._listeners.add(\n document,\n 'selectionchange',\n this._updateAnnotationLayerVisibility\n );\n\n // A flag that indicates whether `destroy` has been called. Used to handle\n // `destroy` being called during async code elsewhere in the class.\n this._destroyed = false;\n }\n\n destroy() {\n this._listeners.removeAll();\n this.pdfViewer.viewer.classList.remove('has-transparent-text-layer');\n this.observer.disconnect();\n this._banner?.remove();\n this._destroyed = true;\n }\n\n /**\n * Return the URL of the currently loaded PDF document.\n */\n uri() {\n return this.pdfMetadata.getUri();\n }\n\n /**\n * Return the metadata (eg. title) for the currently loaded PDF document.\n */\n getMetadata() {\n return this.pdfMetadata.getMetadata();\n }\n\n /**\n * Resolve serialized `selectors` from an annotation to a range.\n *\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n * @return {Promise<Range>}\n */\n anchor(root, selectors) {\n // nb. The `root` argument is not really used by `anchor`. It existed for\n // consistency between HTML and PDF anchoring and could be removed.\n return anchor(root, selectors);\n }\n\n /**\n * Return true if the text in a range lies within the text layer of a PDF.\n *\n * @param {Range} range\n */\n canAnnotate(range) {\n return canDescribe(range);\n }\n\n /**\n * Generate selectors for the text in `range`.\n *\n * @param {HTMLElement} root\n * @param {Range} range\n * @return {Promise<Selector[]>}\n */\n describe(root, range) {\n // nb. The `root` argument is not really used by `anchor`. It existed for\n // consistency between HTML and PDF anchoring and could be removed.\n return describe(root, range);\n }\n\n /**\n * Check whether the PDF has selectable text and show a warning if not.\n */\n async _checkForSelectableText() {\n // Wait for PDF to load.\n try {\n await this.uri();\n } catch (e) {\n return;\n }\n\n // Handle `PDF` instance being destroyed while URI is fetched. This is only\n // expected to happen in synchronous tests.\n if (this._destroyed) {\n return;\n }\n\n try {\n const hasText = await documentHasText();\n this._updateBannerState({ noTextWarning: !hasText });\n } catch (err) {\n /* istanbul ignore next */\n console.warn('Unable to check for text in PDF:', err);\n }\n }\n\n /**\n * Update banners shown above the PDF viewer.\n *\n * @param {Partial<typeof PDFIntegration.prototype._bannerState>} state\n */\n _updateBannerState(state) {\n this._bannerState = { ...this._bannerState, ...state };\n\n // Get a reference to the top-level DOM element associated with the PDF.js\n // viewer.\n const outerContainer = /** @type {HTMLElement} */ (\n document.querySelector('#outerContainer')\n );\n\n const showBanner =\n this._bannerState.contentPartner || this._bannerState.noTextWarning;\n\n if (!showBanner) {\n this._banner?.remove();\n this._banner = null;\n\n // Undo inline styles applied when the banner is shown. The banner will\n // then gets its normal 100% height set by PDF.js's CSS.\n outerContainer.style.height = '';\n\n return;\n }\n\n if (!this._banner) {\n this._banner = document.createElement('hypothesis-banner');\n document.body.prepend(this._banner);\n createShadowRoot(this._banner);\n }\n\n render(\n <Banners>\n {this._bannerState.contentPartner && (\n <ContentPartnerBanner\n provider={this._bannerState.contentPartner}\n onClose={() => this._updateBannerState({ contentPartner: null })}\n />\n )}\n {this._bannerState.noTextWarning && <WarningBanner />}\n </Banners>,\n /** @type {ShadowRoot} */ (this._banner.shadowRoot)\n );\n\n const bannerHeight = this._banner.getBoundingClientRect().height;\n\n // The `#outerContainer` element normally has height set to 100% of the body.\n //\n // Reduce this by the height of the banner so that it doesn't extend beyond\n // the bottom of the viewport.\n //\n // We don't currently handle the height of the banner changing here.\n outerContainer.style.height = `calc(100% - ${bannerHeight}px)`;\n }\n\n // This method (re-)anchors annotations when pages are rendered and destroyed.\n _update() {\n // A list of annotations that need to be refreshed.\n const refreshAnnotations = /** @type {AnnotationData[]} */ ([]);\n\n const pageCount = this.pdfViewer.pagesCount;\n for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {\n const page = this.pdfViewer.getPageView(pageIndex);\n if (!page?.textLayer?.renderingDone) {\n continue;\n }\n\n // Detect what needs to be done by checking the rendering state.\n switch (page.renderingState) {\n case RenderingStates.INITIAL:\n // This page has been reset to its initial state so its text layer\n // is no longer valid. Null it out so that we don't process it again.\n page.textLayer = null;\n break;\n case RenderingStates.FINISHED:\n // This page is still rendered. If it has a placeholder node that\n // means the PDF anchoring module anchored annotations before it was\n // rendered. Remove this, which will cause the annotations to anchor\n // again, below.\n removePlaceholder(page.div);\n break;\n }\n }\n\n // Find all the anchors that have been invalidated by page state changes.\n for (let anchor of this.annotator.anchors) {\n // Skip any we already know about.\n if (anchor.highlights) {\n if (refreshAnnotations.includes(anchor.annotation)) {\n continue;\n }\n\n // If the highlights are no longer in the document it means that either\n // the page was destroyed by PDF.js or the placeholder was removed above.\n // The annotations for these anchors need to be refreshed.\n for (let index = 0; index < anchor.highlights.length; index++) {\n const hl = anchor.highlights[index];\n if (!document.body.contains(hl)) {\n anchor.highlights.splice(index, 1);\n delete anchor.range;\n refreshAnnotations.push(anchor.annotation);\n break;\n }\n }\n }\n }\n\n refreshAnnotations.map(annotation => this.annotator.anchor(annotation));\n }\n\n /**\n * Return the scrollable element which contains the document content.\n *\n * @return {HTMLElement}\n */\n contentContainer() {\n return /** @type {HTMLElement} */ (\n document.querySelector('#viewerContainer')\n );\n }\n\n /**\n * Attempt to make the PDF viewer and the sidebar fit side-by-side without\n * overlap if there is enough room in the viewport to do so reasonably.\n * Resize the PDF viewer container element to leave the right amount of room\n * for the sidebar, and prompt PDF.js to re-render the PDF pages to scale\n * within that resized container.\n *\n * @param {SidebarLayout} sidebarLayout\n * @return {boolean} - True if side-by-side mode was activated\n */\n fitSideBySide(sidebarLayout) {\n const maximumWidthToFit = window.innerWidth - sidebarLayout.width;\n const active = sidebarLayout.expanded && maximumWidthToFit >= MIN_PDF_WIDTH;\n\n // If the sidebar is closed, we reserve enough space for the toolbar controls\n // so that they don't overlap a) the chevron-menu on the right side of\n // PDF.js's top toolbar and b) the document's scrollbar.\n //\n // If the sidebar is open, we reserve space for the whole sidebar if there is\n // room, otherwise we reserve the same space as in the closed state to\n // prevent the PDF content shifting when opening and closing the sidebar.\n const reservedSpace = active\n ? sidebarLayout.width\n : sidebarLayout.toolbarWidth;\n this.pdfContainer.style.width = `calc(100% - ${reservedSpace}px)`;\n\n // The following logic is pulled from PDF.js `webViewerResize`\n const currentScaleValue = this.pdfViewer.currentScaleValue;\n if (\n currentScaleValue === 'auto' ||\n currentScaleValue === 'page-fit' ||\n currentScaleValue === 'page-width'\n ) {\n // NB: There is logic within the setter for `currentScaleValue`\n // Setting this scale value will prompt PDF.js to recalculate viewport\n this.pdfViewer.currentScaleValue = currentScaleValue;\n }\n // This will cause PDF pages to re-render if their scaling has changed\n this.pdfViewer.update();\n\n return active;\n }\n\n /**\n * Scroll to the location of an anchor in the PDF.\n *\n * If the anchor refers to a location that is an un-rendered page far from\n * the viewport, then scrolling happens in three phases. First the document\n * scrolls to the approximate location indicated by the placeholder anchor,\n * then `scrollToAnchor` waits until the page's text layer is rendered and\n * the annotation is re-anchored in the fully rendered page. Then it scrolls\n * again to the final location.\n *\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n const annotation = anchor.annotation;\n const inPlaceholder = anchorIsInPlaceholder(anchor);\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n\n // nb. We only compute the scroll offset once at the start of scrolling.\n // This is important as the highlight may be removed from the document during\n // the scroll due to a page transitioning from rendered <-> un-rendered.\n await scrollElement(this.contentContainer(), offset);\n\n if (inPlaceholder) {\n const anchor = await this._waitForAnnotationToBeAnchored(\n annotation,\n this._reanchoringMaxWait\n );\n if (!anchor) {\n return;\n }\n const offset = this._anchorOffset(anchor);\n if (offset === null) {\n return;\n }\n await scrollElement(this.contentContainer(), offset);\n }\n }\n\n /**\n * Wait for an annotation to be anchored in a rendered page.\n *\n * @param {AnnotationData} annotation\n * @param {number} maxWait\n * @return {Promise<Anchor|null>}\n */\n async _waitForAnnotationToBeAnchored(annotation, maxWait) {\n const start = Date.now();\n let anchor;\n do {\n // nb. Re-anchoring might result in a different anchor object for the\n // same annotation.\n anchor = this.annotator.anchors.find(a => a.annotation === annotation);\n if (!anchor || anchorIsInPlaceholder(anchor)) {\n anchor = null;\n\n // If no anchor was found, wait a bit longer and check again to see if\n // re-anchoring completed.\n await delay(20);\n }\n } while (!anchor && Date.now() - start < maxWait);\n return anchor ?? null;\n }\n\n /**\n * Return the offset that the PDF content container would need to be scrolled\n * to, in order to make an anchor visible.\n *\n * @param {Anchor} anchor\n * @return {number|null} - Target offset or `null` if this anchor was not resolved\n */\n _anchorOffset(anchor) {\n if (!anchor.highlights) {\n // This anchor was not resolved to a location in the document.\n return null;\n }\n const highlight = anchor.highlights[0];\n return offsetRelativeTo(highlight, this.contentContainer());\n }\n}\n","import debounce from 'lodash.debounce';\n\nexport const DEBOUNCE_WAIT = 40;\n\n/** @typedef {(frame: HTMLIFrameElement) => void} FrameCallback */\n\n/**\n * FrameObserver detects iframes added and deleted from the document.\n *\n * To enable annotation, an iframe must be opted-in by adding the\n * `enable-annotation` attribute.\n *\n * We require the `enable-annotation` attribute to avoid the overhead of loading\n * the client into frames which are not useful to annotate. See\n * https://github.com/hypothesis/client/issues/530\n */\nexport class FrameObserver {\n /**\n * @param {Element} element - root of the DOM subtree to watch for the addition\n * and removal of annotatable iframes\n * @param {FrameCallback} onFrameAdded - callback fired when an annotatable iframe is added\n * @param {FrameCallback} onFrameRemoved - callback triggered when the annotatable iframe is removed\n */\n constructor(element, onFrameAdded, onFrameRemoved) {\n this._element = element;\n this._onFrameAdded = onFrameAdded;\n this._onFrameRemoved = onFrameRemoved;\n /** @type {Set<HTMLIFrameElement>} */\n this._annotatableFrames = new Set();\n this._isDisconnected = false;\n\n this._mutationObserver = new MutationObserver(\n debounce(() => {\n this._discoverFrames();\n }, DEBOUNCE_WAIT)\n );\n this._discoverFrames();\n this._mutationObserver.observe(this._element, {\n childList: true,\n subtree: true,\n attributeFilter: ['enable-annotation'],\n });\n }\n\n disconnect() {\n this._isDisconnected = true;\n this._mutationObserver.disconnect();\n }\n\n /**\n * @param {HTMLIFrameElement} frame\n */\n async _addFrame(frame) {\n this._annotatableFrames.add(frame);\n try {\n await onNextDocumentReady(frame);\n if (this._isDisconnected) {\n return;\n }\n const frameWindow = frame.contentWindow;\n // @ts-expect-error\n // This line raises an exception if the iframe is from a different origin\n frameWindow.addEventListener('unload', () => {\n this._removeFrame(frame);\n });\n this._onFrameAdded(frame);\n } catch (e) {\n console.warn(\n `Unable to inject the Hypothesis client (from '${document.location.href}' into a cross-origin frame '${frame.src}')`\n );\n }\n }\n\n /**\n * @param {HTMLIFrameElement} frame\n */\n _removeFrame(frame) {\n this._annotatableFrames.delete(frame);\n this._onFrameRemoved(frame);\n }\n\n _discoverFrames() {\n const frames = new Set(\n /** @type {NodeListOf<HTMLIFrameElement> } */ (\n this._element.querySelectorAll('iframe[enable-annotation]')\n )\n );\n\n for (let frame of frames) {\n if (!this._annotatableFrames.has(frame)) {\n this._addFrame(frame);\n }\n }\n\n for (let frame of this._annotatableFrames) {\n if (!frames.has(frame)) {\n this._removeFrame(frame);\n }\n }\n }\n}\n\n/**\n * Test if this is the empty document that a new iframe has before the URL\n * specified by its `src` attribute loads.\n *\n * @param {HTMLIFrameElement} frame\n */\nfunction hasBlankDocumentThatWillNavigate(frame) {\n return (\n frame.contentDocument?.location.href === 'about:blank' &&\n // Do we expect the frame to navigate away from about:blank?\n frame.hasAttribute('src') &&\n frame.src !== 'about:blank'\n );\n}\n\n/**\n * Wrapper around {@link onDocumentReady} which returns a promise that resolves\n * the first time that a document in `frame` becomes ready.\n *\n * See {@link onDocumentReady} for the definition of _ready_.\n *\n * @param {HTMLIFrameElement} frame\n * @return {Promise<Document>}\n */\nexport function onNextDocumentReady(frame) {\n return new Promise((resolve, reject) => {\n const unsubscribe = onDocumentReady(frame, (err, doc) => {\n unsubscribe();\n if (doc) {\n resolve(doc);\n } else {\n reject(err);\n }\n });\n });\n}\n\n/**\n * Register a callback that is invoked when the content document\n * (`frame.contentDocument`) in a same-origin iframe becomes _ready_.\n *\n * A document is _ready_ when its `readyState` is either \"interactive\" or\n * \"complete\". It must also not be the empty document with URL \"about:blank\"\n * that iframes have before they navigate to the URL specified by their \"src\"\n * attribute.\n *\n * The callback is fired both for the document that is in the frame when\n * `onDocumentReady` is called, as well as for new documents that are\n * subsequently loaded into the same frame.\n *\n * If at any time the frame navigates to an iframe that is cross-origin,\n * the callback will fire with an error. It will fire again for subsequent\n * navigations, but due to platform limitations, it will only fire after the\n * next document fully loads (ie. when the frame's `load` event fires).\n *\n * @param {HTMLIFrameElement} frame\n * @param {(...args: [Error]|[null, Document]) => void} callback\n * @param {object} options\n * @param {number} [options.pollInterval]\n * @return {() => void} Callback that unsubscribes from future changes\n */\nexport function onDocumentReady(frame, callback, { pollInterval = 10 } = {}) {\n /** @type {number|undefined} */\n let pollTimer;\n /** @type {() => void} */\n let pollForDocumentChange;\n\n // Visited documents for which we have fired the callback or are waiting\n // to become ready.\n const documents = new WeakSet();\n\n const cancelPoll = () => {\n clearTimeout(pollTimer);\n pollTimer = undefined;\n };\n\n // Begin polling for a document change when the current document is about\n // to go away.\n const pollOnUnload = () => {\n if (frame.contentDocument) {\n frame.contentWindow?.addEventListener('unload', pollForDocumentChange);\n }\n };\n\n const checkForDocumentChange = () => {\n const currentDocument = frame.contentDocument;\n if (!currentDocument) {\n callback(new Error('Frame is cross-origin'));\n return;\n }\n\n if (documents.has(currentDocument)) {\n return;\n }\n documents.add(currentDocument);\n cancelPoll();\n\n if (!hasBlankDocumentThatWillNavigate(frame)) {\n const isReady =\n currentDocument.readyState === 'interactive' ||\n currentDocument.readyState === 'complete';\n\n if (isReady) {\n callback(null, currentDocument);\n } else {\n currentDocument.addEventListener('DOMContentLoaded', () =>\n callback(null, currentDocument)\n );\n }\n }\n\n // Poll for the next document change.\n pollOnUnload();\n };\n\n let canceled = false;\n pollForDocumentChange = () => {\n cancelPoll();\n if (!canceled) {\n pollTimer = setInterval(checkForDocumentChange, pollInterval);\n }\n };\n\n // Set up observers for signals that the document either has changed or will\n // soon change. There are two signals with different trade-offs:\n //\n // - Polling after the current document is about to be unloaded. This allows\n // us to detect the new document quickly, but may not fire in some\n // situations (exact circumstances unclear, but eg. MDN warns about this).\n //\n // This is set up in the first call to `checkForDocumentChange`.\n //\n // - The iframe's \"load\" event. This is guaranteed to fire but only after the\n // new document is fully loaded.\n frame.addEventListener('load', checkForDocumentChange);\n\n // Notify caller about the current document. This fires asynchronously so that\n // the caller will have received the unsubscribe callback first.\n const initialCheckTimer = setTimeout(checkForDocumentChange, 0);\n\n return () => {\n canceled = true;\n clearTimeout(initialCheckTimer);\n cancelPoll();\n frame.removeEventListener('load', checkForDocumentChange);\n };\n}\n","import debounce from 'lodash.debounce';\n\nimport { ListenerCollection } from '../../shared/listener-collection';\nimport {\n rectCenter,\n rectsOverlapHorizontally,\n rectsOverlapVertically,\n unionRects,\n} from '../util/geometry';\n\n/**\n * @typedef WordBox\n * @prop {string} text\n * @prop {DOMRect} rect - Bounding rectangle of all glyphs in word\n */\n\n/**\n * @typedef LineBox\n * @prop {WordBox[]} words\n * @prop {DOMRect} rect - Bounding rectangle of all words in line\n */\n\n/**\n * @typedef ColumnBox\n * @prop {LineBox[]} lines\n * @prop {DOMRect} rect - Bounding rectangle of all lines in column\n */\n\n/**\n * Group characters in a page into words, lines and columns.\n *\n * The input is assumed to be _roughly_ reading order, more so at the low (word,\n * line) level. When the input is not in reading order, the output may be\n * divided into more lines / columns than expected. Downstream code is expected\n * to tolerate over-segmentation. This function should try to avoid producing\n * lines or columns that significantly intersect, as this can impair text\n * selection.\n *\n * @param {DOMRect[]} charBoxes - Bounding rectangle associated with each character on the page\n * @param {string} text - Text that corresponds to `charBoxes`\n * @return {ColumnBox[]}\n */\nfunction analyzeLayout(charBoxes, text) {\n /** @type {WordBox[]} */\n const words = [];\n\n /** @type {WordBox} */\n let currentWord = { text: '', rect: new DOMRect() };\n\n // Group characters into words.\n const addWord = () => {\n if (currentWord.text.length > 0) {\n words.push(currentWord);\n currentWord = { text: '', rect: new DOMRect() };\n }\n };\n for (let [i, rect] of charBoxes.entries()) {\n const char = text[i];\n const isSpace = /\\s/.test(char);\n\n currentWord.rect = unionRects(currentWord.rect, rect);\n\n // To simplify downstream logic, normalize whitespace.\n currentWord.text += isSpace ? ' ' : char;\n\n if (isSpace) {\n addWord();\n }\n }\n addWord();\n\n /** @type {LineBox[]} */\n const lines = [];\n\n /** @type {LineBox} */\n let currentLine = { words: [], rect: new DOMRect() };\n\n // Group words into lines.\n const addLine = () => {\n if (currentLine.words.length > 0) {\n lines.push(currentLine);\n currentLine = { words: [], rect: new DOMRect() };\n }\n };\n for (let word of words) {\n const prevWord = currentLine.words[currentLine.words.length - 1];\n if (prevWord) {\n const prevCenter = rectCenter(prevWord.rect);\n const currentCenter = rectCenter(word.rect);\n const xDist = currentCenter.x - prevCenter.x;\n if (\n !rectsOverlapVertically(prevWord.rect, word.rect) ||\n // Break line if current word is left of previous word\n xDist < 0\n ) {\n addLine();\n }\n }\n currentLine.words.push(word);\n currentLine.rect = unionRects(currentLine.rect, word.rect);\n }\n addLine();\n\n /** @type {ColumnBox[]} */\n const columns = [];\n\n /** @type {ColumnBox} */\n let currentColumn = { lines: [], rect: new DOMRect() };\n\n // Group lines into columns.\n const addColumn = () => {\n if (currentColumn.lines.length > 0) {\n columns.push(currentColumn);\n currentColumn = { lines: [], rect: new DOMRect() };\n }\n };\n for (let line of lines) {\n const prevLine = currentColumn.lines[currentColumn.lines.length - 1];\n\n if (prevLine) {\n const prevCenter = rectCenter(prevLine.rect);\n const currentCenter = rectCenter(line.rect);\n const yDist = currentCenter.y - prevCenter.y;\n\n if (\n !rectsOverlapHorizontally(prevLine.rect, line.rect) ||\n rectsOverlapVertically(prevLine.rect, line.rect) ||\n // Break column if current line is above previous line.\n //\n // In the case of a two column layout for example, this happens when\n // moving from the bottom of one column to the top of the next.\n yDist < 0 ||\n // Break column if there is a large gap between the previous and current lines.\n //\n // This helps to avoid generating intersecting columns if there happens\n // to be other content between the lines that comes later in the input.\n yDist > line.rect.height * 4\n ) {\n addColumn();\n }\n }\n currentColumn.lines.push(line);\n currentColumn.rect = unionRects(currentColumn.rect, line.rect);\n }\n addColumn();\n\n return columns;\n}\n\n/**\n * ImageTextLayer maintains a transparent text layer on top of an image\n * which contains text. This enables the text in the image to be selected\n * and highlighted.\n *\n * This is similar to the one that PDF.js creates for us in our standard PDF\n * viewer.\n */\nexport class ImageTextLayer {\n /**\n * Create a text layer which is displayed on top of `image`.\n *\n * @param {Element} image - Rendered image on which to overlay the text layer.\n * The text layer will be inserted into the DOM as the next sibling of `image`.\n * @param {DOMRect[]} charBoxes - Bounding boxes for characters in the image.\n * Coordinates should be in the range [0-1], where 0 is the top/left corner\n * of the image and 1 is the bottom/right.\n * @param {string} text - Characters in the image corresponding to `charBoxes`\n */\n constructor(image, charBoxes, text) {\n if (charBoxes.length !== text.length) {\n throw new Error('Char boxes length does not match text length');\n }\n\n // Create container for text layer and position it above the image.\n const containerParent = /** @type {HTMLElement} */ (image.parentNode);\n const container = document.createElement('hypothesis-text-layer');\n containerParent.insertBefore(container, image.nextSibling);\n\n // Position text layer over image. We assume the image's top-left corner\n // aligns with the top-left corner of its container.\n containerParent.style.position = 'relative';\n container.style.position = 'absolute';\n container.style.top = '0';\n container.style.left = '0';\n container.style.color = 'transparent';\n\n // Prevent inherited text alignment from affecting positioning.\n // VitalSource sets `text-align: center` for example.\n container.style.textAlign = 'left';\n\n // Use multiply blending to make text in the image more readable when\n // the corresponding text in the text layer is selected or highlighted.\n // We apply a similar effect in PDF.js.\n container.style.mixBlendMode = 'multiply';\n\n // Set a fixed font style on the container and create a canvas using the same\n // font which we can use to measure the \"natural\" size of the text. This is\n // later used when scaling the text to fit the underlying part of the image.\n const fontSize = 16;\n const fontFamily = 'sans-serif';\n container.style.fontSize = fontSize + 'px';\n container.style.fontFamily = fontFamily;\n const canvas = document.createElement('canvas');\n const context = /** @type {CanvasRenderingContext2D} */ (\n canvas.getContext('2d')\n );\n context.font = `${fontSize}px ${fontFamily}`;\n\n /**\n * Generate a CSS value that scales with the `--x-scale` or `--y-scale` CSS variables.\n *\n * @param {'x'|'y'} dimension\n * @param {number} value\n * @param {string} unit\n */\n const scaledValue = (dimension, value, unit = 'px') =>\n `calc(var(--${dimension}-scale) * ${value}${unit})`;\n\n // Group characters into words, lines and columns. Then use the result to\n // create a hierarchical DOM structure in the text layer:\n //\n // 1. Columns are positioned absolutely\n // 2. Columns stack lines vertically using a block layout\n // 3. Lines arrange words horizontally using an inline layout\n //\n // This allows the browser to select the expected text when the cursor is\n // in-between lines or words.\n const columns = analyzeLayout(charBoxes, text);\n\n for (let column of columns) {\n const columnEl = document.createElement('hypothesis-text-column');\n columnEl.style.display = 'block';\n columnEl.style.position = 'absolute';\n columnEl.style.left = scaledValue('x', column.rect.left);\n columnEl.style.top = scaledValue('y', column.rect.top);\n\n let prevLine = null;\n for (let line of column.lines) {\n const lineEl = document.createElement('hypothesis-text-line');\n lineEl.style.display = 'block';\n lineEl.style.marginLeft = scaledValue(\n 'x',\n line.rect.left - column.rect.left\n );\n lineEl.style.height = scaledValue('y', line.rect.height);\n\n if (prevLine) {\n lineEl.style.marginTop = scaledValue(\n 'y',\n line.rect.top - prevLine.rect.bottom\n );\n }\n prevLine = line;\n\n // Prevent line breaks if the word elements don't quite fit the line.\n lineEl.style.whiteSpace = 'nowrap';\n\n let prevWord = null;\n for (let word of line.words) {\n const wordEl = document.createElement('hypothesis-text-word');\n wordEl.style.display = 'inline-block';\n wordEl.style.transformOrigin = 'top left';\n wordEl.textContent = word.text;\n\n if (prevWord) {\n wordEl.style.marginLeft = scaledValue(\n 'x',\n word.rect.left - prevWord.rect.right\n );\n }\n prevWord = word;\n\n // Set the size of this box used for layout. This does not affect the\n // rendered size of the content.\n wordEl.style.width = scaledValue('x', word.rect.width);\n wordEl.style.height = scaledValue('y', word.rect.height);\n\n // Don't collapse whitespace at end of words, so it remains visible\n // in selected text. Also prevent line breaks due to overflows.\n wordEl.style.whiteSpace = 'pre';\n\n // Scale content using a transform. This affects the rendered size\n // of the text, used by text selection and\n // `Element.getBoundingClientRect`, but not layout.\n const metrics = context.measureText(word.text);\n const xScale = scaledValue('x', word.rect.width / metrics.width, '');\n const yScale = scaledValue('y', word.rect.height / fontSize, '');\n wordEl.style.transform = `scale(${xScale}, ${yScale})`;\n\n lineEl.append(wordEl);\n }\n\n columnEl.append(lineEl);\n }\n\n container.append(columnEl);\n }\n\n const updateTextLayerSize = () => {\n const { width: imageWidth, height: imageHeight } =\n image.getBoundingClientRect();\n container.style.width = imageWidth + 'px';\n container.style.height = imageHeight + 'px';\n\n container.style.setProperty('--x-scale', `${imageWidth}`);\n container.style.setProperty('--y-scale', `${imageHeight}`);\n };\n\n updateTextLayerSize();\n\n /**\n * Container element for the text layer.\n *\n * This is exposed so that callers can tweak the style if needed (eg.\n * to set a z-index value).\n */\n this.container = container;\n\n this._updateTextLayerSize = debounce(updateTextLayerSize, { maxWait: 50 });\n this._listeners = new ListenerCollection();\n\n if (typeof ResizeObserver !== 'undefined') {\n this._imageSizeObserver = new ResizeObserver(() => {\n this._updateTextLayerSize();\n });\n this._imageSizeObserver.observe(image);\n }\n\n // Fallback for browsers that don't support ResizeObserver (Safari < 13.4).\n // Due to the debouncing, we can register this listener in all browsers for\n // simplicity, without downsides.\n this._listeners.add(window, 'resize', this._updateTextLayerSize);\n }\n\n /**\n * Synchronously update the text layer to match the size and position of\n * the image.\n *\n * Normally the text layer is resized automatically but asynchronously when\n * the image size changes (eg. due to the window being resized) and updates\n * are debounced. This method can be used to force an immediate update if\n * needed.\n */\n updateSync() {\n this._updateTextLayerSize();\n this._updateTextLayerSize.flush();\n }\n\n destroy() {\n this.container.remove();\n this._listeners.removeAll();\n this._updateTextLayerSize.cancel();\n this._imageSizeObserver?.disconnect();\n }\n}\n","import { parseJsonConfig } from '../boot/parse-json-config';\nimport { generateHexString } from '../shared/random';\nimport { onNextDocumentReady, FrameObserver } from './frame-observer';\n\n/**\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Options for injecting the client into child frames.\n *\n * This includes the URL of the client's boot script, plus configuration\n * for the client when it loads in the child frame.\n *\n * @typedef {{ clientUrl: string } & Record<string, unknown>} InjectConfig\n */\n\n/**\n * HypothesisInjector injects the Hypothesis client into same-origin iframes.\n *\n * The client will be injected automatically into frames that have the\n * `enable-annotation` attribute set (see {@link FrameObserver}) and can be\n * manually injected into other frames using {@link injectClient}.\n *\n * @implements {Destroyable}\n */\nexport class HypothesisInjector {\n /**\n * @param {Element} element - root of the DOM subtree to watch for the\n * addition and removal of annotatable iframes\n * @param {InjectConfig} config\n */\n constructor(element, config) {\n this._config = config;\n this._frameObserver = new FrameObserver(\n element,\n frame => injectClient(frame, config), // Frame added callback\n () => {} // Frame removed callback\n );\n }\n\n /**\n * Disables the injection of the Hypothesis client into child iframes.\n */\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\n/**\n * Check if the client was added to a frame by {@link injectHypothesis}.\n *\n * @param {HTMLIFrameElement} iframe\n */\nfunction hasHypothesis(iframe) {\n const iframeDocument = /** @type {Document} */ (iframe.contentDocument);\n return iframeDocument.querySelector('script.js-hypothesis-config') !== null;\n}\n\n/**\n * Inject Hypothesis client into a frame.\n *\n * IMPORTANT: This method requires that the iframe is same-origin\n * (frame.contentDocument|contentWindow is not null).\n *\n * This waits for the frame to finish loading before injecting the client.\n * See {@link onDocumentReady}.\n *\n * @param {HTMLIFrameElement} frame\n * @param {InjectConfig} config -\n */\nexport async function injectClient(frame, config) {\n if (hasHypothesis(frame)) {\n return;\n }\n\n await onNextDocumentReady(frame);\n\n // Propagate the client resource locations from the current frame.\n //\n // These settings are set only in the browser extension and not by the\n // embedded client (served by h).\n //\n // We could potentially do this by allowing these settings to be part of\n // the \"annotator\" config (see `annotator/config/index.js`) which gets passed\n // to the constructor.\n const { assetRoot, notebookAppUrl, sidebarAppUrl } =\n parseJsonConfig(document);\n\n const injectedConfig = {\n ...config,\n\n assetRoot,\n notebookAppUrl,\n sidebarAppUrl,\n\n // Generate a random string to use as a frame ID. The format is not important.\n subFrameIdentifier: generateHexString(10),\n };\n\n const configElement = document.createElement('script');\n configElement.className = 'js-hypothesis-config';\n configElement.type = 'application/json';\n configElement.innerText = JSON.stringify(injectedConfig);\n\n const bootScript = document.createElement('script');\n bootScript.async = true;\n bootScript.src = config.clientUrl;\n\n const iframeDocument = /** @type {Document} */ (frame.contentDocument);\n iframeDocument.body.appendChild(configElement);\n iframeDocument.body.appendChild(bootScript);\n}\n","import { ListenerCollection } from '../../shared/listener-collection';\nimport { FeatureFlags } from '../features';\nimport { onDocumentReady } from '../frame-observer';\nimport { HTMLIntegration } from './html';\nimport { preserveScrollPosition } from './html-side-by-side';\nimport { ImageTextLayer } from './image-text-layer';\nimport { injectClient } from '../hypothesis-injector';\n\n/**\n * @typedef {import('../../types/annotator').Anchor} Anchor\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').Integration} Integration\n * @typedef {import('../../types/annotator').Selector} Selector\n * @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../hypothesis-injector').InjectConfig} InjectConfig\n */\n\n// When activating side-by-side mode for VitalSource PDF documents, make sure\n// at least this much space (in pixels) is left for the PDF document. Any\n// smaller and it feels unreadable or too-zoomed-out\nconst MIN_CONTENT_WIDTH = 480;\n\n/**\n * Return the custom DOM element that contains the book content iframe.\n */\nfunction findBookElement(document_ = document) {\n return document_.querySelector('mosaic-book');\n}\n\n/**\n * Return the role of the current frame in the VitalSource Bookshelf reader or\n * `null` if the frame is not part of Bookshelf.\n *\n * @return {'container'|'content'|null} - `container` if this is the parent of\n * the content frame, `content` if this is the frame that contains the book\n * content or `null` if the document is not part of the Bookshelf reader.\n */\nexport function vitalSourceFrameRole(window_ = window) {\n if (findBookElement(window_.document)) {\n return 'container';\n }\n\n const parentDoc = window_.frameElement?.ownerDocument;\n if (parentDoc && findBookElement(parentDoc)) {\n return 'content';\n }\n\n return null;\n}\n\n/**\n * VitalSourceInjector runs in the book container frame and loads the client into\n * book content frames.\n *\n * The frame structure of the VitalSource book reader looks like this:\n *\n * [VitalSource top frame - bookshelf.vitalsource.com]\n * |\n * [Book container frame - jigsaw.vitalsource.com]\n * |\n * [Book content frame - jigsaw.vitalsource.com]\n *\n * The Hypothesis client can be initially loaded in the container frame or the\n * content frame. As the user navigates around the book, the container frame\n * remains the same but the content frame is swapped out. When used in the\n * container frame, this class handles initial injection of the client as a\n * guest in the current content frame, and re-injecting the client into new\n * content frames when they are created.\n */\nexport class VitalSourceInjector {\n /**\n * @param {InjectConfig} config - Configuration for injecting the client into\n * book content frames\n */\n constructor(config) {\n const bookElement = findBookElement();\n if (!bookElement) {\n throw new Error('Book container element not found');\n }\n\n /** @type {WeakSet<HTMLIFrameElement>} */\n const contentFrames = new WeakSet();\n\n const shadowRoot = /** @type {ShadowRoot} */ (bookElement.shadowRoot);\n const injectClientIntoContentFrame = () => {\n const frame = shadowRoot.querySelector('iframe');\n if (!frame || contentFrames.has(frame)) {\n // Either there is no content frame or we are already watching it.\n return;\n }\n contentFrames.add(frame);\n onDocumentReady(frame, (err, document_) => {\n const body = document_?.body;\n const isBookContent =\n body &&\n // Check that this is not the temporary page containing encrypted and\n // invisible book content, which is replaced with the real content after\n // a form submission. These pages look something like:\n //\n // ```\n // <html>\n // <title>content</title>\n // <body><div id=\"page-content\">{ Base64 encoded data }</div></body>\n // </html>\n // ```\n !body.querySelector('#page-content');\n\n if (isBookContent) {\n injectClient(frame, config);\n }\n });\n };\n\n injectClientIntoContentFrame();\n\n // Re-inject client into content frame after a chapter navigation.\n this._frameObserver = new MutationObserver(injectClientIntoContentFrame);\n this._frameObserver.observe(shadowRoot, { childList: true, subtree: true });\n }\n\n destroy() {\n this._frameObserver.disconnect();\n }\n}\n\n/**\n * Bounding box of a single character in the page.\n *\n * Coordinates are expressed in percentage distance from the top-left corner\n * of the rendered page.\n *\n * @typedef GlyphBox\n * @prop {number} l\n * @prop {number} r\n * @prop {number} t\n * @prop {number} b\n */\n\n/**\n * @typedef PDFGlyphData\n * @prop {GlyphBox[]} glyphs\n */\n\n/**\n * Data that the VitalSource book reader renders into the page about the\n * content and location of text in the image.\n *\n * @typedef PDFTextData\n * @prop {PDFGlyphData} glyphs - Locations of each text character in the page\n * @prop {string} words - The text in the page\n */\n\nfunction getPDFPageImage() {\n return /** @type {HTMLImageElement|null} */ (\n document.querySelector('img#pbk-page')\n );\n}\n\n/**\n * Fix how a VitalSource book content frame scrolls, so that various related\n * Hypothesis behaviors (the bucket bar, scrolling annotations into view) work\n * as intended.\n *\n * Some VitalSource books (PDFs) make content scrolling work by making the\n * content iframe really tall and having the parent frame scroll. This stops the\n * Hypothesis bucket bar and scrolling annotations into view from working.\n *\n * @param {HTMLIFrameElement} frame\n */\nfunction makeContentFrameScrollable(frame) {\n if (frame.getAttribute('scrolling') !== 'no') {\n // This is a book (eg. EPUB) where the workaround is not required.\n return;\n }\n\n // Override inline styles of iframe (hence `!important`). The iframe lives\n // in Shadow DOM, so the element styles won't affect the rest of the app.\n const style = document.createElement('style');\n style.textContent = `iframe { height: 100% !important; }`;\n frame.insertAdjacentElement('beforebegin', style);\n\n const removeScrollingAttr = () => frame.removeAttribute('scrolling');\n removeScrollingAttr();\n\n // Sometimes the attribute gets re-added by VS. Remove it if that\n // happens.\n const attrObserver = new MutationObserver(removeScrollingAttr);\n attrObserver.observe(frame, { attributes: true });\n}\n\n/**\n * Integration for the content frame in VitalSource's Bookshelf ebook reader.\n *\n * This integration delegates to the standard HTML integration for most\n * functionality, but it adds logic to:\n *\n * - Customize the document URI and metadata that is associated with annotations\n * - Prevent VitalSource's built-in selection menu from getting in the way\n * of the adder.\n * - Create a hidden text layer in PDF-based books, so the user can select text\n * in the PDF image. This is similar to what PDF.js does for us in PDFs.\n *\n * @implements {Integration}\n */\nexport class VitalSourceContentIntegration {\n /**\n * @param {HTMLElement} container\n */\n constructor(container = document.body) {\n const features = new FeatureFlags();\n\n // Forcibly enable the side-by-side feature for VS books. This feature is\n // only behind a flag for regular web pages, which are typically more\n // complex and varied than EPUB books.\n features.update({ html_side_by_side: true });\n\n this._htmlIntegration = new HTMLIntegration({ container, features });\n\n this._listeners = new ListenerCollection();\n\n // Prevent mouse events from reaching the window. This prevents VitalSource\n // from showing its native selection menu, which obscures the client's\n // annotation toolbar.\n //\n // To avoid interfering with the client's own selection handling, this\n // event blocking must happen at the same level or higher in the DOM tree\n // than where SelectionObserver listens.\n const stopEvents = ['mouseup', 'mousedown', 'mouseout'];\n for (let event of stopEvents) {\n this._listeners.add(document.documentElement, event, e => {\n e.stopPropagation();\n });\n }\n\n // Install scrolling workaround for PDFs. We do this in the content frame\n // so that it works whether Hypothesis is loaded directly into the content\n // frame or injected by VitalSourceInjector from the parent frame.\n const frame = /** @type {HTMLIFrameElement|null} */ (window.frameElement);\n if (frame) {\n makeContentFrameScrollable(frame);\n }\n\n // If this is a PDF, create the hidden text layer above the rendered PDF\n // image.\n const bookImage = getPDFPageImage();\n\n /** @type {PDFTextData|undefined} */\n const pageData = /** @type {any} */ (window).innerPageData;\n\n if (bookImage && pageData) {\n const charRects = pageData.glyphs.glyphs.map(glyph => {\n const left = glyph.l / 100;\n const right = glyph.r / 100;\n const top = glyph.t / 100;\n const bottom = glyph.b / 100;\n return new DOMRect(left, top, right - left, bottom - top);\n });\n\n this._textLayer = new ImageTextLayer(\n bookImage,\n charRects,\n pageData.words\n );\n\n // VitalSource has several DOM elements in the page which are raised\n // above the image using z-index. One of these is used to handle VS's\n // own text selection functionality.\n //\n // Set a z-index on our text layer to raise it above VS's own one.\n this._textLayer.container.style.zIndex = '100';\n }\n }\n\n canAnnotate() {\n return true;\n }\n\n destroy() {\n this._textLayer?.destroy();\n this._listeners.removeAll();\n this._htmlIntegration.destroy();\n }\n\n /**\n * @param {HTMLElement} root\n * @param {Selector[]} selectors\n */\n anchor(root, selectors) {\n return this._htmlIntegration.anchor(root, selectors);\n }\n\n /**\n * @param {HTMLElement} root\n * @param {Range} range\n */\n describe(root, range) {\n return this._htmlIntegration.describe(root, range);\n }\n\n contentContainer() {\n return this._htmlIntegration.contentContainer();\n }\n\n /**\n * @param {SidebarLayout} layout\n */\n fitSideBySide(layout) {\n // For PDF books, handle side-by-side mode in this integration. For EPUBs,\n // delegate to the HTML integration.\n const bookImage = getPDFPageImage();\n if (bookImage && this._textLayer) {\n const bookContainer = /** @type {HTMLElement} */ (\n bookImage.parentElement\n );\n const textLayer = this._textLayer;\n\n // Update the PDF image size and alignment to fit alongside the sidebar.\n // `ImageTextLayer` will handle adjusting the text layer to match.\n const newWidth = window.innerWidth - layout.width;\n\n preserveScrollPosition(() => {\n if (layout.expanded && newWidth > MIN_CONTENT_WIDTH) {\n // The VS book viewer sets `text-align: center` on the <body> element\n // by default, which centers the book image in the page. When the sidebar\n // is open we need the image to be left-aligned.\n bookContainer.style.textAlign = 'left';\n bookImage.style.width = `${newWidth}px`;\n } else {\n bookContainer.style.textAlign = '';\n bookImage.style.width = '';\n }\n\n // Update text layer to match new image dimensions immediately. This\n // is needed so that `preserveScrollPosition` can see how the content\n // has shifted when this callback returns.\n textLayer.updateSync();\n });\n\n return layout.expanded;\n } else {\n return this._htmlIntegration.fitSideBySide(layout);\n }\n }\n\n async getMetadata() {\n // Return minimal metadata which includes only the information we really\n // want to include.\n return {\n title: document.title,\n link: [],\n };\n }\n\n async uri() {\n // An example of a typical URL for the chapter content in the Bookshelf reader is:\n //\n // https://jigsaw.vitalsource.com/books/9781848317703/epub/OPS/xhtml/chapter_001.html#cfi=/6/10%5B;vnd.vst.idref=chap001%5D!/4\n //\n // Where \"9781848317703\" is the VitalSource book ID (\"vbid\"), \"chapter_001.html\"\n // is the location of the HTML page for the current chapter within the book\n // and the `#cfi` fragment identifies the scroll location.\n //\n // Note that this URL is typically different than what is displayed in the\n // iframe's `src` attribute.\n\n // Strip off search parameters and fragments.\n const uri = new URL(document.location.href);\n uri.search = '';\n return uri.toString();\n }\n\n /**\n * @param {Anchor} anchor\n */\n async scrollToAnchor(anchor) {\n return this._htmlIntegration.scrollToAnchor(anchor);\n }\n}\n","import { HTMLIntegration } from './html';\nimport { PDFIntegration, isPDF } from './pdf';\nimport {\n VitalSourceContentIntegration,\n vitalSourceFrameRole,\n} from './vitalsource';\n\n/**\n * @typedef {import('../../types/annotator').Annotator} Annotator\n * @typedef {import('../../types/annotator').ContentPartner} ContentPartner\n * @typedef {import('../../types/annotator').Integration} Integration\n */\n\n/**\n * Create the integration that handles document-type specific aspects of\n * guest functionality.\n *\n * @param {Annotator} annotator\n * @param {object} options\n * @param {ContentPartner} [options.contentPartner] - Content partner banner to show,\n * if supported by the integration.\n * @return {Integration}\n */\nexport function createIntegration(annotator, { contentPartner } = {}) {\n if (isPDF()) {\n return new PDFIntegration(annotator, { contentPartner });\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'content') {\n return new VitalSourceContentIntegration();\n }\n\n return new HTMLIntegration({ features: annotator.features });\n}\n","import { ListenerCollection } from '../shared/listener-collection';\n\n/**\n * Return the current selection or `null` if there is no selection or it is empty.\n *\n * @param {Document} document\n * @return {Range|null}\n */\nexport function selectedRange(document) {\n const selection = document.getSelection();\n if (!selection || selection.rangeCount === 0) {\n return null;\n }\n const range = selection.getRangeAt(0);\n if (range.collapsed) {\n return null;\n }\n return range;\n}\n\n/**\n * An observer that watches for and buffers changes to the document's current selection.\n */\nexport class SelectionObserver {\n /**\n * Start observing changes to the current selection in the document.\n *\n * @param {(range: Range|null) => void} callback -\n * Callback invoked with the selected region of the document when it has\n * changed.\n * @param {Document} document_ - Test seam\n */\n constructor(callback, document_ = document) {\n let isMouseDown = false;\n\n this._pendingCallback = null;\n\n const scheduleCallback = (delay = 10) => {\n this._pendingCallback = setTimeout(() => {\n callback(selectedRange(document_));\n }, delay);\n };\n\n /** @param {Event} event */\n const eventHandler = event => {\n if (event.type === 'mousedown') {\n isMouseDown = true;\n }\n if (event.type === 'mouseup') {\n isMouseDown = false;\n }\n\n // If the user makes a selection with the mouse, wait until they release\n // it before reporting a selection change.\n if (isMouseDown) {\n return;\n }\n\n this._cancelPendingCallback();\n\n // Schedule a notification after a short delay. The delay serves two\n // purposes:\n //\n // - If this handler was called as a result of a 'mouseup' event then the\n // selection will not be updated until the next tick of the event loop.\n // In this case we only need a short delay.\n //\n // - If the user is changing the selection with a non-mouse input (eg.\n // keyboard or selection handles on mobile) this buffers updates and\n // makes sure that we only report one when the update has stopped\n // changing. In this case we want a longer delay.\n\n const delay = event.type === 'mouseup' ? 10 : 100;\n scheduleCallback(delay);\n };\n\n this._document = document_;\n this._listeners = new ListenerCollection();\n\n this._listeners.add(document_, 'selectionchange', eventHandler);\n\n // Mouse events are handled on the body because propagation may be stopped\n // before they reach the document in some environments (eg. VitalSource).\n this._listeners.add(document_.body, 'mousedown', eventHandler);\n this._listeners.add(document_.body, 'mouseup', eventHandler);\n\n // Report the initial selection.\n scheduleCallback(1);\n }\n\n disconnect() {\n this._listeners.removeAll();\n this._cancelPendingCallback();\n }\n\n _cancelPendingCallback() {\n if (this._pendingCallback) {\n clearTimeout(this._pendingCallback);\n this._pendingCallback = null;\n }\n }\n}\n","import { ListenerCollection } from '../shared/listener-collection';\nimport { PortFinder, PortRPC } from '../shared/messaging';\nimport { generateHexString } from '../shared/random';\n\nimport { Adder } from './adder';\nimport { TextRange } from './anchoring/text-range';\nimport { BucketBarClient } from './bucket-bar-client';\nimport { FeatureFlags } from './features';\nimport {\n getHighlightsContainingNode,\n highlightRange,\n removeAllHighlights,\n removeHighlights,\n setHighlightsFocused,\n setHighlightsVisible,\n} from './highlighter';\nimport { createIntegration } from './integrations';\nimport * as rangeUtil from './range-util';\nimport { SelectionObserver, selectedRange } from './selection-observer';\nimport { findClosestOffscreenAnchor } from './util/buckets';\nimport { frameFillsAncestor } from './util/frame';\nimport { normalizeURI } from './util/url';\n\n/**\n * @typedef {import('../types/annotator').AnnotationData} AnnotationData\n * @typedef {import('../types/annotator').Annotator} Annotator\n * @typedef {import('../types/annotator').Anchor} Anchor\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../types/api').Target} Target\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n * @typedef {import('../types/port-rpc-events').GuestToSidebarEvent} GuestToSidebarEvent\n * @typedef {import('../types/port-rpc-events').SidebarToGuestEvent} SidebarToGuestEvent\n */\n\n/**\n * HTML element created by the highlighter with an associated annotation.\n *\n * @typedef {HTMLElement & { _annotation?: AnnotationData }} AnnotationHighlight\n */\n\n/**\n * Return all the annotations tags associated with the selected text.\n *\n * @return {string[]}\n */\nfunction annotationsForSelection() {\n const selection = /** @type {Selection} */ (window.getSelection());\n const range = selection.getRangeAt(0);\n const tags = rangeUtil.itemsForRange(\n range,\n node => /** @type {AnnotationHighlight} */ (node)._annotation?.$tag\n );\n return tags;\n}\n\n/**\n * Return the annotation tags associated with any highlights that contain a given\n * DOM node.\n *\n * @param {Node} node\n * @return {string[]}\n */\nfunction annotationsAt(node) {\n const items = getHighlightsContainingNode(node)\n .map(h => /** @type {AnnotationHighlight} */ (h)._annotation)\n .filter(ann => ann !== undefined)\n .map(ann => ann?.$tag);\n return /** @type {string[]} */ (items);\n}\n\n/**\n * Resolve an anchor's associated document region to a concrete `Range`.\n *\n * This may fail if anchoring failed or if the document has been mutated since\n * the anchor was created in a way that invalidates the anchor.\n *\n * @param {Anchor} anchor\n * @return {Range|null}\n */\nfunction resolveAnchor(anchor) {\n if (!anchor.range) {\n return null;\n }\n try {\n return anchor.range.toRange();\n } catch {\n return null;\n }\n}\n\nfunction removeTextSelection() {\n document.getSelection()?.removeAllRanges();\n}\n\n/**\n * Subset of the Hypothesis client configuration that is used by {@link Guest}.\n *\n * @typedef GuestConfig\n * @prop {string} [subFrameIdentifier] - An identifier used by this guest to\n * identify the current frame when communicating with the sidebar. This is\n * only set in non-host frames.\n * @prop {'jstor'} [contentPartner] - Configures a banner or other indicators\n * showing where the content has come from.\n */\n\n/**\n * `Guest` is the central class of the annotator that handles anchoring (locating)\n * annotations in the document when they are fetched by the sidebar, rendering\n * highlights for them and handling subsequent interactions with the highlights.\n *\n * It is also responsible for listening to changes in the current selection\n * and triggering the display of controls to create new annotations. When one\n * of these controls is clicked, it creates the new annotation and sends it to\n * the sidebar.\n *\n * Within a browser tab, there is typically one `Guest` instance per frame that\n * loads Hypothesis (not all frames will be annotation-enabled). In one frame,\n * usually the top-level one, there will also be an instance of the `Sidebar`\n * class that shows the sidebar app and surrounding UI. The `Guest` instance in\n * each frame connects to the sidebar and host frames as part of its\n * initialization.\n *\n * @implements {Annotator}\n * @implements {Destroyable}\n */\nexport class Guest {\n /**\n * @param {HTMLElement} element -\n * The root element in which the `Guest` instance should be able to anchor\n * or create annotations. In an ordinary web page this typically `document.body`.\n * @param {GuestConfig} [config]\n * @param {Window} [hostFrame] -\n * Host frame which this guest is associated with. This is expected to be\n * an ancestor of the guest frame. It may be same or cross origin.\n */\n constructor(element, config = {}, hostFrame = window) {\n this.element = element;\n this._hostFrame = hostFrame;\n this._highlightsVisible = false;\n this._isAdderVisible = false;\n this._informHostOnNextSelectionClear = true;\n /** @type {Range[]} - Ranges of the current text selection. */\n this.selectedRanges = [];\n\n this._adder = new Adder(this.element, {\n onAnnotate: () => this.createAnnotation(),\n onHighlight: () => this.createAnnotation({ highlight: true }),\n onShowAnnotations: tags => this.selectAnnotations(tags),\n });\n\n this._selectionObserver = new SelectionObserver(range => {\n if (range) {\n this._onSelection(range);\n } else {\n this._onClearSelection();\n }\n });\n\n /**\n * The anchors generated by resolving annotation selectors to locations in the\n * document. These are added by `anchor` and removed by `detach`.\n *\n * There is one anchor per annotation `Target`, which typically means one\n * anchor per annotation.\n *\n * @type {Anchor[]}\n */\n this.anchors = [];\n\n /**\n * Tags of annotations that are currently anchored or being anchored in\n * the guest.\n */\n this._annotations = /** @type {Set<string>} */ (new Set());\n\n // Set the frame identifier if it's available.\n // The \"top\" guest instance will have this as null since it's in a top frame not a sub frame\n /** @type {string|null} */\n this._frameIdentifier = config.subFrameIdentifier || null;\n\n this._portFinder = new PortFinder({\n hostFrame: this._hostFrame,\n source: 'guest',\n });\n\n this.features = new FeatureFlags();\n\n /**\n * Integration that handles document-type specific functionality in the\n * guest.\n */\n this._integration = createIntegration(this, {\n contentPartner: config.contentPartner,\n });\n\n /**\n * Channel for host-guest communication.\n *\n * @type {PortRPC<HostToGuestEvent, GuestToHostEvent>}\n */\n this._hostRPC = new PortRPC();\n this._connectHost(hostFrame);\n\n /**\n * Channel for guest-sidebar communication.\n *\n * @type {PortRPC<SidebarToGuestEvent, GuestToSidebarEvent>}\n */\n this._sidebarRPC = new PortRPC();\n this._connectSidebar();\n\n this._bucketBarClient = new BucketBarClient({\n contentContainer: this._integration.contentContainer(),\n hostRPC: this._hostRPC,\n });\n\n this._sideBySideActive = false;\n\n // Setup event handlers on the root element\n this._listeners = new ListenerCollection();\n this._setupElementEvents();\n\n /**\n * Tags of currently focused annotations. This is used to set the focused\n * state correctly for new highlights if the associated annotation is already\n * focused in the sidebar.\n *\n * @type {Set<string>}\n */\n this._focusedAnnotations = new Set();\n }\n\n // Add DOM event listeners for clicks, taps etc. on the document and\n // highlights.\n _setupElementEvents() {\n // Hide the sidebar in response to a document click or tap, so it doesn't obscure\n // the document content.\n /** @param {Element} element */\n const maybeCloseSidebar = element => {\n if (this._sideBySideActive) {\n // Don't hide the sidebar if event was disabled because the sidebar\n // doesn't overlap the content.\n return;\n }\n if (annotationsAt(element).length) {\n // Don't hide the sidebar if the event comes from an element that contains a highlight\n return;\n }\n this._sidebarRPC.call('closeSidebar');\n };\n\n this._listeners.add(this.element, 'mouseup', event => {\n const { target, metaKey, ctrlKey } = event;\n const tags = annotationsAt(/** @type {Element} */ (target));\n if (tags.length && this._highlightsVisible) {\n const toggle = metaKey || ctrlKey;\n this.selectAnnotations(tags, toggle);\n }\n });\n\n this._listeners.add(this.element, 'mousedown', ({ target }) => {\n maybeCloseSidebar(/** @type {Element} */ (target));\n });\n\n // Allow taps on the document to hide the sidebar as well as clicks.\n // On iOS < 13 (2019), elements like h2 or div don't emit 'click' events.\n this._listeners.add(this.element, 'touchstart', ({ target }) => {\n maybeCloseSidebar(/** @type {Element} */ (target));\n });\n\n this._listeners.add(this.element, 'mouseover', ({ target }) => {\n const tags = annotationsAt(/** @type {Element} */ (target));\n if (tags.length && this._highlightsVisible) {\n this._sidebarRPC.call('focusAnnotations', tags);\n }\n });\n\n this._listeners.add(this.element, 'mouseout', () => {\n if (this._highlightsVisible) {\n this._sidebarRPC.call('focusAnnotations', []);\n }\n });\n\n this._listeners.add(window, 'resize', () => this._repositionAdder());\n }\n\n /**\n * Retrieve metadata for the current document.\n */\n async getDocumentInfo() {\n const [uri, metadata] = await Promise.all([\n this._integration.uri(),\n this._integration.getMetadata(),\n ]);\n\n return {\n uri: normalizeURI(uri),\n metadata,\n frameIdentifier: this._frameIdentifier,\n };\n }\n\n /**\n * Shift the position of the adder on window 'resize' events\n */\n _repositionAdder() {\n if (this._isAdderVisible === false) {\n return;\n }\n const range = window.getSelection()?.getRangeAt(0);\n if (range) {\n this._onSelection(range);\n }\n }\n\n /** @param {Window} hostFrame */\n async _connectHost(hostFrame) {\n this._hostRPC.on('clearSelection', () => {\n if (selectedRange(document)) {\n this._informHostOnNextSelectionClear = false;\n removeTextSelection();\n }\n });\n\n this._hostRPC.on('createAnnotation', () => this.createAnnotation());\n\n this._hostRPC.on(\n 'focusAnnotations',\n /** @param {string[]} tags */\n tags => this._focusAnnotations(tags)\n );\n\n this._hostRPC.on(\n 'scrollToClosestOffScreenAnchor',\n /**\n * @param {string[]} tags\n * @param {'down'|'up'} direction\n */\n (tags, direction) => this._scrollToClosestOffScreenAnchor(tags, direction)\n );\n\n this._hostRPC.on(\n 'selectAnnotations',\n /**\n * @param {string[]} tags\n * @param {boolean} toggle\n */\n (tags, toggle) => this.selectAnnotations(tags, toggle)\n );\n\n this._hostRPC.on(\n 'sidebarLayoutChanged',\n /** @param {SidebarLayout} sidebarLayout */\n sidebarLayout => {\n if (frameFillsAncestor(window, hostFrame)) {\n this.fitSideBySide(sidebarLayout);\n }\n }\n );\n\n // Discover and connect to the host frame. All RPC events must be\n // registered before creating the channel.\n const hostPort = await this._portFinder.discover('host');\n this._hostRPC.connect(hostPort);\n }\n\n async _connectSidebar() {\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n /** @param {Record<string, boolean>} flags */ flags =>\n this.features.update(flags)\n );\n\n // Handlers for events sent when user hovers or clicks on an annotation card\n // in the sidebar.\n this._sidebarRPC.on(\n 'focusAnnotations',\n /** @param {string[]} tags */\n tags => this._focusAnnotations(tags)\n );\n\n this._sidebarRPC.on(\n 'scrollToAnnotation',\n /** @param {string} tag */\n tag => {\n const anchor = this.anchors.find(a => a.annotation.$tag === tag);\n if (!anchor?.highlights) {\n return;\n }\n const range = resolveAnchor(anchor);\n if (!range) {\n return;\n }\n\n // Emit a custom event that the host page can respond to. This is useful,\n // for example, if the highlighted content is contained in a collapsible\n // section of the page that needs to be un-collapsed.\n const event = new CustomEvent('scrolltorange', {\n bubbles: true,\n cancelable: true,\n detail: range,\n });\n const defaultNotPrevented = this.element.dispatchEvent(event);\n\n if (defaultNotPrevented) {\n this._integration.scrollToAnchor(anchor);\n }\n }\n );\n\n // Handler for controls on the sidebar\n this._sidebarRPC.on(\n 'setHighlightsVisible',\n /** @param {boolean} showHighlights */ showHighlights => {\n this.setHighlightsVisible(showHighlights);\n }\n );\n\n this._sidebarRPC.on(\n 'deleteAnnotation',\n /** @param {string} tag */\n tag => this.detach(tag)\n );\n\n this._sidebarRPC.on(\n 'loadAnnotations',\n /** @param {AnnotationData[]} annotations */\n annotations => annotations.forEach(annotation => this.anchor(annotation))\n );\n\n // Connect to sidebar and send document info/URIs to it.\n //\n // RPC calls are deferred until a connection is made, so these steps can\n // complete in either order.\n this._portFinder.discover('sidebar').then(port => {\n this._sidebarRPC.connect(port);\n });\n this.getDocumentInfo().then(metadata =>\n this._sidebarRPC.call('documentInfoChanged', metadata)\n );\n }\n\n destroy() {\n this._portFinder.destroy();\n this._hostRPC.destroy();\n this._sidebarRPC.destroy();\n\n this._listeners.removeAll();\n\n this._selectionObserver.disconnect();\n this._adder.destroy();\n this._bucketBarClient.destroy();\n\n removeAllHighlights(this.element);\n\n this._integration.destroy();\n }\n\n /**\n * Anchor an annotation's selectors in the document.\n *\n * _Anchoring_ resolves a set of selectors to a concrete region of the document\n * which is then highlighted.\n *\n * Any existing anchors associated with `annotation` will be removed before\n * re-anchoring the annotation.\n *\n * @param {AnnotationData} annotation\n * @return {Promise<Anchor[]>}\n */\n async anchor(annotation) {\n /**\n * Resolve an annotation's selectors to a concrete range.\n *\n * @param {Target} target\n * @return {Promise<Anchor>}\n */\n const locate = async target => {\n // Only annotations with an associated quote can currently be anchored.\n // This is because the quote is used to verify anchoring with other selector\n // types.\n if (\n !target.selector ||\n !target.selector.some(s => s.type === 'TextQuoteSelector')\n ) {\n return { annotation, target };\n }\n\n /** @type {Anchor} */\n let anchor;\n try {\n const range = await this._integration.anchor(\n this.element,\n target.selector\n );\n // Convert the `Range` to a `TextRange` which can be converted back to\n // a `Range` later. The `TextRange` representation allows for highlights\n // to be inserted during anchoring other annotations without \"breaking\"\n // this anchor.\n const textRange = TextRange.fromRange(range);\n anchor = { annotation, target, range: textRange };\n } catch (err) {\n anchor = { annotation, target };\n }\n return anchor;\n };\n\n /**\n * Highlight the text range that `anchor` refers to.\n *\n * @param {Anchor} anchor\n */\n const highlight = anchor => {\n const range = resolveAnchor(anchor);\n if (!range) {\n return;\n }\n\n const highlights = /** @type {AnnotationHighlight[]} */ (\n highlightRange(range)\n );\n highlights.forEach(h => {\n h._annotation = anchor.annotation;\n });\n anchor.highlights = highlights;\n\n if (this._focusedAnnotations.has(anchor.annotation.$tag)) {\n setHighlightsFocused(highlights, true);\n }\n };\n\n // Remove existing anchors for this annotation.\n this.detach(annotation.$tag, false /* notify */);\n\n this._annotations.add(annotation.$tag);\n\n // Resolve selectors to ranges and insert highlights.\n if (!annotation.target) {\n annotation.target = [];\n }\n const anchors = await Promise.all(annotation.target.map(locate));\n\n // If the annotation was removed while anchoring, don't save the anchors.\n if (!this._annotations.has(annotation.$tag)) {\n return [];\n }\n\n for (let anchor of anchors) {\n highlight(anchor);\n }\n\n // Set flag indicating whether anchoring succeeded. For each target,\n // anchoring is successful either if there are no selectors (ie. this is a\n // Page Note) or we successfully resolved the selectors to a range.\n annotation.$orphan =\n anchors.length > 0 &&\n anchors.every(anchor => anchor.target.selector && !anchor.range);\n\n this._updateAnchors(this.anchors.concat(anchors), true /* notify */);\n\n // Let other frames (eg. the sidebar) know about the new annotation.\n this._sidebarRPC.call('syncAnchoringStatus', annotation);\n\n return anchors;\n }\n\n /**\n * Remove the anchors and associated highlights for an annotation from the document.\n *\n * @param {string} tag\n * @param {boolean} [notify] - For internal use. Whether to inform the host\n * frame about the removal of an anchor.\n */\n detach(tag, notify = true) {\n this._annotations.delete(tag);\n\n /** @type {Anchor[]} */\n const anchors = [];\n for (let anchor of this.anchors) {\n if (anchor.annotation.$tag !== tag) {\n anchors.push(anchor);\n } else if (anchor.highlights) {\n removeHighlights(anchor.highlights);\n }\n }\n this._updateAnchors(anchors, notify);\n }\n\n /**\n * @param {Anchor[]} anchors\n * @param {boolean} notify\n */\n _updateAnchors(anchors, notify) {\n this.anchors = anchors;\n if (notify) {\n this._bucketBarClient.update(this.anchors);\n }\n }\n\n /**\n * Create a new annotation that is associated with the selected region of\n * the current document.\n *\n * @param {object} options\n * @param {boolean} [options.highlight] - If true, the new annotation has\n * the `$highlight` flag set, causing it to be saved immediately without\n * prompting for a comment.\n * @return {Promise<AnnotationData>} - The new annotation\n */\n async createAnnotation({ highlight = false } = {}) {\n const ranges = this.selectedRanges;\n this.selectedRanges = [];\n\n const info = await this.getDocumentInfo();\n const root = this.element;\n const rangeSelectors = await Promise.all(\n ranges.map(range => this._integration.describe(root, range))\n );\n const target = rangeSelectors.map(selectors => ({\n source: info.uri,\n\n // In the Hypothesis API the field containing the selectors is called\n // `selector`, despite being a list.\n selector: selectors,\n }));\n\n /** @type {AnnotationData} */\n const annotation = {\n uri: info.uri,\n document: info.metadata,\n target,\n $highlight: highlight,\n $tag: 'a:' + generateHexString(8),\n };\n\n this._sidebarRPC.call('createAnnotation', annotation);\n this.anchor(annotation);\n\n // Removing the text selection triggers the `SelectionObserver` callback,\n // which causes the adder to be removed after some delay.\n removeTextSelection();\n\n return annotation;\n }\n\n /**\n * Indicate in the sidebar that certain annotations are focused (ie. the\n * associated document region(s) is hovered).\n *\n * @param {string[]} tags\n */\n _focusAnnotations(tags) {\n this._focusedAnnotations.clear();\n tags.forEach(tag => this._focusedAnnotations.add(tag));\n\n for (let anchor of this.anchors) {\n if (anchor.highlights) {\n const toggle = tags.includes(anchor.annotation.$tag);\n setHighlightsFocused(anchor.highlights, toggle);\n }\n }\n\n this._sidebarRPC.call('focusAnnotations', tags);\n }\n\n /**\n * Scroll to the closest off screen anchor.\n *\n * @param {string[]} tags\n * @param {'down'|'up'} direction\n */\n _scrollToClosestOffScreenAnchor(tags, direction) {\n const anchors = this.anchors.filter(({ annotation }) =>\n tags.includes(annotation.$tag)\n );\n const closest = findClosestOffscreenAnchor(anchors, direction);\n if (closest) {\n this._integration.scrollToAnchor(closest);\n }\n }\n\n /**\n * Show or hide the adder toolbar when the selection changes.\n *\n * @param {Range} range\n */\n _onSelection(range) {\n if (!this._integration.canAnnotate(range)) {\n this._onClearSelection();\n return;\n }\n\n const selection = /** @type {Selection} */ (document.getSelection());\n const isBackwards = rangeUtil.isSelectionBackwards(selection);\n const focusRect = rangeUtil.selectionFocusRect(selection);\n if (!focusRect) {\n // The selected range does not contain any text\n this._onClearSelection();\n return;\n }\n\n this.selectedRanges = [range];\n this._hostRPC.call('textSelected');\n\n this._adder.annotationsForSelection = annotationsForSelection();\n this._isAdderVisible = true;\n this._adder.show(focusRect, isBackwards);\n }\n\n _onClearSelection() {\n this._isAdderVisible = false;\n this._adder.hide();\n this.selectedRanges = [];\n if (this._informHostOnNextSelectionClear) {\n this._hostRPC.call('textUnselected');\n }\n this._informHostOnNextSelectionClear = true;\n }\n\n /**\n * Show the given annotations in the sidebar.\n *\n * This sets up a filter in the sidebar to show only the selected annotations\n * and opens the sidebar.\n *\n * @param {string[]} tags\n * @param {boolean} [toggle] - Toggle whether the annotations are selected\n * instead of showing them regardless of whether they are currently selected.\n */\n selectAnnotations(tags, toggle = false) {\n if (toggle) {\n this._sidebarRPC.call('toggleAnnotationSelection', tags);\n } else {\n this._sidebarRPC.call('showAnnotations', tags);\n }\n this._sidebarRPC.call('openSidebar');\n }\n\n /**\n * Set whether highlights are visible in the document or not.\n *\n * @param {boolean} visible\n */\n setHighlightsVisible(visible) {\n setHighlightsVisible(this.element, visible);\n this._highlightsVisible = visible;\n }\n\n /**\n * Attempt to fit the document content alongside the sidebar.\n *\n * @param {SidebarLayout} sidebarLayout\n */\n fitSideBySide(sidebarLayout) {\n this._sideBySideActive = this._integration.fitSideBySide(sidebarLayout);\n }\n\n /**\n * Return true if side-by-side mode is currently active.\n *\n * Side-by-side mode is activated or de-activated when `fitSideBySide` is called\n * depending on whether the sidebar is expanded and whether there is room for\n * the content alongside the sidebar.\n */\n get sideBySideActive() {\n return this._sideBySideActive;\n }\n\n /**\n * Return the tags of annotations that are currently displayed in a focused\n * state.\n *\n * @return {Set<string>}\n */\n get focusedAnnotationTags() {\n return this._focusedAnnotations;\n }\n}\n","/**\n * Test whether an iframe fills the viewport of an ancestor frame.\n *\n * @param {Window} frame\n * @param {Window} ancestor\n */\nexport function frameFillsAncestor(frame, ancestor) {\n if (frame === ancestor) {\n return true;\n }\n\n if (frame.parent !== ancestor) {\n // To keep things simple, we initially only support direct ancestors.\n return false;\n }\n\n if (!frame.frameElement) {\n // This is a cross-origin iframe. In this case we can't tell if it fills\n // the parent frame or not.\n return false;\n }\n\n const frameBox = frame.frameElement.getBoundingClientRect();\n\n // Threshold for deciding when a frame occupies enough of its parent's width\n // to count as filling the viewport.\n const fullWidthThreshold = 0.8;\n\n return frameBox.width / frame.parent.innerWidth >= fullWidthThreshold;\n}\n","/**\n * Encode app configuration in a URL fragment.\n *\n * This is used by the annotator to pass configuration to the sidebar and\n * notebook apps, which they can easily read on startup. The configuration is\n * passed in the fragment to avoid invalidating cache entries for the URL\n * or adding noise to server logs.\n *\n * @param {string} baseURL\n * @param {object} config\n * @return {string} URL with added fragment\n */\nexport function addConfigFragment(baseURL, config) {\n const url = new URL(baseURL);\n const params = new URLSearchParams();\n params.append('config', JSON.stringify(config));\n url.hash = params.toString();\n return url.toString();\n}\n\n/**\n * Parse configuration from a URL generated by {@link addConfigFragment}.\n *\n * @param {string} url\n * @return {Record<string, unknown>}\n */\nexport function parseConfigFragment(url) {\n const configStr = new URL(url).hash.slice(1);\n const configJSON = new URLSearchParams(configStr).get('config');\n return JSON.parse(configJSON || '{}');\n}\n","/**\n * Create the JSON-serializable subset of annotator configuration that should\n * be passed to the sidebar or notebook applications.\n *\n * @param {string} appURL - URL from which the application will be served\n * @param {Record<string, unknown>} config\n * @return {Record<string, unknown>}\n */\nexport function createAppConfig(appURL, config) {\n /** @type {Record<string, unknown>} */\n const appConfig = {};\n\n for (let [key, value] of Object.entries(config)) {\n // Remove several annotator-only properties.\n //\n // nb. We don't currently strip all the annotator-only properties here.\n // That's OK because validation / filtering happens in the sidebar app itself.\n // It just results in unnecessary content in the sidebar iframe's URL string.\n if (key === 'notebookAppUrl' || key === 'sidebarAppUrl') {\n continue;\n }\n\n // Strip nullish properties, as these are ignored by the application and\n // they add noise to logs etc.\n //\n // eslint-disable-next-line eqeqeq\n if (value == null) {\n continue;\n }\n\n appConfig[key] = value;\n }\n\n // Pass the expected origin of the app. This is used to detect when it is\n // served from a different location than expected, which may stop it working.\n appConfig.origin = new URL(appURL).origin;\n\n // Pass the version of the client, so we can check if it is the same as the\n // one used in the sidebar/notebook.\n appConfig.version = '__VERSION__';\n\n // Pass the URL of the page that embedded the client.\n const hostURL = new URL(window.location.href);\n hostURL.hash = '';\n appConfig.hostURL = hostURL.toString();\n\n // Some config settings are not JSON-stringifiable (e.g. JavaScript\n // functions) and will be omitted when the config is JSON-stringified.\n // Add a JSON-stringifiable option for each of these so that the sidebar can\n // at least know whether the callback functions were provided or not.\n if (Array.isArray(appConfig.services) && appConfig.services?.length > 0) {\n const service = appConfig.services[0];\n if (service.onLoginRequest) {\n service.onLoginRequestProvided = true;\n }\n if (service.onLogoutRequest) {\n service.onLogoutRequestProvided = true;\n }\n if (service.onSignupRequest) {\n service.onSignupRequestProvided = true;\n }\n if (service.onProfileRequest) {\n service.onProfileRequestProvided = true;\n }\n if (service.onHelpRequest) {\n service.onHelpRequestProvided = true;\n }\n }\n\n return appConfig;\n}\n","import { IconButton } from '@hypothesis/frontend-shared';\nimport { useEffect, useRef, useState } from 'preact/hooks';\nimport classnames from 'classnames';\n\nimport { addConfigFragment } from '../../shared/config-fragment';\nimport { createAppConfig } from '../config/app';\n\n/**\n * Configuration used to launch the notebook application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n *\n * @typedef {{ notebookAppUrl: string } & Record<string, unknown>} NotebookConfig\n */\n\n/**\n * @typedef NotebookIframeProps\n * @prop {NotebookConfig} config\n * @prop {string} groupId\n */\n\n/**\n * Create the iframe that will load the notebook application.\n *\n * @param {NotebookIframeProps} props\n */\nfunction NotebookIframe({ config, groupId }) {\n const notebookAppSrc = addConfigFragment(config.notebookAppUrl, {\n ...createAppConfig(config.notebookAppUrl, config),\n\n // Explicity set the \"focused\" group\n group: groupId,\n });\n\n return (\n <iframe\n title={'Hypothesis annotation notebook'}\n className=\"h-full w-full border-0\"\n // Enable media in annotations to be shown fullscreen\n allowFullScreen\n src={notebookAppSrc}\n />\n );\n}\n\n/** @typedef {import('../util/emitter').Emitter} Emitter */\n\n/**\n * @typedef NotebookModalProps\n * @prop {import('../util/emitter').EventBus} eventBus\n * @prop {NotebookConfig} config\n */\n\n/**\n * Create a modal component that hosts (1) the notebook iframe and (2) a button to close the modal.\n *\n * @param {NotebookModalProps} props\n */\nexport default function NotebookModal({ eventBus, config }) {\n // Temporary solution: while there is no mechanism to sync new annotations in\n // the notebook, we force re-rendering of the iframe on every 'openNotebook'\n // event, so that the new annotations are displayed.\n // https://github.com/hypothesis/client/issues/3182\n const [iframeKey, setIframeKey] = useState(0);\n const [isHidden, setIsHidden] = useState(true);\n const [groupId, setGroupId] = useState(/** @type {string|null} */ (null));\n const originalDocumentOverflowStyle = useRef('');\n const emitterRef = useRef(/** @type {Emitter|null} */ (null));\n\n // Stores the original overflow CSS property of document.body and reset it\n // when the component is destroyed\n useEffect(() => {\n originalDocumentOverflowStyle.current = document.body.style.overflow;\n\n return () => {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n };\n }, []);\n\n // The overflow CSS property is set to hidden to prevent scrolling of the host page,\n // while the notebook modal is open. It is restored when the modal is closed.\n useEffect(() => {\n if (isHidden) {\n document.body.style.overflow = originalDocumentOverflowStyle.current;\n } else {\n document.body.style.overflow = 'hidden';\n }\n }, [isHidden]);\n\n useEffect(() => {\n const emitter = eventBus.createEmitter();\n emitter.subscribe('openNotebook', (/** @type {string} */ groupId) => {\n setIsHidden(false);\n setIframeKey(iframeKey => iframeKey + 1);\n setGroupId(groupId);\n });\n emitterRef.current = emitter;\n\n return () => {\n emitter.destroy();\n };\n }, [eventBus]);\n\n const onClose = () => {\n setIsHidden(true);\n emitterRef.current?.publish('closeNotebook');\n };\n\n if (groupId === null) {\n return null;\n }\n\n return (\n <div\n className={classnames(\n 'fixed z-max top-0 left-0 right-0 bottom-0 p-3 bg-black/50',\n { hidden: isHidden }\n )}\n data-testid=\"notebook-outer\"\n >\n <div className=\"relative w-full h-full\" data-testid=\"notebook-inner\">\n <div className=\"absolute right-0 text-xl m-3\">\n <IconButton\n icon=\"cancel\"\n title=\"Close the Notebook\"\n onClick={onClose}\n variant=\"dark\"\n />\n </div>\n <NotebookIframe key={iframeKey} config={config} groupId={groupId} />\n </div>\n </div>\n );\n}\n","import { createShadowRoot } from './util/shadow-root';\nimport { render } from 'preact';\nimport NotebookModal from './components/NotebookModal';\n\n/**\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('./components/NotebookModal').NotebookConfig} NotebookConfig\n */\n\n/** @implements {Destroyable} */\nexport class Notebook {\n /**\n * @param {HTMLElement} element\n * @param {import('./util/emitter').EventBus} eventBus -\n * Enables communication between components sharing the same eventBus\n * @param {NotebookConfig} config\n */\n constructor(element, eventBus, config) {\n /**\n * Un-styled shadow host for the notebook content.\n * This isolates the notebook from the page's styles.\n */\n this._outerContainer = document.createElement('hypothesis-notebook');\n element.appendChild(this._outerContainer);\n this.shadowRoot = createShadowRoot(this._outerContainer);\n\n render(\n <NotebookModal eventBus={eventBus} config={config} />,\n this.shadowRoot\n );\n }\n\n destroy() {\n render(null, this.shadowRoot);\n this._outerContainer.remove();\n }\n}\n","/*! Hammer.JS - v2.0.7 - 2016-04-22\n * http://hammerjs.github.io/\n *\n * Copyright (c) 2016 Jorik Tangelder;\n * Licensed under the MIT license */\n(function(window, document, exportName, undefined) {\n 'use strict';\n\nvar VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];\nvar TEST_ELEMENT = document.createElement('div');\n\nvar TYPE_FUNCTION = 'function';\n\nvar round = Math.round;\nvar abs = Math.abs;\nvar now = Date.now;\n\n/**\n * set a timeout with a given scope\n * @param {Function} fn\n * @param {Number} timeout\n * @param {Object} context\n * @returns {number}\n */\nfunction setTimeoutContext(fn, timeout, context) {\n return setTimeout(bindFn(fn, context), timeout);\n}\n\n/**\n * if the argument is an array, we want to execute the fn on each entry\n * if it aint an array we don't want to do a thing.\n * this is used by all the methods that accept a single and array argument.\n * @param {*|Array} arg\n * @param {String} fn\n * @param {Object} [context]\n * @returns {Boolean}\n */\nfunction invokeArrayArg(arg, fn, context) {\n if (Array.isArray(arg)) {\n each(arg, context[fn], context);\n return true;\n }\n return false;\n}\n\n/**\n * walk objects and arrays\n * @param {Object} obj\n * @param {Function} iterator\n * @param {Object} context\n */\nfunction each(obj, iterator, context) {\n var i;\n\n if (!obj) {\n return;\n }\n\n if (obj.forEach) {\n obj.forEach(iterator, context);\n } else if (obj.length !== undefined) {\n i = 0;\n while (i < obj.length) {\n iterator.call(context, obj[i], i, obj);\n i++;\n }\n } else {\n for (i in obj) {\n obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);\n }\n }\n}\n\n/**\n * wrap a method with a deprecation warning and stack trace\n * @param {Function} method\n * @param {String} name\n * @param {String} message\n * @returns {Function} A new function wrapping the supplied method.\n */\nfunction deprecate(method, name, message) {\n var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\\n' + message + ' AT \\n';\n return function() {\n var e = new Error('get-stack-trace');\n var stack = e && e.stack ? e.stack.replace(/^[^\\(]+?[\\n$]/gm, '')\n .replace(/^\\s+at\\s+/gm, '')\n .replace(/^Object.<anonymous>\\s*\\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';\n\n var log = window.console && (window.console.warn || window.console.log);\n if (log) {\n log.call(window.console, deprecationMessage, stack);\n }\n return method.apply(this, arguments);\n };\n}\n\n/**\n * extend object.\n * means that properties in dest will be overwritten by the ones in src.\n * @param {Object} target\n * @param {...Object} objects_to_assign\n * @returns {Object} target\n */\nvar assign;\nif (typeof Object.assign !== 'function') {\n assign = function assign(target) {\n if (target === undefined || target === null) {\n throw new TypeError('Cannot convert undefined or null to object');\n }\n\n var output = Object(target);\n for (var index = 1; index < arguments.length; index++) {\n var source = arguments[index];\n if (source !== undefined && source !== null) {\n for (var nextKey in source) {\n if (source.hasOwnProperty(nextKey)) {\n output[nextKey] = source[nextKey];\n }\n }\n }\n }\n return output;\n };\n} else {\n assign = Object.assign;\n}\n\n/**\n * extend object.\n * means that properties in dest will be overwritten by the ones in src.\n * @param {Object} dest\n * @param {Object} src\n * @param {Boolean} [merge=false]\n * @returns {Object} dest\n */\nvar extend = deprecate(function extend(dest, src, merge) {\n var keys = Object.keys(src);\n var i = 0;\n while (i < keys.length) {\n if (!merge || (merge && dest[keys[i]] === undefined)) {\n dest[keys[i]] = src[keys[i]];\n }\n i++;\n }\n return dest;\n}, 'extend', 'Use `assign`.');\n\n/**\n * merge the values from src in the dest.\n * means that properties that exist in dest will not be overwritten by src\n * @param {Object} dest\n * @param {Object} src\n * @returns {Object} dest\n */\nvar merge = deprecate(function merge(dest, src) {\n return extend(dest, src, true);\n}, 'merge', 'Use `assign`.');\n\n/**\n * simple class inheritance\n * @param {Function} child\n * @param {Function} base\n * @param {Object} [properties]\n */\nfunction inherit(child, base, properties) {\n var baseP = base.prototype,\n childP;\n\n childP = child.prototype = Object.create(baseP);\n childP.constructor = child;\n childP._super = baseP;\n\n if (properties) {\n assign(childP, properties);\n }\n}\n\n/**\n * simple function bind\n * @param {Function} fn\n * @param {Object} context\n * @returns {Function}\n */\nfunction bindFn(fn, context) {\n return function boundFn() {\n return fn.apply(context, arguments);\n };\n}\n\n/**\n * let a boolean value also be a function that must return a boolean\n * this first item in args will be used as the context\n * @param {Boolean|Function} val\n * @param {Array} [args]\n * @returns {Boolean}\n */\nfunction boolOrFn(val, args) {\n if (typeof val == TYPE_FUNCTION) {\n return val.apply(args ? args[0] || undefined : undefined, args);\n }\n return val;\n}\n\n/**\n * use the val2 when val1 is undefined\n * @param {*} val1\n * @param {*} val2\n * @returns {*}\n */\nfunction ifUndefined(val1, val2) {\n return (val1 === undefined) ? val2 : val1;\n}\n\n/**\n * addEventListener with multiple events at once\n * @param {EventTarget} target\n * @param {String} types\n * @param {Function} handler\n */\nfunction addEventListeners(target, types, handler) {\n each(splitStr(types), function(type) {\n target.addEventListener(type, handler, false);\n });\n}\n\n/**\n * removeEventListener with multiple events at once\n * @param {EventTarget} target\n * @param {String} types\n * @param {Function} handler\n */\nfunction removeEventListeners(target, types, handler) {\n each(splitStr(types), function(type) {\n target.removeEventListener(type, handler, false);\n });\n}\n\n/**\n * find if a node is in the given parent\n * @method hasParent\n * @param {HTMLElement} node\n * @param {HTMLElement} parent\n * @return {Boolean} found\n */\nfunction hasParent(node, parent) {\n while (node) {\n if (node == parent) {\n return true;\n }\n node = node.parentNode;\n }\n return false;\n}\n\n/**\n * small indexOf wrapper\n * @param {String} str\n * @param {String} find\n * @returns {Boolean} found\n */\nfunction inStr(str, find) {\n return str.indexOf(find) > -1;\n}\n\n/**\n * split string on whitespace\n * @param {String} str\n * @returns {Array} words\n */\nfunction splitStr(str) {\n return str.trim().split(/\\s+/g);\n}\n\n/**\n * find if a array contains the object using indexOf or a simple polyFill\n * @param {Array} src\n * @param {String} find\n * @param {String} [findByKey]\n * @return {Boolean|Number} false when not found, or the index\n */\nfunction inArray(src, find, findByKey) {\n if (src.indexOf && !findByKey) {\n return src.indexOf(find);\n } else {\n var i = 0;\n while (i < src.length) {\n if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {\n return i;\n }\n i++;\n }\n return -1;\n }\n}\n\n/**\n * convert array-like objects to real arrays\n * @param {Object} obj\n * @returns {Array}\n */\nfunction toArray(obj) {\n return Array.prototype.slice.call(obj, 0);\n}\n\n/**\n * unique array with objects based on a key (like 'id') or just by the array's value\n * @param {Array} src [{id:1},{id:2},{id:1}]\n * @param {String} [key]\n * @param {Boolean} [sort=False]\n * @returns {Array} [{id:1},{id:2}]\n */\nfunction uniqueArray(src, key, sort) {\n var results = [];\n var values = [];\n var i = 0;\n\n while (i < src.length) {\n var val = key ? src[i][key] : src[i];\n if (inArray(values, val) < 0) {\n results.push(src[i]);\n }\n values[i] = val;\n i++;\n }\n\n if (sort) {\n if (!key) {\n results = results.sort();\n } else {\n results = results.sort(function sortUniqueArray(a, b) {\n return a[key] > b[key];\n });\n }\n }\n\n return results;\n}\n\n/**\n * get the prefixed property\n * @param {Object} obj\n * @param {String} property\n * @returns {String|Undefined} prefixed\n */\nfunction prefixed(obj, property) {\n var prefix, prop;\n var camelProp = property[0].toUpperCase() + property.slice(1);\n\n var i = 0;\n while (i < VENDOR_PREFIXES.length) {\n prefix = VENDOR_PREFIXES[i];\n prop = (prefix) ? prefix + camelProp : property;\n\n if (prop in obj) {\n return prop;\n }\n i++;\n }\n return undefined;\n}\n\n/**\n * get a unique id\n * @returns {number} uniqueId\n */\nvar _uniqueId = 1;\nfunction uniqueId() {\n return _uniqueId++;\n}\n\n/**\n * get the window object of an element\n * @param {HTMLElement} element\n * @returns {DocumentView|Window}\n */\nfunction getWindowForElement(element) {\n var doc = element.ownerDocument || element;\n return (doc.defaultView || doc.parentWindow || window);\n}\n\nvar MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;\n\nvar SUPPORT_TOUCH = ('ontouchstart' in window);\nvar SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;\nvar SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);\n\nvar INPUT_TYPE_TOUCH = 'touch';\nvar INPUT_TYPE_PEN = 'pen';\nvar INPUT_TYPE_MOUSE = 'mouse';\nvar INPUT_TYPE_KINECT = 'kinect';\n\nvar COMPUTE_INTERVAL = 25;\n\nvar INPUT_START = 1;\nvar INPUT_MOVE = 2;\nvar INPUT_END = 4;\nvar INPUT_CANCEL = 8;\n\nvar DIRECTION_NONE = 1;\nvar DIRECTION_LEFT = 2;\nvar DIRECTION_RIGHT = 4;\nvar DIRECTION_UP = 8;\nvar DIRECTION_DOWN = 16;\n\nvar DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;\nvar DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;\nvar DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;\n\nvar PROPS_XY = ['x', 'y'];\nvar PROPS_CLIENT_XY = ['clientX', 'clientY'];\n\n/**\n * create new input type manager\n * @param {Manager} manager\n * @param {Function} callback\n * @returns {Input}\n * @constructor\n */\nfunction Input(manager, callback) {\n var self = this;\n this.manager = manager;\n this.callback = callback;\n this.element = manager.element;\n this.target = manager.options.inputTarget;\n\n // smaller wrapper around the handler, for the scope and the enabled state of the manager,\n // so when disabled the input events are completely bypassed.\n this.domHandler = function(ev) {\n if (boolOrFn(manager.options.enable, [manager])) {\n self.handler(ev);\n }\n };\n\n this.init();\n\n}\n\nInput.prototype = {\n /**\n * should handle the inputEvent data and trigger the callback\n * @virtual\n */\n handler: function() { },\n\n /**\n * bind the events\n */\n init: function() {\n this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);\n this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);\n this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);\n },\n\n /**\n * unbind the events\n */\n destroy: function() {\n this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);\n this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);\n this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);\n }\n};\n\n/**\n * create new input type manager\n * called by the Manager constructor\n * @param {Hammer} manager\n * @returns {Input}\n */\nfunction createInputInstance(manager) {\n var Type;\n var inputClass = manager.options.inputClass;\n\n if (inputClass) {\n Type = inputClass;\n } else if (SUPPORT_POINTER_EVENTS) {\n Type = PointerEventInput;\n } else if (SUPPORT_ONLY_TOUCH) {\n Type = TouchInput;\n } else if (!SUPPORT_TOUCH) {\n Type = MouseInput;\n } else {\n Type = TouchMouseInput;\n }\n return new (Type)(manager, inputHandler);\n}\n\n/**\n * handle input events\n * @param {Manager} manager\n * @param {String} eventType\n * @param {Object} input\n */\nfunction inputHandler(manager, eventType, input) {\n var pointersLen = input.pointers.length;\n var changedPointersLen = input.changedPointers.length;\n var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));\n var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));\n\n input.isFirst = !!isFirst;\n input.isFinal = !!isFinal;\n\n if (isFirst) {\n manager.session = {};\n }\n\n // source event is the normalized value of the domEvents\n // like 'touchstart, mouseup, pointerdown'\n input.eventType = eventType;\n\n // compute scale, rotation etc\n computeInputData(manager, input);\n\n // emit secret event\n manager.emit('hammer.input', input);\n\n manager.recognize(input);\n manager.session.prevInput = input;\n}\n\n/**\n * extend the data with some usable properties like scale, rotate, velocity etc\n * @param {Object} manager\n * @param {Object} input\n */\nfunction computeInputData(manager, input) {\n var session = manager.session;\n var pointers = input.pointers;\n var pointersLength = pointers.length;\n\n // store the first input to calculate the distance and direction\n if (!session.firstInput) {\n session.firstInput = simpleCloneInputData(input);\n }\n\n // to compute scale and rotation we need to store the multiple touches\n if (pointersLength > 1 && !session.firstMultiple) {\n session.firstMultiple = simpleCloneInputData(input);\n } else if (pointersLength === 1) {\n session.firstMultiple = false;\n }\n\n var firstInput = session.firstInput;\n var firstMultiple = session.firstMultiple;\n var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;\n\n var center = input.center = getCenter(pointers);\n input.timeStamp = now();\n input.deltaTime = input.timeStamp - firstInput.timeStamp;\n\n input.angle = getAngle(offsetCenter, center);\n input.distance = getDistance(offsetCenter, center);\n\n computeDeltaXY(session, input);\n input.offsetDirection = getDirection(input.deltaX, input.deltaY);\n\n var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);\n input.overallVelocityX = overallVelocity.x;\n input.overallVelocityY = overallVelocity.y;\n input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;\n\n input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;\n input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;\n\n input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >\n session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);\n\n computeIntervalInputData(session, input);\n\n // find the correct target\n var target = manager.element;\n if (hasParent(input.srcEvent.target, target)) {\n target = input.srcEvent.target;\n }\n input.target = target;\n}\n\nfunction computeDeltaXY(session, input) {\n var center = input.center;\n var offset = session.offsetDelta || {};\n var prevDelta = session.prevDelta || {};\n var prevInput = session.prevInput || {};\n\n if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {\n prevDelta = session.prevDelta = {\n x: prevInput.deltaX || 0,\n y: prevInput.deltaY || 0\n };\n\n offset = session.offsetDelta = {\n x: center.x,\n y: center.y\n };\n }\n\n input.deltaX = prevDelta.x + (center.x - offset.x);\n input.deltaY = prevDelta.y + (center.y - offset.y);\n}\n\n/**\n * velocity is calculated every x ms\n * @param {Object} session\n * @param {Object} input\n */\nfunction computeIntervalInputData(session, input) {\n var last = session.lastInterval || input,\n deltaTime = input.timeStamp - last.timeStamp,\n velocity, velocityX, velocityY, direction;\n\n if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {\n var deltaX = input.deltaX - last.deltaX;\n var deltaY = input.deltaY - last.deltaY;\n\n var v = getVelocity(deltaTime, deltaX, deltaY);\n velocityX = v.x;\n velocityY = v.y;\n velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;\n direction = getDirection(deltaX, deltaY);\n\n session.lastInterval = input;\n } else {\n // use latest velocity info if it doesn't overtake a minimum period\n velocity = last.velocity;\n velocityX = last.velocityX;\n velocityY = last.velocityY;\n direction = last.direction;\n }\n\n input.velocity = velocity;\n input.velocityX = velocityX;\n input.velocityY = velocityY;\n input.direction = direction;\n}\n\n/**\n * create a simple clone from the input used for storage of firstInput and firstMultiple\n * @param {Object} input\n * @returns {Object} clonedInputData\n */\nfunction simpleCloneInputData(input) {\n // make a simple copy of the pointers because we will get a reference if we don't\n // we only need clientXY for the calculations\n var pointers = [];\n var i = 0;\n while (i < input.pointers.length) {\n pointers[i] = {\n clientX: round(input.pointers[i].clientX),\n clientY: round(input.pointers[i].clientY)\n };\n i++;\n }\n\n return {\n timeStamp: now(),\n pointers: pointers,\n center: getCenter(pointers),\n deltaX: input.deltaX,\n deltaY: input.deltaY\n };\n}\n\n/**\n * get the center of all the pointers\n * @param {Array} pointers\n * @return {Object} center contains `x` and `y` properties\n */\nfunction getCenter(pointers) {\n var pointersLength = pointers.length;\n\n // no need to loop when only one touch\n if (pointersLength === 1) {\n return {\n x: round(pointers[0].clientX),\n y: round(pointers[0].clientY)\n };\n }\n\n var x = 0, y = 0, i = 0;\n while (i < pointersLength) {\n x += pointers[i].clientX;\n y += pointers[i].clientY;\n i++;\n }\n\n return {\n x: round(x / pointersLength),\n y: round(y / pointersLength)\n };\n}\n\n/**\n * calculate the velocity between two points. unit is in px per ms.\n * @param {Number} deltaTime\n * @param {Number} x\n * @param {Number} y\n * @return {Object} velocity `x` and `y`\n */\nfunction getVelocity(deltaTime, x, y) {\n return {\n x: x / deltaTime || 0,\n y: y / deltaTime || 0\n };\n}\n\n/**\n * get the direction between two points\n * @param {Number} x\n * @param {Number} y\n * @return {Number} direction\n */\nfunction getDirection(x, y) {\n if (x === y) {\n return DIRECTION_NONE;\n }\n\n if (abs(x) >= abs(y)) {\n return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;\n}\n\n/**\n * calculate the absolute distance between two points\n * @param {Object} p1 {x, y}\n * @param {Object} p2 {x, y}\n * @param {Array} [props] containing x and y keys\n * @return {Number} distance\n */\nfunction getDistance(p1, p2, props) {\n if (!props) {\n props = PROPS_XY;\n }\n var x = p2[props[0]] - p1[props[0]],\n y = p2[props[1]] - p1[props[1]];\n\n return Math.sqrt((x * x) + (y * y));\n}\n\n/**\n * calculate the angle between two coordinates\n * @param {Object} p1\n * @param {Object} p2\n * @param {Array} [props] containing x and y keys\n * @return {Number} angle\n */\nfunction getAngle(p1, p2, props) {\n if (!props) {\n props = PROPS_XY;\n }\n var x = p2[props[0]] - p1[props[0]],\n y = p2[props[1]] - p1[props[1]];\n return Math.atan2(y, x) * 180 / Math.PI;\n}\n\n/**\n * calculate the rotation degrees between two pointersets\n * @param {Array} start array of pointers\n * @param {Array} end array of pointers\n * @return {Number} rotation\n */\nfunction getRotation(start, end) {\n return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);\n}\n\n/**\n * calculate the scale factor between two pointersets\n * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out\n * @param {Array} start array of pointers\n * @param {Array} end array of pointers\n * @return {Number} scale\n */\nfunction getScale(start, end) {\n return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);\n}\n\nvar MOUSE_INPUT_MAP = {\n mousedown: INPUT_START,\n mousemove: INPUT_MOVE,\n mouseup: INPUT_END\n};\n\nvar MOUSE_ELEMENT_EVENTS = 'mousedown';\nvar MOUSE_WINDOW_EVENTS = 'mousemove mouseup';\n\n/**\n * Mouse events input\n * @constructor\n * @extends Input\n */\nfunction MouseInput() {\n this.evEl = MOUSE_ELEMENT_EVENTS;\n this.evWin = MOUSE_WINDOW_EVENTS;\n\n this.pressed = false; // mousedown state\n\n Input.apply(this, arguments);\n}\n\ninherit(MouseInput, Input, {\n /**\n * handle mouse events\n * @param {Object} ev\n */\n handler: function MEhandler(ev) {\n var eventType = MOUSE_INPUT_MAP[ev.type];\n\n // on start we want to have the left mouse button down\n if (eventType & INPUT_START && ev.button === 0) {\n this.pressed = true;\n }\n\n if (eventType & INPUT_MOVE && ev.which !== 1) {\n eventType = INPUT_END;\n }\n\n // mouse must be down\n if (!this.pressed) {\n return;\n }\n\n if (eventType & INPUT_END) {\n this.pressed = false;\n }\n\n this.callback(this.manager, eventType, {\n pointers: [ev],\n changedPointers: [ev],\n pointerType: INPUT_TYPE_MOUSE,\n srcEvent: ev\n });\n }\n});\n\nvar POINTER_INPUT_MAP = {\n pointerdown: INPUT_START,\n pointermove: INPUT_MOVE,\n pointerup: INPUT_END,\n pointercancel: INPUT_CANCEL,\n pointerout: INPUT_CANCEL\n};\n\n// in IE10 the pointer types is defined as an enum\nvar IE10_POINTER_TYPE_ENUM = {\n 2: INPUT_TYPE_TOUCH,\n 3: INPUT_TYPE_PEN,\n 4: INPUT_TYPE_MOUSE,\n 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816\n};\n\nvar POINTER_ELEMENT_EVENTS = 'pointerdown';\nvar POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';\n\n// IE10 has prefixed support, and case-sensitive\nif (window.MSPointerEvent && !window.PointerEvent) {\n POINTER_ELEMENT_EVENTS = 'MSPointerDown';\n POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';\n}\n\n/**\n * Pointer events input\n * @constructor\n * @extends Input\n */\nfunction PointerEventInput() {\n this.evEl = POINTER_ELEMENT_EVENTS;\n this.evWin = POINTER_WINDOW_EVENTS;\n\n Input.apply(this, arguments);\n\n this.store = (this.manager.session.pointerEvents = []);\n}\n\ninherit(PointerEventInput, Input, {\n /**\n * handle mouse events\n * @param {Object} ev\n */\n handler: function PEhandler(ev) {\n var store = this.store;\n var removePointer = false;\n\n var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');\n var eventType = POINTER_INPUT_MAP[eventTypeNormalized];\n var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;\n\n var isTouch = (pointerType == INPUT_TYPE_TOUCH);\n\n // get index of the event in the store\n var storeIndex = inArray(store, ev.pointerId, 'pointerId');\n\n // start and mouse must be down\n if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {\n if (storeIndex < 0) {\n store.push(ev);\n storeIndex = store.length - 1;\n }\n } else if (eventType & (INPUT_END | INPUT_CANCEL)) {\n removePointer = true;\n }\n\n // it not found, so the pointer hasn't been down (so it's probably a hover)\n if (storeIndex < 0) {\n return;\n }\n\n // update the event in the store\n store[storeIndex] = ev;\n\n this.callback(this.manager, eventType, {\n pointers: store,\n changedPointers: [ev],\n pointerType: pointerType,\n srcEvent: ev\n });\n\n if (removePointer) {\n // remove from the store\n store.splice(storeIndex, 1);\n }\n }\n});\n\nvar SINGLE_TOUCH_INPUT_MAP = {\n touchstart: INPUT_START,\n touchmove: INPUT_MOVE,\n touchend: INPUT_END,\n touchcancel: INPUT_CANCEL\n};\n\nvar SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';\nvar SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';\n\n/**\n * Touch events input\n * @constructor\n * @extends Input\n */\nfunction SingleTouchInput() {\n this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;\n this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;\n this.started = false;\n\n Input.apply(this, arguments);\n}\n\ninherit(SingleTouchInput, Input, {\n handler: function TEhandler(ev) {\n var type = SINGLE_TOUCH_INPUT_MAP[ev.type];\n\n // should we handle the touch events?\n if (type === INPUT_START) {\n this.started = true;\n }\n\n if (!this.started) {\n return;\n }\n\n var touches = normalizeSingleTouches.call(this, ev, type);\n\n // when done, reset the started state\n if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {\n this.started = false;\n }\n\n this.callback(this.manager, type, {\n pointers: touches[0],\n changedPointers: touches[1],\n pointerType: INPUT_TYPE_TOUCH,\n srcEvent: ev\n });\n }\n});\n\n/**\n * @this {TouchInput}\n * @param {Object} ev\n * @param {Number} type flag\n * @returns {undefined|Array} [all, changed]\n */\nfunction normalizeSingleTouches(ev, type) {\n var all = toArray(ev.touches);\n var changed = toArray(ev.changedTouches);\n\n if (type & (INPUT_END | INPUT_CANCEL)) {\n all = uniqueArray(all.concat(changed), 'identifier', true);\n }\n\n return [all, changed];\n}\n\nvar TOUCH_INPUT_MAP = {\n touchstart: INPUT_START,\n touchmove: INPUT_MOVE,\n touchend: INPUT_END,\n touchcancel: INPUT_CANCEL\n};\n\nvar TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';\n\n/**\n * Multi-user touch events input\n * @constructor\n * @extends Input\n */\nfunction TouchInput() {\n this.evTarget = TOUCH_TARGET_EVENTS;\n this.targetIds = {};\n\n Input.apply(this, arguments);\n}\n\ninherit(TouchInput, Input, {\n handler: function MTEhandler(ev) {\n var type = TOUCH_INPUT_MAP[ev.type];\n var touches = getTouches.call(this, ev, type);\n if (!touches) {\n return;\n }\n\n this.callback(this.manager, type, {\n pointers: touches[0],\n changedPointers: touches[1],\n pointerType: INPUT_TYPE_TOUCH,\n srcEvent: ev\n });\n }\n});\n\n/**\n * @this {TouchInput}\n * @param {Object} ev\n * @param {Number} type flag\n * @returns {undefined|Array} [all, changed]\n */\nfunction getTouches(ev, type) {\n var allTouches = toArray(ev.touches);\n var targetIds = this.targetIds;\n\n // when there is only one touch, the process can be simplified\n if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {\n targetIds[allTouches[0].identifier] = true;\n return [allTouches, allTouches];\n }\n\n var i,\n targetTouches,\n changedTouches = toArray(ev.changedTouches),\n changedTargetTouches = [],\n target = this.target;\n\n // get target touches from touches\n targetTouches = allTouches.filter(function(touch) {\n return hasParent(touch.target, target);\n });\n\n // collect touches\n if (type === INPUT_START) {\n i = 0;\n while (i < targetTouches.length) {\n targetIds[targetTouches[i].identifier] = true;\n i++;\n }\n }\n\n // filter changed touches to only contain touches that exist in the collected target ids\n i = 0;\n while (i < changedTouches.length) {\n if (targetIds[changedTouches[i].identifier]) {\n changedTargetTouches.push(changedTouches[i]);\n }\n\n // cleanup removed touches\n if (type & (INPUT_END | INPUT_CANCEL)) {\n delete targetIds[changedTouches[i].identifier];\n }\n i++;\n }\n\n if (!changedTargetTouches.length) {\n return;\n }\n\n return [\n // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'\n uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),\n changedTargetTouches\n ];\n}\n\n/**\n * Combined touch and mouse input\n *\n * Touch has a higher priority then mouse, and while touching no mouse events are allowed.\n * This because touch devices also emit mouse events while doing a touch.\n *\n * @constructor\n * @extends Input\n */\n\nvar DEDUP_TIMEOUT = 2500;\nvar DEDUP_DISTANCE = 25;\n\nfunction TouchMouseInput() {\n Input.apply(this, arguments);\n\n var handler = bindFn(this.handler, this);\n this.touch = new TouchInput(this.manager, handler);\n this.mouse = new MouseInput(this.manager, handler);\n\n this.primaryTouch = null;\n this.lastTouches = [];\n}\n\ninherit(TouchMouseInput, Input, {\n /**\n * handle mouse and touch events\n * @param {Hammer} manager\n * @param {String} inputEvent\n * @param {Object} inputData\n */\n handler: function TMEhandler(manager, inputEvent, inputData) {\n var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),\n isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);\n\n if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {\n return;\n }\n\n // when we're in a touch event, record touches to de-dupe synthetic mouse event\n if (isTouch) {\n recordTouches.call(this, inputEvent, inputData);\n } else if (isMouse && isSyntheticEvent.call(this, inputData)) {\n return;\n }\n\n this.callback(manager, inputEvent, inputData);\n },\n\n /**\n * remove the event listeners\n */\n destroy: function destroy() {\n this.touch.destroy();\n this.mouse.destroy();\n }\n});\n\nfunction recordTouches(eventType, eventData) {\n if (eventType & INPUT_START) {\n this.primaryTouch = eventData.changedPointers[0].identifier;\n setLastTouch.call(this, eventData);\n } else if (eventType & (INPUT_END | INPUT_CANCEL)) {\n setLastTouch.call(this, eventData);\n }\n}\n\nfunction setLastTouch(eventData) {\n var touch = eventData.changedPointers[0];\n\n if (touch.identifier === this.primaryTouch) {\n var lastTouch = {x: touch.clientX, y: touch.clientY};\n this.lastTouches.push(lastTouch);\n var lts = this.lastTouches;\n var removeLastTouch = function() {\n var i = lts.indexOf(lastTouch);\n if (i > -1) {\n lts.splice(i, 1);\n }\n };\n setTimeout(removeLastTouch, DEDUP_TIMEOUT);\n }\n}\n\nfunction isSyntheticEvent(eventData) {\n var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;\n for (var i = 0; i < this.lastTouches.length; i++) {\n var t = this.lastTouches[i];\n var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);\n if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {\n return true;\n }\n }\n return false;\n}\n\nvar PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');\nvar NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;\n\n// magical touchAction value\nvar TOUCH_ACTION_COMPUTE = 'compute';\nvar TOUCH_ACTION_AUTO = 'auto';\nvar TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented\nvar TOUCH_ACTION_NONE = 'none';\nvar TOUCH_ACTION_PAN_X = 'pan-x';\nvar TOUCH_ACTION_PAN_Y = 'pan-y';\nvar TOUCH_ACTION_MAP = getTouchActionProps();\n\n/**\n * Touch Action\n * sets the touchAction property or uses the js alternative\n * @param {Manager} manager\n * @param {String} value\n * @constructor\n */\nfunction TouchAction(manager, value) {\n this.manager = manager;\n this.set(value);\n}\n\nTouchAction.prototype = {\n /**\n * set the touchAction value on the element or enable the polyfill\n * @param {String} value\n */\n set: function(value) {\n // find out the touch-action by the event handlers\n if (value == TOUCH_ACTION_COMPUTE) {\n value = this.compute();\n }\n\n if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {\n this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;\n }\n this.actions = value.toLowerCase().trim();\n },\n\n /**\n * just re-set the touchAction value\n */\n update: function() {\n this.set(this.manager.options.touchAction);\n },\n\n /**\n * compute the value for the touchAction property based on the recognizer's settings\n * @returns {String} value\n */\n compute: function() {\n var actions = [];\n each(this.manager.recognizers, function(recognizer) {\n if (boolOrFn(recognizer.options.enable, [recognizer])) {\n actions = actions.concat(recognizer.getTouchAction());\n }\n });\n return cleanTouchActions(actions.join(' '));\n },\n\n /**\n * this method is called on each input cycle and provides the preventing of the browser behavior\n * @param {Object} input\n */\n preventDefaults: function(input) {\n var srcEvent = input.srcEvent;\n var direction = input.offsetDirection;\n\n // if the touch action did prevented once this session\n if (this.manager.session.prevented) {\n srcEvent.preventDefault();\n return;\n }\n\n var actions = this.actions;\n var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];\n var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];\n var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];\n\n if (hasNone) {\n //do not prevent defaults if this is a tap gesture\n\n var isTapPointer = input.pointers.length === 1;\n var isTapMovement = input.distance < 2;\n var isTapTouchTime = input.deltaTime < 250;\n\n if (isTapPointer && isTapMovement && isTapTouchTime) {\n return;\n }\n }\n\n if (hasPanX && hasPanY) {\n // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent\n return;\n }\n\n if (hasNone ||\n (hasPanY && direction & DIRECTION_HORIZONTAL) ||\n (hasPanX && direction & DIRECTION_VERTICAL)) {\n return this.preventSrc(srcEvent);\n }\n },\n\n /**\n * call preventDefault to prevent the browser's default behavior (scrolling in most cases)\n * @param {Object} srcEvent\n */\n preventSrc: function(srcEvent) {\n this.manager.session.prevented = true;\n srcEvent.preventDefault();\n }\n};\n\n/**\n * when the touchActions are collected they are not a valid value, so we need to clean things up. *\n * @param {String} actions\n * @returns {*}\n */\nfunction cleanTouchActions(actions) {\n // none\n if (inStr(actions, TOUCH_ACTION_NONE)) {\n return TOUCH_ACTION_NONE;\n }\n\n var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);\n var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);\n\n // if both pan-x and pan-y are set (different recognizers\n // for different directions, e.g. horizontal pan but vertical swipe?)\n // we need none (as otherwise with pan-x pan-y combined none of these\n // recognizers will work, since the browser would handle all panning\n if (hasPanX && hasPanY) {\n return TOUCH_ACTION_NONE;\n }\n\n // pan-x OR pan-y\n if (hasPanX || hasPanY) {\n return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;\n }\n\n // manipulation\n if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {\n return TOUCH_ACTION_MANIPULATION;\n }\n\n return TOUCH_ACTION_AUTO;\n}\n\nfunction getTouchActionProps() {\n if (!NATIVE_TOUCH_ACTION) {\n return false;\n }\n var touchMap = {};\n var cssSupports = window.CSS && window.CSS.supports;\n ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {\n\n // If css.supports is not supported but there is native touch-action assume it supports\n // all values. This is the case for IE 10 and 11.\n touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;\n });\n return touchMap;\n}\n\n/**\n * Recognizer flow explained; *\n * All recognizers have the initial state of POSSIBLE when a input session starts.\n * The definition of a input session is from the first input until the last input, with all it's movement in it. *\n * Example session for mouse-input: mousedown -> mousemove -> mouseup\n *\n * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed\n * which determines with state it should be.\n *\n * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to\n * POSSIBLE to give it another change on the next cycle.\n *\n * Possible\n * |\n * +-----+---------------+\n * | |\n * +-----+-----+ |\n * | | |\n * Failed Cancelled |\n * +-------+------+\n * | |\n * Recognized Began\n * |\n * Changed\n * |\n * Ended/Recognized\n */\nvar STATE_POSSIBLE = 1;\nvar STATE_BEGAN = 2;\nvar STATE_CHANGED = 4;\nvar STATE_ENDED = 8;\nvar STATE_RECOGNIZED = STATE_ENDED;\nvar STATE_CANCELLED = 16;\nvar STATE_FAILED = 32;\n\n/**\n * Recognizer\n * Every recognizer needs to extend from this class.\n * @constructor\n * @param {Object} options\n */\nfunction Recognizer(options) {\n this.options = assign({}, this.defaults, options || {});\n\n this.id = uniqueId();\n\n this.manager = null;\n\n // default is enable true\n this.options.enable = ifUndefined(this.options.enable, true);\n\n this.state = STATE_POSSIBLE;\n\n this.simultaneous = {};\n this.requireFail = [];\n}\n\nRecognizer.prototype = {\n /**\n * @virtual\n * @type {Object}\n */\n defaults: {},\n\n /**\n * set options\n * @param {Object} options\n * @return {Recognizer}\n */\n set: function(options) {\n assign(this.options, options);\n\n // also update the touchAction, in case something changed about the directions/enabled state\n this.manager && this.manager.touchAction.update();\n return this;\n },\n\n /**\n * recognize simultaneous with an other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n recognizeWith: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {\n return this;\n }\n\n var simultaneous = this.simultaneous;\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n if (!simultaneous[otherRecognizer.id]) {\n simultaneous[otherRecognizer.id] = otherRecognizer;\n otherRecognizer.recognizeWith(this);\n }\n return this;\n },\n\n /**\n * drop the simultaneous link. it doesnt remove the link on the other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n dropRecognizeWith: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {\n return this;\n }\n\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n delete this.simultaneous[otherRecognizer.id];\n return this;\n },\n\n /**\n * recognizer can only run when an other is failing\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n requireFailure: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {\n return this;\n }\n\n var requireFail = this.requireFail;\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n if (inArray(requireFail, otherRecognizer) === -1) {\n requireFail.push(otherRecognizer);\n otherRecognizer.requireFailure(this);\n }\n return this;\n },\n\n /**\n * drop the requireFailure link. it does not remove the link on the other recognizer.\n * @param {Recognizer} otherRecognizer\n * @returns {Recognizer} this\n */\n dropRequireFailure: function(otherRecognizer) {\n if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {\n return this;\n }\n\n otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);\n var index = inArray(this.requireFail, otherRecognizer);\n if (index > -1) {\n this.requireFail.splice(index, 1);\n }\n return this;\n },\n\n /**\n * has require failures boolean\n * @returns {boolean}\n */\n hasRequireFailures: function() {\n return this.requireFail.length > 0;\n },\n\n /**\n * if the recognizer can recognize simultaneous with an other recognizer\n * @param {Recognizer} otherRecognizer\n * @returns {Boolean}\n */\n canRecognizeWith: function(otherRecognizer) {\n return !!this.simultaneous[otherRecognizer.id];\n },\n\n /**\n * You should use `tryEmit` instead of `emit` directly to check\n * that all the needed recognizers has failed before emitting.\n * @param {Object} input\n */\n emit: function(input) {\n var self = this;\n var state = this.state;\n\n function emit(event) {\n self.manager.emit(event, input);\n }\n\n // 'panstart' and 'panmove'\n if (state < STATE_ENDED) {\n emit(self.options.event + stateStr(state));\n }\n\n emit(self.options.event); // simple 'eventName' events\n\n if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)\n emit(input.additionalEvent);\n }\n\n // panend and pancancel\n if (state >= STATE_ENDED) {\n emit(self.options.event + stateStr(state));\n }\n },\n\n /**\n * Check that all the require failure recognizers has failed,\n * if true, it emits a gesture event,\n * otherwise, setup the state to FAILED.\n * @param {Object} input\n */\n tryEmit: function(input) {\n if (this.canEmit()) {\n return this.emit(input);\n }\n // it's failing anyway\n this.state = STATE_FAILED;\n },\n\n /**\n * can we emit?\n * @returns {boolean}\n */\n canEmit: function() {\n var i = 0;\n while (i < this.requireFail.length) {\n if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {\n return false;\n }\n i++;\n }\n return true;\n },\n\n /**\n * update the recognizer\n * @param {Object} inputData\n */\n recognize: function(inputData) {\n // make a new copy of the inputData\n // so we can change the inputData without messing up the other recognizers\n var inputDataClone = assign({}, inputData);\n\n // is is enabled and allow recognizing?\n if (!boolOrFn(this.options.enable, [this, inputDataClone])) {\n this.reset();\n this.state = STATE_FAILED;\n return;\n }\n\n // reset when we've reached the end\n if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {\n this.state = STATE_POSSIBLE;\n }\n\n this.state = this.process(inputDataClone);\n\n // the recognizer has recognized a gesture\n // so trigger an event\n if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {\n this.tryEmit(inputDataClone);\n }\n },\n\n /**\n * return the state of the recognizer\n * the actual recognizing happens in this method\n * @virtual\n * @param {Object} inputData\n * @returns {Const} STATE\n */\n process: function(inputData) { }, // jshint ignore:line\n\n /**\n * return the preferred touch-action\n * @virtual\n * @returns {Array}\n */\n getTouchAction: function() { },\n\n /**\n * called when the gesture isn't allowed to recognize\n * like when another is being recognized or it is disabled\n * @virtual\n */\n reset: function() { }\n};\n\n/**\n * get a usable string, used as event postfix\n * @param {Const} state\n * @returns {String} state\n */\nfunction stateStr(state) {\n if (state & STATE_CANCELLED) {\n return 'cancel';\n } else if (state & STATE_ENDED) {\n return 'end';\n } else if (state & STATE_CHANGED) {\n return 'move';\n } else if (state & STATE_BEGAN) {\n return 'start';\n }\n return '';\n}\n\n/**\n * direction cons to string\n * @param {Const} direction\n * @returns {String}\n */\nfunction directionStr(direction) {\n if (direction == DIRECTION_DOWN) {\n return 'down';\n } else if (direction == DIRECTION_UP) {\n return 'up';\n } else if (direction == DIRECTION_LEFT) {\n return 'left';\n } else if (direction == DIRECTION_RIGHT) {\n return 'right';\n }\n return '';\n}\n\n/**\n * get a recognizer by name if it is bound to a manager\n * @param {Recognizer|String} otherRecognizer\n * @param {Recognizer} recognizer\n * @returns {Recognizer}\n */\nfunction getRecognizerByNameIfManager(otherRecognizer, recognizer) {\n var manager = recognizer.manager;\n if (manager) {\n return manager.get(otherRecognizer);\n }\n return otherRecognizer;\n}\n\n/**\n * This recognizer is just used as a base for the simple attribute recognizers.\n * @constructor\n * @extends Recognizer\n */\nfunction AttrRecognizer() {\n Recognizer.apply(this, arguments);\n}\n\ninherit(AttrRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof AttrRecognizer\n */\n defaults: {\n /**\n * @type {Number}\n * @default 1\n */\n pointers: 1\n },\n\n /**\n * Used to check if it the recognizer receives valid input, like input.distance > 10.\n * @memberof AttrRecognizer\n * @param {Object} input\n * @returns {Boolean} recognized\n */\n attrTest: function(input) {\n var optionPointers = this.options.pointers;\n return optionPointers === 0 || input.pointers.length === optionPointers;\n },\n\n /**\n * Process the input and return the state for the recognizer\n * @memberof AttrRecognizer\n * @param {Object} input\n * @returns {*} State\n */\n process: function(input) {\n var state = this.state;\n var eventType = input.eventType;\n\n var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);\n var isValid = this.attrTest(input);\n\n // on cancel input and we've recognized before, return STATE_CANCELLED\n if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {\n return state | STATE_CANCELLED;\n } else if (isRecognized || isValid) {\n if (eventType & INPUT_END) {\n return state | STATE_ENDED;\n } else if (!(state & STATE_BEGAN)) {\n return STATE_BEGAN;\n }\n return state | STATE_CHANGED;\n }\n return STATE_FAILED;\n }\n});\n\n/**\n * Pan\n * Recognized when the pointer is down and moved in the allowed direction.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction PanRecognizer() {\n AttrRecognizer.apply(this, arguments);\n\n this.pX = null;\n this.pY = null;\n}\n\ninherit(PanRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof PanRecognizer\n */\n defaults: {\n event: 'pan',\n threshold: 10,\n pointers: 1,\n direction: DIRECTION_ALL\n },\n\n getTouchAction: function() {\n var direction = this.options.direction;\n var actions = [];\n if (direction & DIRECTION_HORIZONTAL) {\n actions.push(TOUCH_ACTION_PAN_Y);\n }\n if (direction & DIRECTION_VERTICAL) {\n actions.push(TOUCH_ACTION_PAN_X);\n }\n return actions;\n },\n\n directionTest: function(input) {\n var options = this.options;\n var hasMoved = true;\n var distance = input.distance;\n var direction = input.direction;\n var x = input.deltaX;\n var y = input.deltaY;\n\n // lock to axis?\n if (!(direction & options.direction)) {\n if (options.direction & DIRECTION_HORIZONTAL) {\n direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;\n hasMoved = x != this.pX;\n distance = Math.abs(input.deltaX);\n } else {\n direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;\n hasMoved = y != this.pY;\n distance = Math.abs(input.deltaY);\n }\n }\n input.direction = direction;\n return hasMoved && distance > options.threshold && direction & options.direction;\n },\n\n attrTest: function(input) {\n return AttrRecognizer.prototype.attrTest.call(this, input) &&\n (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));\n },\n\n emit: function(input) {\n\n this.pX = input.deltaX;\n this.pY = input.deltaY;\n\n var direction = directionStr(input.direction);\n\n if (direction) {\n input.additionalEvent = this.options.event + direction;\n }\n this._super.emit.call(this, input);\n }\n});\n\n/**\n * Pinch\n * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).\n * @constructor\n * @extends AttrRecognizer\n */\nfunction PinchRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(PinchRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof PinchRecognizer\n */\n defaults: {\n event: 'pinch',\n threshold: 0,\n pointers: 2\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_NONE];\n },\n\n attrTest: function(input) {\n return this._super.attrTest.call(this, input) &&\n (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);\n },\n\n emit: function(input) {\n if (input.scale !== 1) {\n var inOut = input.scale < 1 ? 'in' : 'out';\n input.additionalEvent = this.options.event + inOut;\n }\n this._super.emit.call(this, input);\n }\n});\n\n/**\n * Press\n * Recognized when the pointer is down for x ms without any movement.\n * @constructor\n * @extends Recognizer\n */\nfunction PressRecognizer() {\n Recognizer.apply(this, arguments);\n\n this._timer = null;\n this._input = null;\n}\n\ninherit(PressRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof PressRecognizer\n */\n defaults: {\n event: 'press',\n pointers: 1,\n time: 251, // minimal time of the pointer to be pressed\n threshold: 9 // a minimal movement is ok, but keep it low\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_AUTO];\n },\n\n process: function(input) {\n var options = this.options;\n var validPointers = input.pointers.length === options.pointers;\n var validMovement = input.distance < options.threshold;\n var validTime = input.deltaTime > options.time;\n\n this._input = input;\n\n // we only allow little movement\n // and we've reached an end event, so a tap is possible\n if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {\n this.reset();\n } else if (input.eventType & INPUT_START) {\n this.reset();\n this._timer = setTimeoutContext(function() {\n this.state = STATE_RECOGNIZED;\n this.tryEmit();\n }, options.time, this);\n } else if (input.eventType & INPUT_END) {\n return STATE_RECOGNIZED;\n }\n return STATE_FAILED;\n },\n\n reset: function() {\n clearTimeout(this._timer);\n },\n\n emit: function(input) {\n if (this.state !== STATE_RECOGNIZED) {\n return;\n }\n\n if (input && (input.eventType & INPUT_END)) {\n this.manager.emit(this.options.event + 'up', input);\n } else {\n this._input.timeStamp = now();\n this.manager.emit(this.options.event, this._input);\n }\n }\n});\n\n/**\n * Rotate\n * Recognized when two or more pointer are moving in a circular motion.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction RotateRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(RotateRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof RotateRecognizer\n */\n defaults: {\n event: 'rotate',\n threshold: 0,\n pointers: 2\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_NONE];\n },\n\n attrTest: function(input) {\n return this._super.attrTest.call(this, input) &&\n (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);\n }\n});\n\n/**\n * Swipe\n * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.\n * @constructor\n * @extends AttrRecognizer\n */\nfunction SwipeRecognizer() {\n AttrRecognizer.apply(this, arguments);\n}\n\ninherit(SwipeRecognizer, AttrRecognizer, {\n /**\n * @namespace\n * @memberof SwipeRecognizer\n */\n defaults: {\n event: 'swipe',\n threshold: 10,\n velocity: 0.3,\n direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,\n pointers: 1\n },\n\n getTouchAction: function() {\n return PanRecognizer.prototype.getTouchAction.call(this);\n },\n\n attrTest: function(input) {\n var direction = this.options.direction;\n var velocity;\n\n if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {\n velocity = input.overallVelocity;\n } else if (direction & DIRECTION_HORIZONTAL) {\n velocity = input.overallVelocityX;\n } else if (direction & DIRECTION_VERTICAL) {\n velocity = input.overallVelocityY;\n }\n\n return this._super.attrTest.call(this, input) &&\n direction & input.offsetDirection &&\n input.distance > this.options.threshold &&\n input.maxPointers == this.options.pointers &&\n abs(velocity) > this.options.velocity && input.eventType & INPUT_END;\n },\n\n emit: function(input) {\n var direction = directionStr(input.offsetDirection);\n if (direction) {\n this.manager.emit(this.options.event + direction, input);\n }\n\n this.manager.emit(this.options.event, input);\n }\n});\n\n/**\n * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur\n * between the given interval and position. The delay option can be used to recognize multi-taps without firing\n * a single tap.\n *\n * The eventData from the emitted event contains the property `tapCount`, which contains the amount of\n * multi-taps being recognized.\n * @constructor\n * @extends Recognizer\n */\nfunction TapRecognizer() {\n Recognizer.apply(this, arguments);\n\n // previous time and center,\n // used for tap counting\n this.pTime = false;\n this.pCenter = false;\n\n this._timer = null;\n this._input = null;\n this.count = 0;\n}\n\ninherit(TapRecognizer, Recognizer, {\n /**\n * @namespace\n * @memberof PinchRecognizer\n */\n defaults: {\n event: 'tap',\n pointers: 1,\n taps: 1,\n interval: 300, // max time between the multi-tap taps\n time: 250, // max time of the pointer to be down (like finger on the screen)\n threshold: 9, // a minimal movement is ok, but keep it low\n posThreshold: 10 // a multi-tap can be a bit off the initial position\n },\n\n getTouchAction: function() {\n return [TOUCH_ACTION_MANIPULATION];\n },\n\n process: function(input) {\n var options = this.options;\n\n var validPointers = input.pointers.length === options.pointers;\n var validMovement = input.distance < options.threshold;\n var validTouchTime = input.deltaTime < options.time;\n\n this.reset();\n\n if ((input.eventType & INPUT_START) && (this.count === 0)) {\n return this.failTimeout();\n }\n\n // we only allow little movement\n // and we've reached an end event, so a tap is possible\n if (validMovement && validTouchTime && validPointers) {\n if (input.eventType != INPUT_END) {\n return this.failTimeout();\n }\n\n var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;\n var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;\n\n this.pTime = input.timeStamp;\n this.pCenter = input.center;\n\n if (!validMultiTap || !validInterval) {\n this.count = 1;\n } else {\n this.count += 1;\n }\n\n this._input = input;\n\n // if tap count matches we have recognized it,\n // else it has began recognizing...\n var tapCount = this.count % options.taps;\n if (tapCount === 0) {\n // no failing requirements, immediately trigger the tap event\n // or wait as long as the multitap interval to trigger\n if (!this.hasRequireFailures()) {\n return STATE_RECOGNIZED;\n } else {\n this._timer = setTimeoutContext(function() {\n this.state = STATE_RECOGNIZED;\n this.tryEmit();\n }, options.interval, this);\n return STATE_BEGAN;\n }\n }\n }\n return STATE_FAILED;\n },\n\n failTimeout: function() {\n this._timer = setTimeoutContext(function() {\n this.state = STATE_FAILED;\n }, this.options.interval, this);\n return STATE_FAILED;\n },\n\n reset: function() {\n clearTimeout(this._timer);\n },\n\n emit: function() {\n if (this.state == STATE_RECOGNIZED) {\n this._input.tapCount = this.count;\n this.manager.emit(this.options.event, this._input);\n }\n }\n});\n\n/**\n * Simple way to create a manager with a default set of recognizers.\n * @param {HTMLElement} element\n * @param {Object} [options]\n * @constructor\n */\nfunction Hammer(element, options) {\n options = options || {};\n options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);\n return new Manager(element, options);\n}\n\n/**\n * @const {string}\n */\nHammer.VERSION = '2.0.7';\n\n/**\n * default settings\n * @namespace\n */\nHammer.defaults = {\n /**\n * set if DOM events are being triggered.\n * But this is slower and unused by simple implementations, so disabled by default.\n * @type {Boolean}\n * @default false\n */\n domEvents: false,\n\n /**\n * The value for the touchAction property/fallback.\n * When set to `compute` it will magically set the correct value based on the added recognizers.\n * @type {String}\n * @default compute\n */\n touchAction: TOUCH_ACTION_COMPUTE,\n\n /**\n * @type {Boolean}\n * @default true\n */\n enable: true,\n\n /**\n * EXPERIMENTAL FEATURE -- can be removed/changed\n * Change the parent input target element.\n * If Null, then it is being set the to main element.\n * @type {Null|EventTarget}\n * @default null\n */\n inputTarget: null,\n\n /**\n * force an input class\n * @type {Null|Function}\n * @default null\n */\n inputClass: null,\n\n /**\n * Default recognizer setup when calling `Hammer()`\n * When creating a new Manager these will be skipped.\n * @type {Array}\n */\n preset: [\n // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]\n [RotateRecognizer, {enable: false}],\n [PinchRecognizer, {enable: false}, ['rotate']],\n [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],\n [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],\n [TapRecognizer],\n [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],\n [PressRecognizer]\n ],\n\n /**\n * Some CSS properties can be used to improve the working of Hammer.\n * Add them to this method and they will be set when creating a new Manager.\n * @namespace\n */\n cssProps: {\n /**\n * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.\n * @type {String}\n * @default 'none'\n */\n userSelect: 'none',\n\n /**\n * Disable the Windows Phone grippers when pressing an element.\n * @type {String}\n * @default 'none'\n */\n touchSelect: 'none',\n\n /**\n * Disables the default callout shown when you touch and hold a touch target.\n * On iOS, when you touch and hold a touch target such as a link, Safari displays\n * a callout containing information about the link. This property allows you to disable that callout.\n * @type {String}\n * @default 'none'\n */\n touchCallout: 'none',\n\n /**\n * Specifies whether zooming is enabled. Used by IE10>\n * @type {String}\n * @default 'none'\n */\n contentZooming: 'none',\n\n /**\n * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.\n * @type {String}\n * @default 'none'\n */\n userDrag: 'none',\n\n /**\n * Overrides the highlight color shown when the user taps a link or a JavaScript\n * clickable element in iOS. This property obeys the alpha value, if specified.\n * @type {String}\n * @default 'rgba(0,0,0,0)'\n */\n tapHighlightColor: 'rgba(0,0,0,0)'\n }\n};\n\nvar STOP = 1;\nvar FORCED_STOP = 2;\n\n/**\n * Manager\n * @param {HTMLElement} element\n * @param {Object} [options]\n * @constructor\n */\nfunction Manager(element, options) {\n this.options = assign({}, Hammer.defaults, options || {});\n\n this.options.inputTarget = this.options.inputTarget || element;\n\n this.handlers = {};\n this.session = {};\n this.recognizers = [];\n this.oldCssProps = {};\n\n this.element = element;\n this.input = createInputInstance(this);\n this.touchAction = new TouchAction(this, this.options.touchAction);\n\n toggleCssProps(this, true);\n\n each(this.options.recognizers, function(item) {\n var recognizer = this.add(new (item[0])(item[1]));\n item[2] && recognizer.recognizeWith(item[2]);\n item[3] && recognizer.requireFailure(item[3]);\n }, this);\n}\n\nManager.prototype = {\n /**\n * set options\n * @param {Object} options\n * @returns {Manager}\n */\n set: function(options) {\n assign(this.options, options);\n\n // Options that need a little more setup\n if (options.touchAction) {\n this.touchAction.update();\n }\n if (options.inputTarget) {\n // Clean up existing event listeners and reinitialize\n this.input.destroy();\n this.input.target = options.inputTarget;\n this.input.init();\n }\n return this;\n },\n\n /**\n * stop recognizing for this session.\n * This session will be discarded, when a new [input]start event is fired.\n * When forced, the recognizer cycle is stopped immediately.\n * @param {Boolean} [force]\n */\n stop: function(force) {\n this.session.stopped = force ? FORCED_STOP : STOP;\n },\n\n /**\n * run the recognizers!\n * called by the inputHandler function on every movement of the pointers (touches)\n * it walks through all the recognizers and tries to detect the gesture that is being made\n * @param {Object} inputData\n */\n recognize: function(inputData) {\n var session = this.session;\n if (session.stopped) {\n return;\n }\n\n // run the touch-action polyfill\n this.touchAction.preventDefaults(inputData);\n\n var recognizer;\n var recognizers = this.recognizers;\n\n // this holds the recognizer that is being recognized.\n // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED\n // if no recognizer is detecting a thing, it is set to `null`\n var curRecognizer = session.curRecognizer;\n\n // reset when the last recognizer is recognized\n // or when we're in a new session\n if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {\n curRecognizer = session.curRecognizer = null;\n }\n\n var i = 0;\n while (i < recognizers.length) {\n recognizer = recognizers[i];\n\n // find out if we are allowed try to recognize the input for this one.\n // 1. allow if the session is NOT forced stopped (see the .stop() method)\n // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one\n // that is being recognized.\n // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.\n // this can be setup with the `recognizeWith()` method on the recognizer.\n if (session.stopped !== FORCED_STOP && ( // 1\n !curRecognizer || recognizer == curRecognizer || // 2\n recognizer.canRecognizeWith(curRecognizer))) { // 3\n recognizer.recognize(inputData);\n } else {\n recognizer.reset();\n }\n\n // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the\n // current active recognizer. but only if we don't already have an active recognizer\n if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {\n curRecognizer = session.curRecognizer = recognizer;\n }\n i++;\n }\n },\n\n /**\n * get a recognizer by its event name.\n * @param {Recognizer|String} recognizer\n * @returns {Recognizer|Null}\n */\n get: function(recognizer) {\n if (recognizer instanceof Recognizer) {\n return recognizer;\n }\n\n var recognizers = this.recognizers;\n for (var i = 0; i < recognizers.length; i++) {\n if (recognizers[i].options.event == recognizer) {\n return recognizers[i];\n }\n }\n return null;\n },\n\n /**\n * add a recognizer to the manager\n * existing recognizers with the same event name will be removed\n * @param {Recognizer} recognizer\n * @returns {Recognizer|Manager}\n */\n add: function(recognizer) {\n if (invokeArrayArg(recognizer, 'add', this)) {\n return this;\n }\n\n // remove existing\n var existing = this.get(recognizer.options.event);\n if (existing) {\n this.remove(existing);\n }\n\n this.recognizers.push(recognizer);\n recognizer.manager = this;\n\n this.touchAction.update();\n return recognizer;\n },\n\n /**\n * remove a recognizer by name or instance\n * @param {Recognizer|String} recognizer\n * @returns {Manager}\n */\n remove: function(recognizer) {\n if (invokeArrayArg(recognizer, 'remove', this)) {\n return this;\n }\n\n recognizer = this.get(recognizer);\n\n // let's make sure this recognizer exists\n if (recognizer) {\n var recognizers = this.recognizers;\n var index = inArray(recognizers, recognizer);\n\n if (index !== -1) {\n recognizers.splice(index, 1);\n this.touchAction.update();\n }\n }\n\n return this;\n },\n\n /**\n * bind event\n * @param {String} events\n * @param {Function} handler\n * @returns {EventEmitter} this\n */\n on: function(events, handler) {\n if (events === undefined) {\n return;\n }\n if (handler === undefined) {\n return;\n }\n\n var handlers = this.handlers;\n each(splitStr(events), function(event) {\n handlers[event] = handlers[event] || [];\n handlers[event].push(handler);\n });\n return this;\n },\n\n /**\n * unbind event, leave emit blank to remove all handlers\n * @param {String} events\n * @param {Function} [handler]\n * @returns {EventEmitter} this\n */\n off: function(events, handler) {\n if (events === undefined) {\n return;\n }\n\n var handlers = this.handlers;\n each(splitStr(events), function(event) {\n if (!handler) {\n delete handlers[event];\n } else {\n handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);\n }\n });\n return this;\n },\n\n /**\n * emit event to the listeners\n * @param {String} event\n * @param {Object} data\n */\n emit: function(event, data) {\n // we also want to trigger dom events\n if (this.options.domEvents) {\n triggerDomEvent(event, data);\n }\n\n // no handlers, so skip it all\n var handlers = this.handlers[event] && this.handlers[event].slice();\n if (!handlers || !handlers.length) {\n return;\n }\n\n data.type = event;\n data.preventDefault = function() {\n data.srcEvent.preventDefault();\n };\n\n var i = 0;\n while (i < handlers.length) {\n handlers[i](data);\n i++;\n }\n },\n\n /**\n * destroy the manager and unbinds all events\n * it doesn't unbind dom events, that is the user own responsibility\n */\n destroy: function() {\n this.element && toggleCssProps(this, false);\n\n this.handlers = {};\n this.session = {};\n this.input.destroy();\n this.element = null;\n }\n};\n\n/**\n * add/remove the css properties as defined in manager.options.cssProps\n * @param {Manager} manager\n * @param {Boolean} add\n */\nfunction toggleCssProps(manager, add) {\n var element = manager.element;\n if (!element.style) {\n return;\n }\n var prop;\n each(manager.options.cssProps, function(value, name) {\n prop = prefixed(element.style, name);\n if (add) {\n manager.oldCssProps[prop] = element.style[prop];\n element.style[prop] = value;\n } else {\n element.style[prop] = manager.oldCssProps[prop] || '';\n }\n });\n if (!add) {\n manager.oldCssProps = {};\n }\n}\n\n/**\n * trigger dom event\n * @param {String} event\n * @param {Object} data\n */\nfunction triggerDomEvent(event, data) {\n var gestureEvent = document.createEvent('Event');\n gestureEvent.initEvent(event, true, true);\n gestureEvent.gesture = data;\n data.target.dispatchEvent(gestureEvent);\n}\n\nassign(Hammer, {\n INPUT_START: INPUT_START,\n INPUT_MOVE: INPUT_MOVE,\n INPUT_END: INPUT_END,\n INPUT_CANCEL: INPUT_CANCEL,\n\n STATE_POSSIBLE: STATE_POSSIBLE,\n STATE_BEGAN: STATE_BEGAN,\n STATE_CHANGED: STATE_CHANGED,\n STATE_ENDED: STATE_ENDED,\n STATE_RECOGNIZED: STATE_RECOGNIZED,\n STATE_CANCELLED: STATE_CANCELLED,\n STATE_FAILED: STATE_FAILED,\n\n DIRECTION_NONE: DIRECTION_NONE,\n DIRECTION_LEFT: DIRECTION_LEFT,\n DIRECTION_RIGHT: DIRECTION_RIGHT,\n DIRECTION_UP: DIRECTION_UP,\n DIRECTION_DOWN: DIRECTION_DOWN,\n DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,\n DIRECTION_VERTICAL: DIRECTION_VERTICAL,\n DIRECTION_ALL: DIRECTION_ALL,\n\n Manager: Manager,\n Input: Input,\n TouchAction: TouchAction,\n\n TouchInput: TouchInput,\n MouseInput: MouseInput,\n PointerEventInput: PointerEventInput,\n TouchMouseInput: TouchMouseInput,\n SingleTouchInput: SingleTouchInput,\n\n Recognizer: Recognizer,\n AttrRecognizer: AttrRecognizer,\n Tap: TapRecognizer,\n Pan: PanRecognizer,\n Swipe: SwipeRecognizer,\n Pinch: PinchRecognizer,\n Rotate: RotateRecognizer,\n Press: PressRecognizer,\n\n on: addEventListeners,\n off: removeEventListeners,\n each: each,\n merge: merge,\n extend: extend,\n assign: assign,\n inherit: inherit,\n bindFn: bindFn,\n prefixed: prefixed\n});\n\n// this prevents errors when Hammer is loaded in the presence of an AMD\n// style loader but by script tag, not by the loader.\nvar freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line\nfreeGlobal.Hammer = Hammer;\n\nif (typeof define === 'function' && define.amd) {\n define(function() {\n return Hammer;\n });\n} else if (typeof module != 'undefined' && module.exports) {\n module.exports = Hammer;\n} else {\n window[exportName] = Hammer;\n}\n\n})(window, document, 'Hammer');\n","import { LabeledButton } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * @typedef {import('../util/buckets').Bucket} Bucket\n * @typedef {import(\"preact\").ComponentChildren} Children\n */\n\n/**\n * Render a set of buckets in a vertical channel positioned along the edge of\n * the sidebar.\n *\n * @param {object} props\n * @param {Children} props.children\n */\nfunction BucketList({ children }) {\n return (\n <ul\n className={classnames(\n // 2020-11-20: Making bucket bar one pixel wider (23px vs 22px) is an\n // interim and pragmatic solution for an apparent glitch on\n // Safari and Chrome. Adding one pixel resolves this issue:\n // https://github.com/hypothesis/client/pull/2750\n 'absolute w-[23px] left-[-22px] h-full',\n // The background is set to low opacity when the sidebar is collapsed.\n 'bg-grey-2 sidebar-collapsed:bg-black/[.08]',\n // Disable pointer events along the sidebar itself; re-enable them in\n // bucket indicator buttons\n 'pointer-events-none'\n )}\n >\n {children}\n </ul>\n );\n}\n\n/**\n * Render a vertically-positioned bucket-list item.\n *\n * @param {object} props\n * @param {Children} props.children\n * @param {number} props.topPosition - The vertical top position, in pixels,\n * for this bucket item relative to the top of the containing BucketList\n */\nfunction BucketItem({ children, topPosition }) {\n return (\n <li\n className={classnames(\n 'absolute right-0',\n // Re-enable pointer events, which are disabled on the containing list\n 'pointer-events-auto'\n )}\n style={{ top: topPosition }}\n >\n {children}\n </li>\n );\n}\n\n/**\n * A list of buckets, including up and down navigation (when applicable) and\n * on-screen buckets\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n *\n * @param {object} props\n * @param {Bucket} props.above\n * @param {Bucket} props.below\n * @param {Bucket[]} props.buckets\n * @param {(tags: string[]) => void} props.onFocusAnnotations\n * @param {(tags: string[], direction: 'down'|'up') => void} props.onScrollToClosestOffScreenAnchor\n * @param {(tags: string[], toggle: boolean) => void} props.onSelectAnnotations\n */\nexport default function Buckets({\n above,\n below,\n buckets,\n onFocusAnnotations,\n onScrollToClosestOffScreenAnchor,\n onSelectAnnotations,\n}) {\n const showUpNavigation = above.tags.size > 0;\n const showDownNavigation = below.tags.size > 0;\n\n return (\n <BucketList>\n {showUpNavigation && (\n <BucketItem topPosition={above.position}>\n <LabeledButton\n className={classnames(\n 'BucketButton UpBucketButton',\n // Center the button vertically at `above.position` by pulling\n // its top margin up by about half the button's height.\n // This puts it nearer the toolbar's other buttons above the\n // bucket list.\n 'right-0 mt-[-11px]'\n )}\n data-testid=\"up-navigation-button\"\n onClick={() =>\n onScrollToClosestOffScreenAnchor([...above.tags], 'up')\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...above.tags])}\n onMouseEnter={() => onFocusAnnotations([...above.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${above.tags.size})`}\n >\n {above.tags.size}\n </LabeledButton>\n </BucketItem>\n )}\n {buckets.map((bucket, index) => (\n <BucketItem topPosition={bucket.position} key={index}>\n <LabeledButton\n className={classnames(\n 'BucketButton LeftBucketButton',\n // Center the bucket indicator button vertically on `bucket.position`\n // by pulling it by half the height of the button\n 'right-0 mt-[-8px]'\n )}\n onClick={event =>\n onSelectAnnotations(\n [...bucket.tags],\n event.metaKey || event.ctrlKey\n )\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...bucket.tags])}\n onMouseEnter={() => onFocusAnnotations([...bucket.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Select nearby annotations (${bucket.tags.size})`}\n >\n {bucket.tags.size}\n </LabeledButton>\n </BucketItem>\n ))}\n {showDownNavigation && (\n <BucketItem topPosition={below.position}>\n <LabeledButton\n className=\"BucketButton DownBucketButton right-0\"\n data-testid=\"down-navigation-button\"\n onClick={() =>\n onScrollToClosestOffScreenAnchor([...below.tags], 'down')\n }\n onBlur={() => onFocusAnnotations([])}\n onFocus={() => onFocusAnnotations([...below.tags])}\n onMouseEnter={() => onFocusAnnotations([...below.tags])}\n onMouseOut={() => onFocusAnnotations([])}\n title={`Go up to next annotations (${below.tags.size})`}\n >\n {below.tags.size}\n </LabeledButton>\n </BucketItem>\n )}\n </BucketList>\n );\n}\n","import { render } from 'preact';\n\nimport Buckets from './components/Buckets';\nimport { computeBuckets } from './util/buckets';\n\n/**\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n */\n\n/**\n * Controller for the \"bucket bar\" shown alongside the sidebar indicating where\n * annotations are in the document.\n *\n * @implements {Destroyable}\n */\nexport class BucketBar {\n /**\n * @param {HTMLElement} container\n * @param {object} options\n * @param {(tags: string[]) => void} options.onFocusAnnotations\n * @param {(tags: string[], direction: 'down'|'up') => void} options.onScrollToClosestOffScreenAnchor\n * @param {(tags: string[], toggle: boolean) => void} options.onSelectAnnotations\n */\n constructor(\n container,\n {\n onFocusAnnotations,\n onScrollToClosestOffScreenAnchor,\n onSelectAnnotations,\n }\n ) {\n this._bucketsContainer = document.createElement('div');\n container.appendChild(this._bucketsContainer);\n\n this._onFocusAnnotations = onFocusAnnotations;\n this._onScrollToClosestOffScreenAnchor = onScrollToClosestOffScreenAnchor;\n this._onSelectAnnotations = onSelectAnnotations;\n\n // Immediately render the bucket bar\n this.update([]);\n }\n\n destroy() {\n render(null, this._bucketsContainer);\n this._bucketsContainer.remove();\n }\n\n /**\n * @param {AnchorPosition[]} positions\n */\n update(positions) {\n const buckets = computeBuckets(positions);\n render(\n <Buckets\n above={buckets.above}\n below={buckets.below}\n buckets={buckets.buckets}\n onFocusAnnotations={tags => this._onFocusAnnotations(tags)}\n onScrollToClosestOffScreenAnchor={(tags, direction) =>\n this._onScrollToClosestOffScreenAnchor(tags, direction)\n }\n onSelectAnnotations={(tags, toogle) =>\n this._onSelectAnnotations(tags, toogle)\n }\n />,\n this._bucketsContainer\n );\n }\n}\n","import { IconButton } from '@hypothesis/frontend-shared';\nimport classnames from 'classnames';\n\n/**\n * @typedef {import(\"@hypothesis/frontend-shared/lib/components/buttons\").IconButtonProps} IconButtonProps\n *\n * @typedef {Omit<IconButtonProps, \"className\">} ToolbarButtonProps\n */\n\n/**\n * Style an IconButton for use on the Toolbar\n *\n * @param {ToolbarButtonProps} props\n */\nfunction ToolbarButton({ ...buttonProps }) {\n const { icon, title, ...restProps } = buttonProps;\n return (\n <IconButton\n className={classnames(\n 'w-[30px] h-[30px]', // These buttons have precise dimensions\n 'rounded-px', // size of border radius in absolute units\n 'flex items-center justify-center',\n 'border bg-white text-grey-6 hover:text-grey-9',\n 'shadow transition-colors'\n )}\n icon={icon}\n title={title}\n {...restProps}\n />\n );\n}\n\n/**\n * @typedef ToolbarProps\n *\n * @prop {() => void} closeSidebar -\n * Callback for the \"Close sidebar\" button. This button is only shown when\n * `useMinimalControls` is true and the sidebar is open.\n * @prop {() => void} createAnnotation -\n * Callback for the \"Create annotation\" / \"Create page note\" button. The type\n * of annotation depends on whether there is a text selection and is decided\n * by the caller.\n * @prop {boolean} isSidebarOpen - Is the sidebar currently visible?\n * @prop {'annotation'|'note'} newAnnotationType -\n * Icon to show on the \"Create annotation\" button indicating what kind of annotation\n * will be created.\n * @prop {boolean} showHighlights - Are highlights currently visible in the document?\n * @prop {() => void} toggleHighlights -\n * Callback to toggle visibility of highlights in the document.\n * @prop {() => void} toggleSidebar -\n * Callback to toggle the visibility of the sidebar.\n * @prop {import(\"preact\").Ref<HTMLButtonElement>} [toggleSidebarRef] -\n * Ref that gets set to the toolbar button for toggling the sidebar.\n * This is exposed to enable the drag-to-resize functionality of this\n * button.\n * @prop {boolean} [useMinimalControls] -\n * If true, all controls are hidden except for the \"Close sidebar\" button\n * when the sidebar is open. This is enabled in the \"clean\" theme.\n */\n\n/**\n * Controls on the edge of the sidebar for opening/closing the sidebar,\n * controlling highlight visibility and creating new page notes.\n *\n * This component and its buttons are sized with absolute units such that they\n * don't scale with changes to the host page's root font size. They will still\n * properly scale with user/browser zooming.\n *\n * @param {ToolbarProps} props\n */\nexport default function Toolbar({\n closeSidebar,\n createAnnotation,\n isSidebarOpen,\n newAnnotationType,\n showHighlights,\n toggleHighlights,\n toggleSidebar,\n toggleSidebarRef,\n useMinimalControls = false,\n}) {\n return (\n <div\n className={classnames(\n 'absolute left-[-33px] w-[33px] z-2',\n 'text-px-base leading-none' // non-scaling sizing\n )}\n >\n {/* In the clean theme (`useMinimalControls` is `true`),\n the only button that should appear is a button\n to close the sidebar, and only if the sidebar is open. This button is\n absolutely positioned some way down the edge of the sidebar.\n */}\n {useMinimalControls && isSidebarOpen && (\n <IconButton\n className={classnames(\n 'w-[27px] h-[27px] mt-[140px] ml-px-1.5',\n 'flex items-center justify-center bg-white border',\n 'text-grey-6 hover:text-grey-9 transition-colors',\n // Turn off right border to blend with sidebar\n 'border-r-0',\n // A more intense shadow than other ToolbarButtons, to match that\n // of the edge of the sidebar in clean theme\n 'shadow-sidebar'\n )}\n title=\"Close annotation sidebar\"\n icon=\"cancel\"\n onClick={closeSidebar}\n />\n )}\n {!useMinimalControls && (\n <>\n <IconButton\n className={classnames(\n // Height and width to align with the sidebar's top bar\n 'h-[40px] w-[33px] pl-px-1.5',\n 'bg-white text-grey-5 hover:text-grey-9',\n // Turn on left and bottom borders to continue the\n // border of the sidebar's top bar\n 'border-l border-b'\n )}\n buttonRef={toggleSidebarRef}\n title=\"Annotation sidebar\"\n icon={isSidebarOpen ? 'caret-right' : 'caret-left'}\n expanded={isSidebarOpen}\n pressed={isSidebarOpen}\n onClick={toggleSidebar}\n />\n <div className=\"space-y-px-1.5 mt-px-2\">\n <ToolbarButton\n title=\"Show highlights\"\n icon={showHighlights ? 'show' : 'hide'}\n selected={showHighlights}\n onClick={toggleHighlights}\n />\n <ToolbarButton\n title={\n newAnnotationType === 'note'\n ? 'New page note'\n : 'New annotation'\n }\n icon={newAnnotationType === 'note' ? 'note' : 'annotate'}\n onClick={createAnnotation}\n />\n </div>\n </>\n )}\n </div>\n );\n}\n","import { createRef, render } from 'preact';\n\nimport Toolbar from './components/Toolbar';\n\n/**\n * @typedef ToolbarOptions\n * @prop {() => void} createAnnotation\n * @prop {(open: boolean) => void} setSidebarOpen\n * @prop {(visible: boolean) => void} setHighlightsVisible\n */\n\n/**\n * Controller for the toolbar on the edge of the sidebar.\n *\n * This toolbar provides controls for opening and closing the sidebar, toggling\n * highlight visibility etc.\n */\nexport class ToolbarController {\n /**\n * @param {HTMLElement} container - Element into which the toolbar is rendered\n * @param {ToolbarOptions} options\n */\n constructor(container, options) {\n const { createAnnotation, setSidebarOpen, setHighlightsVisible } = options;\n\n this._container = container;\n\n this._useMinimalControls = false;\n\n /** @type {'annotation'|'note'} */\n this._newAnnotationType = 'note';\n\n this._highlightsVisible = false;\n this._sidebarOpen = false;\n\n this._closeSidebar = () => setSidebarOpen(false);\n this._toggleSidebar = () => setSidebarOpen(!this._sidebarOpen);\n this._toggleHighlights = () =>\n setHighlightsVisible(!this._highlightsVisible);\n this._createAnnotation = () => {\n createAnnotation();\n setSidebarOpen(true);\n };\n\n /** Reference to the sidebar toggle button. */\n this._sidebarToggleButton = createRef();\n\n this.render();\n }\n\n getWidth() {\n const content = /** @type {HTMLElement} */ (this._container.firstChild);\n return content.getBoundingClientRect().width;\n }\n\n /**\n * Set whether the toolbar is in the \"minimal controls\" mode where\n * only the \"Close\" button is shown.\n */\n set useMinimalControls(minimal) {\n this._useMinimalControls = minimal;\n this.render();\n }\n\n get useMinimalControls() {\n return this._useMinimalControls;\n }\n\n /**\n * Update the toolbar to reflect whether the sidebar is open or not.\n */\n set sidebarOpen(open) {\n this._sidebarOpen = open;\n this.render();\n }\n\n get sidebarOpen() {\n return this._sidebarOpen;\n }\n\n /**\n * Update the toolbar to reflect whether the \"Create annotation\" button will\n * create a page note (if there is no selection) or an annotation (if there is\n * a selection).\n */\n set newAnnotationType(type) {\n this._newAnnotationType = type;\n this.render();\n }\n\n get newAnnotationType() {\n return this._newAnnotationType;\n }\n\n /**\n * Update the toolbar to reflect whether highlights are currently visible.\n */\n set highlightsVisible(visible) {\n this._highlightsVisible = visible;\n this.render();\n }\n\n get highlightsVisible() {\n return this._highlightsVisible;\n }\n\n /**\n * Return the DOM element that toggles the sidebar's visibility.\n *\n * @type {HTMLButtonElement}\n */\n get sidebarToggleButton() {\n return this._sidebarToggleButton.current;\n }\n\n render() {\n render(\n <Toolbar\n closeSidebar={this._closeSidebar}\n createAnnotation={this._createAnnotation}\n newAnnotationType={this._newAnnotationType}\n isSidebarOpen={this._sidebarOpen}\n showHighlights={this._highlightsVisible}\n toggleHighlights={this._toggleHighlights}\n toggleSidebar={this._toggleSidebar}\n toggleSidebarRef={this._sidebarToggleButton}\n useMinimalControls={this.useMinimalControls}\n />,\n this._container\n );\n }\n}\n","import * as Hammer from 'hammerjs';\n\nimport { addConfigFragment } from '../shared/config-fragment';\nimport { sendErrorsTo } from '../shared/frame-error-capture';\nimport { ListenerCollection } from '../shared/listener-collection';\nimport { PortRPC } from '../shared/messaging';\n\nimport { annotationCounts } from './annotation-counts';\nimport { BucketBar } from './bucket-bar';\nimport { createAppConfig } from './config/app';\nimport { FeatureFlags } from './features';\nimport { sidebarTrigger } from './sidebar-trigger';\nimport { ToolbarController } from './toolbar';\nimport { createShadowRoot } from './util/shadow-root';\n\n/**\n * @typedef {import('./guest').Guest} Guest\n * @typedef {import('../types/annotator').AnchorPosition} AnchorPosition\n * @typedef {import('../types/annotator').SidebarLayout} SidebarLayout\n * @typedef {import('../types/annotator').Destroyable} Destroyable\n * @typedef {import('../types/config').Service} Service\n * @typedef {import('../types/port-rpc-events').GuestToHostEvent} GuestToHostEvent\n * @typedef {import('../types/port-rpc-events').HostToGuestEvent} HostToGuestEvent\n * @typedef {import('../types/port-rpc-events').HostToSidebarEvent} HostToSidebarEvent\n * @typedef {import('../types/port-rpc-events').SidebarToHostEvent} SidebarToHostEvent\n */\n\n// Minimum width to which the iframeContainer can be resized.\nexport const MIN_RESIZE = 280;\n\n/**\n * Client configuration used to launch the sidebar application.\n *\n * This includes the URL for the iframe and configuration to pass to the\n * application on launch.\n *\n * @typedef {{ sidebarAppUrl: string } & Record<string, unknown>} SidebarConfig\n */\n\n/**\n * Client configuration used by the sidebar container ({@link Sidebar}).\n *\n * @typedef SidebarContainerConfig\n * @prop {Service[]} [services] - Details of the annotation service the\n * client should connect to. This includes callbacks provided by the host\n * page to handle certain actions in the sidebar (eg. the Login button).\n * @prop {string} [externalContainerSelector] - CSS selector of a container\n * element in the host page which the sidebar should be added into, instead\n * of creating a new container.\n * @prop {(layout: SidebarLayout) => void} [onLayoutChange] - Callback that\n * allows the host page to react to the sidebar being opened, closed or\n * resized\n */\n\n/**\n * Create the iframe that will load the sidebar application.\n *\n * @param {SidebarConfig} config\n * @return {HTMLIFrameElement}\n */\nfunction createSidebarIframe(config) {\n const sidebarURL = /** @type {string} */ (config.sidebarAppUrl);\n const sidebarAppSrc = addConfigFragment(\n sidebarURL,\n createAppConfig(sidebarURL, config)\n );\n\n const sidebarFrame = document.createElement('iframe');\n\n // Enable media in annotations to be shown fullscreen\n sidebarFrame.setAttribute('allowfullscreen', '');\n\n sidebarFrame.src = sidebarAppSrc;\n sidebarFrame.title = 'Hypothesis annotation viewer';\n sidebarFrame.className = 'sidebar-frame';\n\n return sidebarFrame;\n}\n\n/**\n * The `Sidebar` class creates (1) the sidebar application iframe, (2) its container,\n * as well as (3) the adjacent controls.\n *\n * @implements {Destroyable}\n */\nexport class Sidebar {\n /**\n * @param {HTMLElement} element\n * @param {import('./util/emitter').EventBus} eventBus -\n * Enables communication between components sharing the same eventBus\n * @param {SidebarContainerConfig & SidebarConfig} config\n */\n constructor(element, eventBus, config) {\n this._emitter = eventBus.createEmitter();\n\n /**\n * Tracks which `Guest` has a text selection. `null` indicates to default\n * to the first connected guest frame.\n *\n * @type {PortRPC<GuestToHostEvent, HostToGuestEvent>|null}\n */\n this._guestWithSelection = null;\n\n /**\n * Channels for host-guest communication.\n *\n * @type {PortRPC<GuestToHostEvent, HostToGuestEvent>[]}\n */\n this._guestRPC = [];\n\n /**\n * Channel for host-sidebar communication.\n *\n * @type {PortRPC<SidebarToHostEvent, HostToSidebarEvent>}\n */\n this._sidebarRPC = new PortRPC();\n\n /**\n * The `<iframe>` element containing the sidebar application.\n */\n this.iframe = createSidebarIframe(config);\n\n this._config = config;\n\n /** @type {BucketBar|null} */\n this.bucketBar = null;\n\n this.features = new FeatureFlags();\n\n if (config.externalContainerSelector) {\n this.externalFrame =\n /** @type {HTMLElement} */\n (document.querySelector(config.externalContainerSelector)) ?? element;\n this.externalFrame.appendChild(this.iframe);\n } else {\n this.iframeContainer = document.createElement('div');\n this.iframeContainer.style.display = 'none';\n this.iframeContainer.className = 'sidebar-container';\n\n if (config.theme === 'clean') {\n this.iframeContainer.classList.add('theme-clean');\n } else {\n this.bucketBar = new BucketBar(this.iframeContainer, {\n onFocusAnnotations: tags =>\n this._guestRPC.forEach(rpc => rpc.call('focusAnnotations', tags)),\n onScrollToClosestOffScreenAnchor: (tags, direction) =>\n this._guestRPC.forEach(rpc =>\n rpc.call('scrollToClosestOffScreenAnchor', tags, direction)\n ),\n onSelectAnnotations: (tags, toggle) =>\n this._guestRPC.forEach(rpc =>\n rpc.call('selectAnnotations', tags, toggle)\n ),\n });\n }\n\n this.iframeContainer.appendChild(this.iframe);\n\n // Wrap up the 'iframeContainer' element into a shadow DOM so it is not affected by host CSS styles\n this.hypothesisSidebar = document.createElement('hypothesis-sidebar');\n const shadowRoot = createShadowRoot(this.hypothesisSidebar);\n shadowRoot.appendChild(this.iframeContainer);\n\n element.appendChild(this.hypothesisSidebar);\n }\n\n // Register the sidebar as a handler for Hypothesis errors in this frame.\n if (this.iframe.contentWindow) {\n sendErrorsTo(this.iframe.contentWindow);\n }\n\n this._listeners = new ListenerCollection();\n\n // Set up the toolbar on the left edge of the sidebar.\n const toolbarContainer = document.createElement('div');\n this.toolbar = new ToolbarController(toolbarContainer, {\n createAnnotation: () => {\n if (this._guestRPC.length === 0) {\n return;\n }\n\n const rpc = this._guestWithSelection ?? this._guestRPC[0];\n rpc.call('createAnnotation');\n },\n setSidebarOpen: open => (open ? this.open() : this.close()),\n setHighlightsVisible: show => this.setHighlightsVisible(show),\n });\n\n if (config.theme === 'clean') {\n this.toolbar.useMinimalControls = true;\n } else {\n this.toolbar.useMinimalControls = false;\n }\n\n if (this.iframeContainer) {\n // If using our own container frame for the sidebar, add the toolbar to it.\n this.iframeContainer.prepend(toolbarContainer);\n this.toolbarWidth = this.toolbar.getWidth();\n } else {\n // If using a host-page provided container for the sidebar, the toolbar is\n // not shown.\n this.toolbarWidth = 0;\n }\n\n this._listeners.add(window, 'resize', () => this._onResize());\n\n this._gestureState = {\n // Initial position at the start of a drag/pan resize event (in pixels).\n initial: /** @type {number|null} */ (null),\n\n // Final position at end of drag resize event.\n final: /** @type {number|null} */ (null),\n };\n this._setupGestures();\n this.close();\n\n // Publisher-provided callback functions\n const [serviceConfig] = config.services || [];\n if (serviceConfig) {\n this.onLoginRequest = serviceConfig.onLoginRequest;\n this.onLogoutRequest = serviceConfig.onLogoutRequest;\n this.onSignupRequest = serviceConfig.onSignupRequest;\n this.onProfileRequest = serviceConfig.onProfileRequest;\n this.onHelpRequest = serviceConfig.onHelpRequest;\n }\n\n this.onLayoutChange = config.onLayoutChange;\n\n /** @type {SidebarLayout} */\n this._layoutState = {\n expanded: false,\n width: 0,\n toolbarWidth: 0,\n };\n\n // Initial layout notification\n this._updateLayoutState(false);\n this._setupSidebarEvents();\n }\n\n destroy() {\n this._guestRPC.forEach(rpc => rpc.destroy());\n this._sidebarRPC.destroy();\n this.bucketBar?.destroy();\n this._listeners.removeAll();\n this._hammerManager?.destroy();\n if (this.hypothesisSidebar) {\n this.hypothesisSidebar.remove();\n } else {\n this.iframe.remove();\n }\n this._emitter.destroy();\n\n // Unregister the sidebar iframe as a handler for errors in this frame.\n sendErrorsTo(null);\n }\n\n /**\n * Setup communication with a frame that has connected to the host.\n *\n * @param {'guest'|'sidebar'} source\n * @param {MessagePort} port\n */\n onFrameConnected(source, port) {\n switch (source) {\n case 'guest':\n this._connectGuest(port);\n break;\n case 'sidebar':\n this._sidebarRPC.connect(port);\n break;\n }\n }\n\n /**\n * @param {MessagePort} port\n */\n _connectGuest(port) {\n /** @type {PortRPC<GuestToHostEvent, HostToGuestEvent>} */\n const guestRPC = new PortRPC();\n\n guestRPC.on('textSelected', () => {\n this._guestWithSelection = guestRPC;\n this.toolbar.newAnnotationType = 'annotation';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n guestRPC.on('textUnselected', () => {\n this._guestWithSelection = null;\n this.toolbar.newAnnotationType = 'note';\n this._guestRPC\n .filter(port => port !== guestRPC)\n .forEach(rpc => rpc.call('clearSelection'));\n });\n\n // The listener will do nothing if the sidebar doesn't have a bucket bar\n // (clean theme)\n const bucketBar = this.bucketBar;\n // Currently, we ignore `anchorsChanged` for all the guests except the first connected guest.\n if (bucketBar) {\n guestRPC.on(\n 'anchorsChanged',\n /** @param {AnchorPosition[]} positions */\n positions => {\n if (this._guestRPC.indexOf(guestRPC) === 0) {\n bucketBar.update(positions);\n }\n }\n );\n }\n\n guestRPC.on('close', () => {\n guestRPC.destroy();\n if (guestRPC === this._guestWithSelection) {\n this._guestWithSelection = null;\n }\n this._guestRPC = this._guestRPC.filter(rpc => rpc !== guestRPC);\n });\n\n guestRPC.connect(port);\n this._guestRPC.push(guestRPC);\n\n guestRPC.call('sidebarLayoutChanged', this._layoutState);\n }\n\n _setupSidebarEvents() {\n annotationCounts(document.body, this._sidebarRPC);\n sidebarTrigger(document.body, () => this.open());\n\n this._sidebarRPC.on(\n 'featureFlagsUpdated',\n /** @param {Record<string, boolean>} flags */ flags =>\n this.features.update(flags)\n );\n\n this._sidebarRPC.on('connect', () => {\n // Show the UI\n if (this.iframeContainer) {\n this.iframeContainer.style.display = '';\n }\n\n const showHighlights = this._config.showHighlights === 'always';\n this.setHighlightsVisible(showHighlights);\n\n if (\n this._config.openSidebar ||\n this._config.annotations ||\n this._config.query ||\n this._config.group\n ) {\n this.open();\n }\n });\n\n this._sidebarRPC.on('showHighlights', () =>\n this.setHighlightsVisible(true)\n );\n\n this._sidebarRPC.on('openSidebar', () => this.open());\n\n this._sidebarRPC.on('closeSidebar', () => this.close());\n\n // Sidebar listens to the `openNotebook` event coming from the sidebar's\n // iframe and re-publishes it via the emitter to the Notebook\n this._sidebarRPC.on(\n 'openNotebook',\n /** @param {string} groupId */\n groupId => {\n this.hide();\n this._emitter.publish('openNotebook', groupId);\n }\n );\n\n this._emitter.subscribe('closeNotebook', () => {\n this.show();\n });\n\n /** @type {Array<[SidebarToHostEvent, Function|undefined]>} */\n const eventHandlers = [\n ['loginRequested', this.onLoginRequest],\n ['logoutRequested', this.onLogoutRequest],\n ['signupRequested', this.onSignupRequest],\n ['profileRequested', this.onProfileRequest],\n ['helpRequested', this.onHelpRequest],\n ];\n eventHandlers.forEach(([event, handler]) => {\n if (handler) {\n this._sidebarRPC.on(event, () => handler());\n }\n });\n }\n\n _resetGestureState() {\n this._gestureState = { initial: null, final: null };\n }\n\n _setupGestures() {\n const toggleButton = this.toolbar.sidebarToggleButton;\n if (toggleButton) {\n this._hammerManager = new Hammer.Manager(toggleButton);\n this._hammerManager.on(\n 'panstart panend panleft panright',\n /* istanbul ignore next */\n event => this._onPan(event)\n );\n this._hammerManager.add(\n new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL })\n );\n }\n }\n\n // Schedule any changes needed to update the sidebar layout.\n _updateLayout() {\n // Only schedule one frame at a time.\n if (this.renderFrame) {\n return;\n }\n\n // Schedule a frame.\n this.renderFrame = requestAnimationFrame(() => {\n this.renderFrame = null;\n\n if (\n this._gestureState.final !== this._gestureState.initial &&\n this.iframeContainer\n ) {\n const margin = /** @type {number} */ (this._gestureState.final);\n const width = -margin;\n this.iframeContainer.style.marginLeft = `${margin}px`;\n if (width >= MIN_RESIZE) {\n this.iframeContainer.style.width = `${width}px`;\n }\n this._updateLayoutState();\n }\n });\n }\n\n /**\n * Update the current layout state and notify the embedder if they provided\n * an `onLayoutChange` callback in the Hypothesis config, as well as guests\n * so they can enable/adapt side-by-side mode.\n *\n * This is called when the sidebar is opened, closed or resized.\n *\n * @param {boolean} [expanded] -\n * `true` or `false` if the sidebar is being directly opened or closed, as\n * opposed to being resized via the sidebar's drag handles\n */\n _updateLayoutState(expanded) {\n // The sidebar structure is:\n //\n // [ Toolbar ][ ]\n // [ ---------- ][ Sidebar iframe container (@frame) ]\n // [ Bucket Bar ][ ]\n //\n // The sidebar iframe is hidden or shown by adjusting the left margin of\n // its container.\n\n const toolbarWidth = (this.iframeContainer && this.toolbar.getWidth()) || 0;\n const frame = /** @type {HTMLElement} */ (\n this.iframeContainer ?? this.externalFrame\n );\n const rect = frame.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(frame);\n const width = parseInt(computedStyle.width);\n const leftMargin = parseInt(computedStyle.marginLeft);\n\n // The width of the sidebar that is visible on screen, including the\n // toolbar, which is always visible.\n let frameVisibleWidth = toolbarWidth;\n\n if (typeof expanded === 'boolean') {\n if (expanded) {\n frameVisibleWidth += width;\n }\n } else {\n if (leftMargin < MIN_RESIZE) {\n frameVisibleWidth -= leftMargin;\n } else {\n frameVisibleWidth += width;\n }\n\n // Infer expanded state based on whether at least part of the sidebar\n // frame is visible.\n expanded = frameVisibleWidth > toolbarWidth;\n }\n\n const layoutState = /** @type {SidebarLayout} */ ({\n expanded,\n width: expanded ? frameVisibleWidth : toolbarWidth,\n height: rect.height,\n toolbarWidth,\n });\n\n this._layoutState = layoutState;\n if (this.onLayoutChange) {\n this.onLayoutChange(layoutState);\n }\n\n this._guestRPC.forEach(rpc =>\n rpc.call('sidebarLayoutChanged', layoutState)\n );\n }\n\n /**\n * On window resize events, update the marginLeft of the sidebar by calling hide/show methods.\n */\n _onResize() {\n if (this.toolbar.sidebarOpen === true) {\n if (window.innerWidth < MIN_RESIZE) {\n this.close();\n } else {\n this.open();\n }\n }\n }\n\n /** @param {HammerInput} event */\n _onPan(event) {\n const frame = this.iframeContainer;\n if (!frame) {\n return;\n }\n\n switch (event.type) {\n case 'panstart':\n this._resetGestureState();\n\n // Disable animated transition of sidebar position\n frame.classList.add('sidebar-no-transition');\n\n // Disable pointer events on the iframe.\n frame.style.pointerEvents = 'none';\n\n this._gestureState.initial = parseInt(\n getComputedStyle(frame).marginLeft\n );\n\n break;\n case 'panend':\n frame.classList.remove('sidebar-no-transition');\n\n // Re-enable pointer events on the iframe.\n frame.style.pointerEvents = '';\n\n // Snap open or closed.\n if (\n this._gestureState.final === null ||\n this._gestureState.final <= -MIN_RESIZE\n ) {\n this.open();\n } else {\n this.close();\n }\n this._resetGestureState();\n break;\n case 'panleft':\n case 'panright': {\n if (typeof this._gestureState.initial !== 'number') {\n return;\n }\n\n const margin = this._gestureState.initial;\n const delta = event.deltaX;\n this._gestureState.final = Math.min(Math.round(margin + delta), 0);\n this._updateLayout();\n break;\n }\n }\n }\n\n open() {\n this._sidebarRPC.call('sidebarOpened');\n\n if (this.iframeContainer) {\n const width = this.iframeContainer.getBoundingClientRect().width;\n this.iframeContainer.style.marginLeft = `${-1 * width}px`;\n this.iframeContainer.classList.remove('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = true;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(true);\n }\n\n this._updateLayoutState(true);\n }\n\n close() {\n if (this.iframeContainer) {\n this.iframeContainer.style.marginLeft = '';\n this.iframeContainer.classList.add('sidebar-collapsed');\n }\n\n this.toolbar.sidebarOpen = false;\n\n if (this._config.showHighlights === 'whenSidebarOpen') {\n this.setHighlightsVisible(false);\n }\n\n this._updateLayoutState(false);\n }\n\n /**\n * Set whether highlights are visible in guest frames.\n *\n * @param {boolean} visible\n */\n setHighlightsVisible(visible) {\n this.toolbar.highlightsVisible = visible;\n\n // Notify sidebar app of change which will in turn reflect state to guest frames.\n this._sidebarRPC.call('setHighlightsVisible', visible);\n }\n\n /**\n * Shows the sidebar's controls\n */\n show() {\n if (this.iframeContainer) {\n this.iframeContainer.classList.remove('is-hidden');\n }\n }\n\n /**\n * Hides the sidebar's controls\n */\n hide() {\n if (this.iframeContainer) {\n this.iframeContainer.classList.add('is-hidden');\n }\n }\n}\n","const ANNOTATION_COUNT_ATTR = 'data-hypothesis-annotation-count';\n\n/**\n * Update the elements in the container element with the count data attribute\n * with the new annotation count. See:\n * https://h.readthedocs.io/projects/client/en/latest/publishers/host-page-integration/#cmdoption-arg-data-hypothesis-annotation-count\n *\n * @param {Element} rootEl - The DOM element which contains the elements that\n * display annotation count.\n * @param {import('../shared/messaging').PortRPC<'publicAnnotationCountChanged', string>} rpc - Channel for host-sidebar communication\n */\nexport function annotationCounts(rootEl, rpc) {\n rpc.on('publicAnnotationCountChanged', updateAnnotationCountElems);\n\n /** @param {number} newCount */\n function updateAnnotationCountElems(newCount) {\n const elems = rootEl.querySelectorAll('[' + ANNOTATION_COUNT_ATTR + ']');\n Array.from(elems).forEach(elem => {\n elem.textContent = newCount.toString();\n });\n }\n}\n","const SIDEBAR_TRIGGER_BTN_ATTR = 'data-hypothesis-trigger';\n\n/**\n * Show the sidebar when user clicks on an element with the\n * trigger data attribute.\n *\n * @param {Element} rootEl - The DOM element which contains the trigger elements.\n * @param {() => void} showFn - Function which shows the sidebar.\n */\nexport function sidebarTrigger(rootEl, showFn) {\n const triggerElems = rootEl.querySelectorAll(\n '[' + SIDEBAR_TRIGGER_BTN_ATTR + ']'\n );\n\n Array.from(triggerElems).forEach(triggerElem => {\n triggerElem.addEventListener('click', e => {\n showFn();\n e.stopPropagation();\n });\n });\n}\n","import { TinyEmitter } from 'tiny-emitter';\n\n/** @typedef {import('../../types/annotator').Destroyable} Destroyable */\n\n/**\n * Emitter is a communication class that implements the publisher/subscriber\n * pattern. It allows sending and listening events through a shared EventBus.\n * The different elements of the application can communicate with each other\n * without being tightly coupled.\n *\n * @implements {Destroyable}\n */\nexport class Emitter {\n /**\n * @param {TinyEmitter} emitter\n */\n constructor(emitter) {\n this._emitter = emitter;\n\n /** @type {[event: string, callback: Function][]} */\n this._subscriptions = [];\n }\n\n /**\n * Fire an event.\n *\n * @param {string} event\n * @param {unknown[]} args\n */\n publish(event, ...args) {\n this._emitter.emit(event, ...args);\n }\n\n /**\n * Register an event listener.\n *\n * @param {string} event\n * @param {Function} callback\n */\n subscribe(event, callback) {\n this._emitter.on(event, callback);\n this._subscriptions.push([event, callback]);\n }\n\n /**\n * Remove an event listener.\n *\n * @param {string} event\n * @param {Function} callback\n */\n unsubscribe(event, callback) {\n this._emitter.off(event, callback);\n this._subscriptions = this._subscriptions.filter(\n ([subEvent, subCallback]) =>\n subEvent !== event || subCallback !== callback\n );\n }\n\n /**\n * Remove all event listeners.\n */\n destroy() {\n for (let [event, callback] of this._subscriptions) {\n this._emitter.off(event, callback);\n }\n this._subscriptions = [];\n }\n}\n\nexport class EventBus {\n constructor() {\n this._emitter = new TinyEmitter();\n }\n\n createEmitter() {\n return new Emitter(this._emitter);\n }\n}\n","// Load polyfill for :focus-visible pseudo-class.\nimport 'focus-visible';\n\n// Enable debug checks for Preact. Removed in prod builds by Rollup config.\nimport 'preact/debug';\n\n// Load icons.\nimport { registerIcons } from '@hypothesis/frontend-shared';\nimport { annotatorIcons } from './icons';\nregisterIcons(annotatorIcons);\n\nimport {\n PortProvider,\n installPortCloseWorkaroundForSafari,\n} from '../shared/messaging';\nimport { getConfig } from './config/index';\nimport { Guest } from './guest';\nimport { HypothesisInjector } from './hypothesis-injector';\nimport {\n VitalSourceInjector,\n vitalSourceFrameRole,\n} from './integrations/vitalsource';\nimport { Notebook } from './notebook';\nimport { Sidebar } from './sidebar';\nimport { EventBus } from './util/emitter';\n\n/** @typedef {import('../types/annotator').Destroyable} Destroyable */\n\n// Look up the URL of the sidebar. This element is added to the page by the\n// boot script before the \"annotator\" bundle loads.\nconst sidebarLinkElement = /** @type {HTMLLinkElement} */ (\n document.querySelector(\n 'link[type=\"application/annotator+html\"][rel=\"sidebar\"]'\n )\n);\n\n/**\n * @typedef {import('./components/NotebookModal').NotebookConfig} NotebookConfig\n * @typedef {import('./guest').GuestConfig} GuestConfig\n * @typedef {import('./hypothesis-injector').InjectConfig} InjectConfig\n * @typedef {import('./sidebar').SidebarConfig} SidebarConfig\n * @typedef {import('./sidebar').SidebarContainerConfig} SidebarContainerConfig\n */\n\n/**\n * Entry point for the part of the Hypothesis client that runs in the page being\n * annotated.\n *\n * Depending on the client configuration in the current frame, this can\n * initialize different functionality. In \"host\" frames the sidebar controls and\n * iframe containing the sidebar application are created. In \"guest\" frames the\n * functionality to support anchoring and creating annotations is loaded. An\n * instance of Hypothesis will have one host frame, one sidebar frame and one or\n * more guest frames. The most common case is that the host frame, where the\n * client is initially loaded, is also the only guest frame.\n */\nfunction init() {\n const annotatorConfig = /** @type {GuestConfig & InjectConfig} */ (\n getConfig('annotator')\n );\n\n const hostFrame = annotatorConfig.subFrameIdentifier ? window.parent : window;\n\n /** @type {Destroyable[]} */\n const destroyables = [];\n\n if (hostFrame === window) {\n // Ensure port \"close\" notifications from eg. guest frames are delivered properly.\n const removeWorkaround = installPortCloseWorkaroundForSafari();\n destroyables.push({ destroy: removeWorkaround });\n\n const sidebarConfig = /** @type {SidebarConfig} */ (getConfig('sidebar'));\n\n const hypothesisAppsOrigin = new URL(sidebarConfig.sidebarAppUrl).origin;\n const portProvider = new PortProvider(hypothesisAppsOrigin);\n\n const eventBus = new EventBus();\n const sidebar = new Sidebar(document.body, eventBus, sidebarConfig);\n const notebook = new Notebook(\n document.body,\n eventBus,\n /** @type {NotebookConfig} */ (getConfig('notebook'))\n );\n\n portProvider.on('frameConnected', (source, port) =>\n sidebar.onFrameConnected(source, port)\n );\n destroyables.push(portProvider, sidebar, notebook);\n }\n\n const vsFrameRole = vitalSourceFrameRole();\n if (vsFrameRole === 'container') {\n const vitalSourceInjector = new VitalSourceInjector(annotatorConfig);\n destroyables.push(vitalSourceInjector);\n } else {\n // Set up automatic injection of the client into iframes in this frame.\n const hypothesisInjector = new HypothesisInjector(\n document.body,\n annotatorConfig\n );\n // Create the guest that handles creating annotations and displaying highlights.\n const guest = new Guest(document.body, annotatorConfig, hostFrame);\n destroyables.push(hypothesisInjector, guest);\n }\n\n sidebarLinkElement.addEventListener('destroy', () => {\n destroyables.forEach(instance => instance.destroy());\n\n // Remove all the `<link>`, `<script>` and `<style>` elements added to the\n // page by the boot script.\n const clientAssets = document.querySelectorAll('[data-hypothesis-asset]');\n clientAssets.forEach(el => el.remove());\n });\n}\n\n/**\n * Returns a Promise that resolves when the document has loaded (but subresources\n * may still be loading).\n *\n * @return {Promise<void>}\n */\nfunction documentReady() {\n return new Promise(resolve => {\n if (document.readyState !== 'loading') {\n resolve();\n }\n // nb. `readystatechange` may be emitted twice, but `resolve` only resolves\n // on the first call.\n document.addEventListener('readystatechange', () => resolve());\n });\n}\n\ndocumentReady().then(init);\n"],"names":["applyFocusVisiblePolyfill","scope","hadKeyboardEvent","hadFocusVisibleRecently","hadFocusVisibleRecentlyTimeout","inputTypesAllowlist","text","search","url","tel","email","password","number","date","month","week","time","datetime","isValidFocusTarget","el","document","nodeName","classList","focusTriggersKeyboardModality","type","tagName","readOnly","isContentEditable","addFocusVisibleClass","contains","add","setAttribute","removeFocusVisibleClass","hasAttribute","remove","removeAttribute","onKeyDown","e","metaKey","altKey","ctrlKey","activeElement","onPointerDown","onFocus","target","onBlur","window","clearTimeout","setTimeout","onVisibilityChange","visibilityState","addInitialPointerMoveListeners","addEventListener","onInitialPointerMove","removeInitialPointerMoveListeners","removeEventListener","toLowerCase","nodeType","Node","DOCUMENT_FRAGMENT_NODE","host","DOCUMENT_NODE","documentElement","event","CustomEvent","error","createEvent","initCustomEvent","dispatchEvent","factory","n","l","u","t","o","r","c","s","a","h","parentNode","removeChild","y","i","f","props","key","ref","__k","__","__b","__e","__d","__c","__h","constructor","__v","vnode","d","children","_","this","context","k","indexOf","length","b","base","m","push","g","__r","debounceRendering","sort","some","__P","j","__n","ownerSVGElement","z","w","v","p","A","Array","isArray","x","P","N","M","appendChild","nextSibling","insertBefore","$","setProperty","test","H","style","cssText","replace","slice","T","I","C","contextType","value","__E","prototype","render","O","sub","state","__s","getDerivedStateFromProps","componentWillMount","componentDidMount","componentWillReceiveProps","shouldComponentUpdate","forEach","componentWillUpdate","componentDidUpdate","getChildContext","getSnapshotBeforeUpdate","L","diffed","call","localName","createTextNode","createElementNS","createElement","is","data","childNodes","dangerouslySetInnerHTML","attributes","name","__html","innerHTML","checked","current","unmount","componentWillUnmount","S","arguments","defaultProps","firstChild","getDerivedStateFromError","setState","componentDidCatch","forceUpdate","Promise","then","bind","resolve","__PREACT_DEVTOOLS__","attachPreact","Fragment","Component","displayName","__o","reduce","__source","fileName","lineNumber","console","warn","WeakMap","hasOwnProperty","Object","toString","JSON","stringify","pop","useEffect","useLayoutEffect","lazyPropTypes","Error","componentStack","propTypes","has","set","__f","keys","message","get","create","__self","__proto__","join","hasOwn","classNames","classes","arg","argType","inner","apply","module","exports","default","__H","shift","requestAnimationFrame","cancelAnimationFrame","filter","iconRegistry","Map","SvgIcon","className","inline","title","markup","element","useRef","svg","querySelector","spanProps","_jsxDEV","classnames","columnNumber","registerIcon","Symbol","_jsxFileName","ButtonBase","buttonRef","icon","iconPosition","size","variant","expanded","pressed","restProps","_restProps$role","role","ariaProps","IconButton","LabeledButton","cancelSVG","Icon","containerClasses","Link","linkRef","rel","annotatorIcons","annotate","cancel","caution","hide","highlight","jstor","note","pointer","show","ListenerCollection","_listeners","eventTarget","eventType","listener","options","symbol","listenerId","delete","removeAll","clear","byteToHex","val","str","generateHexString","len","bytes","Uint8Array","crypto","getRandomValues","from","map","isMessage","property","isMessageEqual","PortFinder","hostFrame","source","_hostFrame","_source","destroy","async","isValidRequest","requestId","reject","postRequest","postMessage","frame1","frame2","intervalId","setInterval","timeoutId","clearInterval","ports","E","on","callback","ctx","fn","once","self","off","emit","evtArr","evts","liveEvents","tinyEmitterModule","TinyEmitter","tinyEmitter","errorDestination","serializeError","err","stack","String","undefined","sendError","postErr","DOMException","sendErr","sendErrorsTo","destination","PortProvider","hypothesisAppsOrigin","_hypothesisAppsOrigin","_emitter","_handledRequests","Set","_sidebarHostChannel","MessageChannel","_allowedMessages","allowedOrigin","_listen","errorContext","sentErrors","reportError","origin","channel","MessagePort","ServiceWorker","isSourceWindow","find","allowedMessage","_messageMatches","messageChannel","targetOrigin","port1","port2","args","eventName","handler","VERSION","PROTOCOL","sendCall","port","method","sequence","protocol","version","shouldUseSafariWorkaround","userAgent","webkitVersionMatch","match","parseInt","PortRPC","navigator","currentWindow","_port","_methods","_sequence","_callbacks","parent","_pendingCalls","_receivedCloseEvent","connect","_handle","start","close","_destroyed","seq","finalArg","_parseMessage","msg","response","cb","assign","dest","src","parseJsonConfig","config","settingsElements","querySelectorAll","settings","parse","textContent","toBoolean","trim","toLocaleLowerCase","numericalVal","Number","isNaN","Boolean","urlFromLinkTag","window_","link","href","checkIfString","settingsFrom","configFuncSettings","hypothesisConfig","docs","configFuncSettingsFrom","jsonConfigs","hostPageSetting","ignoreOtherConfiguration","annotations","annotFragmentMatch","location","annotationsFromURL","clientUrl","group","groupFragmentMatch","groupFromURL","notebookAppUrl","showHighlights","sidebarAppUrl","query","queryFragmentMatch","decodeURIComponent","queryFromURL","getHostPageSetting","configDefinitions","allowInBrowserExt","defaultValue","getValue","appType","branding","contentPartner","enableExperimentalNewNoteButton","focus","theme","usernameUrl","onLayoutChange","openSidebar","coerce","requestConfigFromFrame","services","subFrameIdentifier","externalContainerSelector","getConfig","contexts","annotator","sidebar","notebook","configurationKeys","configDef","hasDefault","isURLFromBrowserExtension","startsWith","modifiers","alt","ctrl","meta","installShortcut","shortcut","onPress","rootElement","onKeydown","parts","split","requiredModifiers","requiredKey","part","modifierFlag","shiftKey","matchShortcut","useShortcut","NumberIcon","badgeCount","_jsx","AdderToolbarArrow","arrowDirection","ToolbarButton","label","onClick","_jsxs","AdderToolbar","isVisible","onCommand","annotationCount","annotateShortcut","highlightShortcut","showShortcut","dir","visibility","_Fragment","isTouchDevice","_window","matchMedia","matches","createShadowRoot","container","shadowRoot","attachShadow","mode","_document$querySelect","linkEl","loadStyles","applyFocusVisible","stopPropagation","passive","toPx","pixels","Adder","_outerContainer","_shadowRoot","position","top","left","_view","ownerDocument","defaultView","_width","getBoundingClientRect","width","_height","height","_isVisible","_arrowDirection","_onAnnotate","onAnnotate","_onHighlight","onHighlight","_onShowAnnotations","onShowAnnotations","annotationsForSelection","_render","selectionRect","isRTLselection","_calculateTarget","_showAt","hMargin","Math","min","adderWidth","touchScreenOffset","adderHeight","innerHeight","max","innerWidth","_findZindex","elementsFromPoint","zIndexes","getComputedStyle","zIndex","isInteger","parentRect","parentEl","parentElement","nearestPositionedAncestor","command","nodeTextLength","node","ELEMENT_NODE","TEXT_NODE","previousSiblingsTextLength","sibling","previousSibling","resolveOffsets","offsets","nextOffset","nodeIter","createNodeIterator","NodeFilter","SHOW_TEXT","results","textNode","currentNode","nextNode","offset","RangeError","TextPosition","relativeTo","direction","tw","createTreeWalker","getRootNode","forwards","previousNode","static","fromPoint","textOffset","TextRange","end","toRange","range","Range","setStart","setEnd","startContainer","startOffset","endContainer","endOffset","root","placeholderSelector","isInPlaceholder","closest","isSelectionBackwards","selection","focusNode","anchorNode","focusOffset","anchorOffset","getRangeAt","isNodeInRange","_node$nodeValue$lengt","_node$nodeValue","nodeValue","comparePoint","forEachNodeInRange","commonAncestorContainer","SHOW_ALL","selectionFocusRect","isCollapsed","textBoxes","whitespaceOnly","textNodes","rects","nodeRange","createRange","selectNodeContents","collapsed","viewportRects","getClientRects","detach","concat","getTextBoundingBoxes","SVG_NAMESPACE","highlightRange","cssClass","splitText","wholeTextNodesInRange","inPlaceholder","textNodeSpans","prevNode","currentSpan","whitespace","span","highlights","nodes","highlightEl","replaceChild","highlightEls","canvasEl","pageEl","getPDFCanvas","svgHighlightLayer","svgStyle","mixBlendMode","canvasRect","highlightRects","highlightRect","rect","svgHighlight","append","drawHighlightsAbovePDFCanvas","replaceWith","replacements","removeHighlights","setHighlightsFocused","focused","toggle","collection","acc","bottom","right","BucketBarClient","contentContainer","hostRPC","_hostRPC","_updatePending","_anchors","update","anchors","positions","annotation","tag","$tag","anchor1","anchor2","computeAnchorPositions","shownWarnings","warnOnce","reset","annotatorFlags","FeatureFlags","knownFlags","super","_flags","_knownFlags","flags","flag","entries","flagEnabled","_this$_flags$get","includes","allFlags","fromEntries","reverse","oneIfNotZero","advanceBlock","peq","hIn","pV","mV","hInIsNegative","eq","xV","xH","pH","mH","hOut","lastRowMask","findMatchEnds","pattern","maxErrors","bMax","ceil","Uint32Array","fill","emptyPeq","asciiPeq","charCodeAt","charPeq","idx","score","charCode","carry","maxBlockScore","remainder","splice","errors","patRev","minStart","rm","findMatchStarts","matchPos","exactMatches","approxSearch","textMatchScore","matchQuote","quote","scoreMatch","quoteScore","prefixScore","prefix","suffixScore","suffix","posScore","hint","abs","quoteWeight","scoredMatches","getPathSegment","result","getNodeName","pos","tmp","getNodePosition","xpathFromNode","xpath","elem","nthChildOfType","index","toUpperCase","matchIndex","child","nodeFromXPath","body","segments","segment","elementName","elementIndex","separatorPos","indexStr","evaluateSimpleXPath","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","RangeAnchor","selector","startPos","fromCharOffset","endPos","toSelector","normalizedRange","fromRange","textRange","TextPositionAnchor","fromOffsets","TextQuoteAnchor","exact","toPositionAnchor","anchor","selectors","maybeAssertQuote","_quote","promise","range_","catch","fromSelector","position_","quote_","describe","types","normalizeURI","uri","baseURI","URL","HTMLMetadata","_getDocumentHref","links","_getLinks","getDocumentMetadata","metadata","dc","_getMetaTags","eprints","facebook","highwire","prism","twitter","favicon","_getFavicon","_getTitle","dcLink","documentFingerprint","attribute","tags","getAttribute","content","RegExp","linkElements","hreflang","_absoluteUrl","values","doi","id","dcRelationValues","dcIdentifierValues","identifier","dcUrnRelationComponent","dcUrnIdentifierComponent","dcUrn","encodeURIComponent","allowedSchemes","scheme","rectIsEmpty","linesOverlap","rectIntersects","rectA","rectB","rectsOverlapVertically","rectsOverlapHorizontally","unionRects","DOMRect","rectCenter","DOMPoint","contentSelectors","textRectRange","textRect","elementContentRect","scrollLeft","scrollTop","scrollHeight","scrollWidth","textNodesInRect","shouldVisit","contentIntersectsRect","getScrollAnchor","viewport","anchorRange","hasFixedPosition","textNodeLoop","textLen","word","wordBox","preserveScrollPosition","scrollRoot","anchorTop","scrollDelta","COMPLETE","CANCELED","setElementScroll","scrollTo","animate","scrollSettings","_scrollSettings","maxSynchronousAlignments","parentPosition","differenceX","differenceY","targetWidth","targetHeight","align","targetPosition","leftAlign","topAlign","leftOffset","topOffset","leftScalar","topScalar","isWindow","pageXOffset","pageYOffset","lockX","lockY","offsetLeft","offsetTop","clientWidth","clientHeight","getTargetScrollLocation","Date","now","startTime","timeValue","endIterations","easeValue","ease","scrollAncestor","task","raf","defaultIsWindow","defaultIsScrollable","overflow","defaultValidTarget","findParentElement","assignedSlot","ownerWindow","scrollIntoView","pow","parents","validTarget","isScrollable","debug","log","scrollingElements","done","cancelHandler","idle","lastSettings","passiveOptions","endType","cancellable","transitionScrollTo","interpolate","fraction","scrollElement","maxDuration","scrollStart","scrollDuration","scrollFraction","HTMLIntegration","features","_htmlMeta","_sideBySideEnabled","_sideBySideActive","_lastLayout","_flagsChanged","sideBySide","fitSideBySide","canAnnotate","layout","maximumWidthToFit","active","_activateSideBySide","_deactivateSideBySide","sidebarWidth","rightMargin","computeLeftMargin","marginLeft","marginRight","beforeBodyLeft","contentArea","leftMarginVotes","rightMarginVotes","contentSelector","paragraphs","textLength","_leftMarginVotes$get","_rightMarginVotes$get","leftVotes","rightVotes","leftMargin","leftPos","rightPos","guessMainContentArea","freeSpace","adjustedMargin","_anchor$highlights","defineProperty","configurable","scrollElementIntoView","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","freeGlobal","global","freeSelf","Function","objectToString","nativeMax","nativeMin","isObject","toNumber","isObjectLike","isSymbol","other","valueOf","isBinary","lodash_debounce","func","wait","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","TypeError","invokeFunc","thisArg","leadingEdge","timerExpired","shouldInvoke","timeSinceLastCall","trailingEdge","remainingWait","debounced","isInvoking","flush","advance","count","countChars","translateOffsets","input","output","beforeStartCount","startToEndCount","outputStart","RenderingStates","pageTextCache","quotePositionCache","quotePositionCacheKey","getNodeTextLayer","_el$closest","getPDFViewer","PDFViewerApplication","pdfViewer","getPageView","pageIndex","pageView","pdfPage","onPagesLoaded","eventBus","getPageTextContent","cachedText","pageText","getTextContent","normalizeWhitespace","items","it","getPageText","findPageByOffset","viewer","pageStartOffset","pageEndOffset","pagesCount","isSpace","char","isNotSpace","anchorByPosition","page","all","renderingState","textLayer","renderingDone","textLayerDiv","textLayerStr","textLayerStart","textLayerEnd","stripSpaces","placeholder","createPlaceholder","div","setStartBefore","setEndAfter","stripped","matchedText","substring","cacheKey","cachedPos","quoteSelector","positionHint","pageCount","pageIndexes","expectedPageIndex","expectedOffsetInPage","strippedPrefix","strippedSuffix","strippedQuote","bestMatch","strippedText","strippedHint","exactQuoteMatch","exactPrefixMatch","exactSuffixMatch","hasContext","anchorQuote","getTextLayerForRange","startTextLayer","endTextLayer","startPageIndex","getSiblingIndex","pageOffset","getPageOffset","Banners","ContentPartnerBanner","provider","onClose","WarningBanner","PDFMetadata","app","_loaded","initializedPromise","initialized","timeout","pdfViewerInitialized","downloadComplete","finish","getUri","getPDFURL","fingerprintToURN","getFingerprint","info","documentInfo","contentDispositionFilename","pdfDocument","getMetadata","Title","pathSegments","pathname","filenameFromURL","fingerprints","fingerprint","anchorIsInPlaceholder","delay","ms","PDFIntegration","_pdfViewerApp$appConf","_pdfViewerApp$appConf2","_options$reanchoringM","_options$contentPartn","pdfViewerApp","pdfContainer","appConfig","appContainer","pdfMetadata","observer","MutationObserver","debounce","_update","observe","attributeFilter","childList","subtree","_reanchoringMaxWait","reanchoringMaxWait","_banner","_bannerState","noTextWarning","_updateBannerState","_checkForSelectableText","_updateAnnotationLayerVisibility","getSelection","_this$_banner","disconnect","canDescribe","hasText","documentHasText","outerContainer","_this$_banner2","prepend","bannerHeight","refreshAnnotations","_page$textLayer","_container$querySelec","hl","sidebarLayout","reservedSpace","toolbarWidth","currentScaleValue","_anchorOffset","_waitForAnnotationToBeAnchored","_anchor","offsetParent","offsetRelativeTo","FrameObserver","onFrameAdded","onFrameRemoved","_element","_onFrameAdded","_onFrameRemoved","_annotatableFrames","_isDisconnected","_mutationObserver","_discoverFrames","frame","onNextDocumentReady","contentWindow","_removeFrame","frames","_addFrame","unsubscribe","onDocumentReady","doc","pollInterval","pollTimer","pollForDocumentChange","documents","WeakSet","cancelPoll","checkForDocumentChange","currentDocument","contentDocument","_frame$contentDocumen","hasBlankDocumentThatWillNavigate","readyState","_frame$contentWindow","canceled","initialCheckTimer","ImageTextLayer","image","charBoxes","containerParent","color","textAlign","fontFamily","fontSize","getContext","font","scaledValue","dimension","unit","columns","words","currentWord","addWord","lines","currentLine","addLine","prevWord","prevCenter","xDist","currentColumn","addColumn","line","prevLine","yDist","analyzeLayout","column","columnEl","display","lineEl","marginTop","whiteSpace","wordEl","transformOrigin","metrics","measureText","xScale","yScale","transform","updateTextLayerSize","imageWidth","imageHeight","_updateTextLayerSize","ResizeObserver","_imageSizeObserver","updateSync","_this$_imageSizeObser","HypothesisInjector","_config","_frameObserver","injectClient","assetRoot","injectedConfig","configElement","innerText","bootScript","iframeDocument","findBookElement","document_","vitalSourceFrameRole","_window_$frameElement","parentDoc","frameElement","VitalSourceInjector","bookElement","contentFrames","injectClientIntoContentFrame","getPDFPageImage","VitalSourceContentIntegration","html_side_by_side","_htmlIntegration","stopEvents","insertAdjacentElement","removeScrollingAttr","makeContentFrameScrollable","bookImage","pageData","innerPageData","charRects","glyphs","glyph","_textLayer","_this$_textLayer","bookContainer","newWidth","scrollToAnchor","createIntegration","selectedRange","rangeCount","SelectionObserver","isMouseDown","_pendingCallback","scheduleCallback","eventHandler","_cancelPendingCallback","_document","itemForNode","checkedNodes","item","rangeUtil","_node$_annotation","_annotation","annotationsAt","getHighlightsContainingNode","ann","resolveAnchor","removeTextSelection","_document$getSelectio","removeAllRanges","Guest","_highlightsVisible","_isAdderVisible","_informHostOnNextSelectionClear","selectedRanges","_adder","createAnnotation","selectAnnotations","_selectionObserver","_onSelection","_onClearSelection","_annotations","_frameIdentifier","_portFinder","_integration","_connectHost","_sidebarRPC","_connectSidebar","_bucketBarClient","_setupElementEvents","_focusedAnnotations","maybeCloseSidebar","_repositionAdder","frameIdentifier","_window$getSelection","_focusAnnotations","_scrollToClosestOffScreenAnchor","ancestor","hostPort","discover","bubbles","cancelable","detail","setHighlightsVisible","getDocumentInfo","removeAllHighlights","$orphan","every","_updateAnchors","notify","ranges","$highlight","closestAnchor","closestTop","findClosestOffscreenAnchor","isBackwards","focusRect","visible","sideBySideActive","focusedAnnotationTags","addConfigFragment","baseURL","params","URLSearchParams","hash","createAppConfig","appURL","_appConfig$services","hostURL","service","onLoginRequest","onLoginRequestProvided","onLogoutRequest","onLogoutRequestProvided","onSignupRequest","onSignupRequestProvided","onProfileRequest","onProfileRequestProvided","onHelpRequest","onHelpRequestProvided","NotebookIframe","groupId","allowFullScreen","NotebookModal","iframeKey","setIframeKey","useState","isHidden","setIsHidden","setGroupId","originalDocumentOverflowStyle","emitterRef","emitter","createEmitter","subscribe","hidden","_emitterRef$current","publish","Notebook","exportName","VENDOR_PREFIXES","TEST_ELEMENT","round","setTimeoutContext","bindFn","invokeArrayArg","each","obj","iterator","deprecate","deprecationMessage","nextKey","extend","merge","inherit","properties","childP","baseP","_super","boolOrFn","ifUndefined","val1","val2","addEventListeners","splitStr","removeEventListeners","hasParent","inStr","inArray","findByKey","toArray","uniqueArray","prefixed","prop","camelProp","_uniqueId","getWindowForElement","parentWindow","SUPPORT_TOUCH","SUPPORT_POINTER_EVENTS","SUPPORT_ONLY_TOUCH","INPUT_TYPE_TOUCH","INPUT_TYPE_MOUSE","DIRECTION_VERTICAL","DIRECTION_UP","PROPS_XY","PROPS_CLIENT_XY","Input","manager","inputTarget","domHandler","ev","enable","init","inputHandler","pointersLen","pointers","changedPointersLen","changedPointers","isFirst","isFinal","session","pointersLength","firstInput","simpleCloneInputData","firstMultiple","offsetCenter","center","getCenter","timeStamp","deltaTime","angle","getAngle","distance","getDistance","offsetDelta","prevDelta","prevInput","deltaX","deltaY","computeDeltaXY","offsetDirection","getDirection","overallVelocity","getVelocity","overallVelocityX","overallVelocityY","scale","rotation","getRotation","maxPointers","velocity","velocityX","velocityY","last","lastInterval","computeIntervalInputData","srcEvent","computeInputData","recognize","clientX","clientY","p1","p2","sqrt","atan2","PI","evEl","evTarget","evWin","MOUSE_INPUT_MAP","mousedown","mousemove","mouseup","MOUSE_ELEMENT_EVENTS","MOUSE_WINDOW_EVENTS","MouseInput","button","which","pointerType","POINTER_INPUT_MAP","pointerdown","pointermove","pointerup","pointercancel","pointerout","IE10_POINTER_TYPE_ENUM","POINTER_ELEMENT_EVENTS","POINTER_WINDOW_EVENTS","PointerEventInput","store","pointerEvents","MSPointerEvent","PointerEvent","removePointer","eventTypeNormalized","isTouch","storeIndex","pointerId","SINGLE_TOUCH_INPUT_MAP","touchstart","touchmove","touchend","touchcancel","SINGLE_TOUCH_TARGET_EVENTS","SINGLE_TOUCH_WINDOW_EVENTS","SingleTouchInput","started","normalizeSingleTouches","touches","changed","changedTouches","TOUCH_INPUT_MAP","TOUCH_TARGET_EVENTS","TouchInput","targetIds","getTouches","allTouches","targetTouches","changedTargetTouches","touch","TouchMouseInput","mouse","primaryTouch","lastTouches","recordTouches","eventData","setLastTouch","lastTouch","lts","isSyntheticEvent","dx","dy","inputEvent","inputData","isMouse","sourceCapabilities","firesTouchEvents","PREFIXED_TOUCH_ACTION","NATIVE_TOUCH_ACTION","TOUCH_ACTION_COMPUTE","TOUCH_ACTION_AUTO","TOUCH_ACTION_MANIPULATION","TOUCH_ACTION_NONE","TOUCH_ACTION_PAN_X","TOUCH_ACTION_PAN_Y","TOUCH_ACTION_MAP","touchMap","cssSupports","CSS","supports","getTouchActionProps","TouchAction","compute","actions","touchAction","recognizers","recognizer","getTouchAction","hasPanX","hasPanY","cleanTouchActions","preventDefaults","prevented","preventDefault","hasNone","isTapPointer","isTapMovement","isTapTouchTime","DIRECTION_LEFT","preventSrc","STATE_FAILED","Recognizer","defaults","simultaneous","requireFail","stateStr","directionStr","getRecognizerByNameIfManager","otherRecognizer","AttrRecognizer","PanRecognizer","pX","pY","PinchRecognizer","PressRecognizer","_timer","_input","RotateRecognizer","SwipeRecognizer","TapRecognizer","pTime","pCenter","Hammer","preset","Manager","recognizeWith","dropRecognizeWith","requireFailure","dropRequireFailure","hasRequireFailures","canRecognizeWith","additionalEvent","tryEmit","canEmit","inputDataClone","process","attrTest","optionPointers","isRecognized","isValid","threshold","DIRECTION_HORIZONTAL","directionTest","hasMoved","inOut","validPointers","validMovement","validTime","taps","interval","posThreshold","validTouchTime","failTimeout","validInterval","validMultiTap","tapCount","domEvents","inputClass","cssProps","userSelect","touchSelect","touchCallout","contentZooming","userDrag","tapHighlightColor","handlers","oldCssProps","toggleCssProps","stop","force","stopped","curRecognizer","existing","events","gestureEvent","initEvent","gesture","triggerDomEvent","INPUT_START","INPUT_MOVE","INPUT_END","INPUT_CANCEL","STATE_POSSIBLE","STATE_BEGAN","STATE_CHANGED","STATE_ENDED","STATE_RECOGNIZED","STATE_CANCELLED","DIRECTION_NONE","DIRECTION_RIGHT","DIRECTION_DOWN","DIRECTION_ALL","Tap","Pan","Swipe","Pinch","Rotate","Press","BucketList","BucketItem","topPosition","Buckets","above","below","buckets","onFocusAnnotations","onScrollToClosestOffScreenAnchor","onSelectAnnotations","showUpNavigation","showDownNavigation","onMouseEnter","onMouseOut","bucket","BucketBar","_bucketsContainer","_onFocusAnnotations","_onScrollToClosestOffScreenAnchor","_onSelectAnnotations","anchorPositions","aboveTags","belowTags","currentBucket","newBucket","aPos","isContainedWithin","updatedBottom","updatedHeight","computeBuckets","toogle","buttonProps","Toolbar","closeSidebar","isSidebarOpen","newAnnotationType","toggleHighlights","toggleSidebar","toggleSidebarRef","useMinimalControls","selected","ToolbarController","setSidebarOpen","_container","_useMinimalControls","_newAnnotationType","_sidebarOpen","_closeSidebar","_toggleSidebar","_toggleHighlights","_createAnnotation","_sidebarToggleButton","getWidth","minimal","sidebarOpen","open","highlightsVisible","sidebarToggleButton","MIN_RESIZE","Sidebar","_guestWithSelection","_guestRPC","iframe","sidebarURL","sidebarAppSrc","sidebarFrame","createSidebarIframe","bucketBar","externalFrame","iframeContainer","rpc","hypothesisSidebar","toolbarContainer","toolbar","_this$_guestWithSelec","_onResize","_gestureState","initial","final","_setupGestures","serviceConfig","_layoutState","_updateLayoutState","_setupSidebarEvents","_this$bucketBar","_this$_hammerManager","_hammerManager","onFrameConnected","_connectGuest","guestRPC","rootEl","newCount","elems","showFn","triggerElems","triggerElem","sidebarTrigger","_resetGestureState","toggleButton","_onPan","_updateLayout","renderFrame","margin","_this$iframeContainer","computedStyle","frameVisibleWidth","layoutState","delta","Emitter","_subscriptions","subEvent","subCallback","EventBus","icons","registerIcons","sidebarLinkElement","annotatorConfig","destroyables","removeWorkaround","_event$data","installPortCloseWorkaroundForSafari","sidebarConfig","portProvider","vitalSourceInjector","hypothesisInjector","guest","instance"],"mappings":"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"}
|