UNPKG

pdfmark.js

Version:

Highlight keywords in pdfjs svg output. Based on mark.js.

8 lines (7 loc) 15.4 kB
/*!*************************************************** * pdfmark.js v1.2.4 * * Copyright (c) 2014–2018, Julian Kühnel * Released under the MIT license https://git.io/vwTVl *****************************************************/ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Pdfmark=e()}(this,function(){"use strict";class t{constructor(t,e=!0,s=[],i=5e3){this.ctx=t,this.iframes=e,this.exclude=s,this.iframesTimeout=i}static matches(t,e){const s="string"==typeof e?[e]:e,i=t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.webkitMatchesSelector;if(i){let e=!1;return s.every(s=>!i.call(t,s)||(e=!0,!1)),e}return!1}getContexts(){let t,e=[];return(t=void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(t=>{const s=e.filter(e=>e.contains(t)).length>0;-1!==e.indexOf(t)||s||e.push(t)}),e}getIframeContents(t,e,s=(()=>{})){let i;try{const e=t.contentWindow;if(i=e.document,!e||!i)throw new Error("iframe inaccessible")}catch(t){s()}i&&e(i)}isIframeBlank(t){const e="about:blank",s=t.getAttribute("src").trim();return t.contentWindow.location.href===e&&s!==e&&s}observeIframeLoad(t,e,s){let i=!1,r=null;const n=()=>{if(!i){i=!0,clearTimeout(r);try{this.isIframeBlank(t)||(t.removeEventListener("load",n),this.getIframeContents(t,e,s))}catch(t){s()}}};t.addEventListener("load",n),r=setTimeout(n,this.iframesTimeout)}onIframeReady(t,e,s){try{"complete"===t.contentWindow.document.readyState?this.isIframeBlank(t)?this.observeIframeLoad(t,e,s):this.getIframeContents(t,e,s):this.observeIframeLoad(t,e,s)}catch(t){s()}}waitForIframes(t,e){let s=0;this.forEachIframe(t,()=>!0,t=>{s++,this.waitForIframes(t.querySelector("html"),()=>{--s||e()})},t=>{t||e()})}forEachIframe(e,s,i,r=(()=>{})){let n=e.querySelectorAll("iframe"),o=n.length,a=0;n=Array.prototype.slice.call(n);const h=()=>{--o<=0&&r(a)};o||h(),n.forEach(e=>{t.matches(e,this.exclude)?h():this.onIframeReady(e,t=>{s(e)&&(a++,i(t)),h()},h)})}createIterator(t,e,s){return document.createNodeIterator(t,e,s,!1)}createInstanceOnIframe(e){return new t(e.querySelector("html"),this.iframes)}compareNodeIframe(t,e,s){if(t.compareDocumentPosition(s)&Node.DOCUMENT_POSITION_PRECEDING){if(null===e)return!0;if(e.compareDocumentPosition(s)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}getIteratorNode(t){const e=t.previousNode();let s;return{prevNode:e,node:s=null===e?t.nextNode():t.nextNode()&&t.nextNode()}}checkIframeFilter(t,e,s,i){let r=!1,n=!1;return i.forEach((t,e)=>{t.val===s&&(r=e,n=t.handled)}),this.compareNodeIframe(t,e,s)?(!1!==r||n?!1===r||n||(i[r].handled=!0):i.push({val:s,handled:!0}),!0):(!1===r&&i.push({val:s,handled:!1}),!1)}handleOpenIframes(t,e,s,i){t.forEach(t=>{t.handled||this.getIframeContents(t.val,t=>{this.createInstanceOnIframe(t).forEachNode(e,s,i)})})}iterateThroughNodes(t,e,s,i,r){const n=this.createIterator(e,t,i);let o,a,h=[],c=[],l=()=>(({prevNode:a,node:o}=this.getIteratorNode(n)),o);for(;l();)this.iframes&&this.forEachIframe(e,t=>this.checkIframeFilter(o,a,t,h),e=>{this.createInstanceOnIframe(e).forEachNode(t,t=>c.push(t),i)}),c.push(o);c.forEach(t=>{s(t)}),this.iframes&&this.handleOpenIframes(h,t,s,i),r()}forEachNode(t,e,s,i=(()=>{})){const r=this.getContexts();let n=r.length;n||i(),r.forEach(r=>{const o=()=>{this.iterateThroughNodes(t,r,e,s,()=>{--n<=0&&i()})};this.iframes?this.waitForIframes(r,o):o()})}}class e{constructor(t){this.opt=Object.assign({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},t)}create(t){return"disabled"!==this.opt.wildcards&&(t=this.setupWildcardsRegExp(t)),t=this.escapeStr(t),Object.keys(this.opt.synonyms).length&&(t=this.createSynonymsRegExp(t)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(t=this.setupIgnoreJoinersRegExp(t)),this.opt.diacritics&&(t=this.createDiacriticsRegExp(t)),t=this.createMergedBlanksRegExp(t),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(t=this.createJoinersRegExp(t)),"disabled"!==this.opt.wildcards&&(t=this.createWildcardsRegExp(t)),t=this.createAccuracyRegExp(t),new RegExp(t,`gm${this.opt.caseSensitive?"":"i"}`)}escapeStr(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}createSynonymsRegExp(t){const e=this.opt.synonyms,s=this.opt.caseSensitive?"":"i",i=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(let r in e)if(e.hasOwnProperty(r)){const n=e[r],o="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(r):this.escapeStr(r),a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(n):this.escapeStr(n);""!==o&&""!==a&&(t=t.replace(new RegExp(`(${this.escapeStr(o)}|${this.escapeStr(a)})`,`gm${s}`),i+`(${this.processSynonyms(o)}|`+`${this.processSynonyms(a)})`+i))}return t}processSynonyms(t){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(t=this.setupIgnoreJoinersRegExp(t)),t}setupWildcardsRegExp(t){return(t=t.replace(/(?:\\)*\?/g,t=>"\\"===t.charAt(0)?"?":"")).replace(/(?:\\)*\*/g,t=>"\\"===t.charAt(0)?"*":"")}createWildcardsRegExp(t){let e="withSpaces"===this.opt.wildcards;return t.replace(/\u0001/g,e?"[\\S\\s]?":"\\S?").replace(/\u0002/g,e?"[\\S\\s]*?":"\\S*")}setupIgnoreJoinersRegExp(t){return t.replace(/[^(|)\\]/g,(t,e,s)=>{let i=s.charAt(e+1);return/[(|)\\]/.test(i)||""===i?t:t+"\0"})}createJoinersRegExp(t){let e=[];const s=this.opt.ignorePunctuation;return Array.isArray(s)&&s.length&&e.push(this.escapeStr(s.join(""))),this.opt.ignoreJoiners&&e.push("\\u00ad\\u200b\\u200c\\u200d"),e.length?t.split(/\u0000+/).join(`[${e.join("")}]*`):t}createDiacriticsRegExp(t){const e=this.opt.caseSensitive?"":"i",s=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"];let i=[];return t.split("").forEach(r=>{s.every(s=>{if(-1!==s.indexOf(r)){if(i.indexOf(s)>-1)return!1;t=t.replace(new RegExp(`[${s}]`,`gm${e}`),`[${s}]`),i.push(s)}return!0})}),t}createMergedBlanksRegExp(t){return t.replace(/[\s]+/gim,"[\\s]+")}createAccuracyRegExp(t){let e=this.opt.accuracy,s="string"==typeof e?e:e.value,i="string"==typeof e?[]:e.limiters,r="";switch(i.forEach(t=>{r+=`|${this.escapeStr(t)}`}),s){case"partially":default:return`()(${t})`;case"complementary":return`()([^${r="\\s"+(r||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿"))}]*${t}[^${r}]*)`;case"exactly":return`(^|\\s${r})(${t})(?=$|\\s${r})`}}}const s=(t,e)=>{for(let s in e)t.setAttribute(s,e[s])};class i{constructor(t){this.ctx=t,this.ie=!1;const e=window.navigator.userAgent;(e.indexOf("MSIE")>-1||e.indexOf("Trident")>-1)&&(this.ie=!0)}set opt(t){this._opt=Object.assign({},{element:"",className:"",exclude:[],iframes:!1,iframesTimeout:5e3,separateWordSearch:!0,acrossElements:!1,ignoreGroups:0,each:()=>{},noMatch:()=>{},filter:()=>!0,done:()=>{},debug:!1,log:window.console},t)}get opt(){return this._opt}get iterator(){return new t(this.ctx,this.opt.iframes,this.opt.exclude,this.opt.iframesTimeout)}log(t,e="debug"){const s=this.opt.log;this.opt.debug&&"object"==typeof s&&"function"==typeof s[e]&&s[e](`mark.js: ${t}`)}getSeparatedKeywords(t){let e=[];return t.forEach(t=>{this.opt.separateWordSearch?t.split(" ").forEach(t=>{t.trim()&&-1===e.indexOf(t)&&e.push(t)}):t.trim()&&-1===e.indexOf(t)&&e.push(t)}),{keywords:e.sort((t,e)=>e.length-t.length),length:e.length}}isNumeric(t){return Number(parseFloat(t))==t}checkRanges(t){if(!Array.isArray(t)||"[object Object]"!==Object.prototype.toString.call(t[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(t),[];const e=[];let s=0;return t.sort((t,e)=>t.start-e.start).forEach(t=>{let{start:i,end:r,valid:n}=this.callNoMatchOnInvalidRanges(t,s);n&&(t.start=i,t.length=r-i,e.push(t),s=r)}),e}callNoMatchOnInvalidRanges(t,e){let s,i,r=!1;return t&&void 0!==t.start?(i=(s=parseInt(t.start,10))+parseInt(t.length,10),this.isNumeric(t.start)&&this.isNumeric(t.length)&&i-e>0&&i-s>0?r=!0:(this.log("Ignoring invalid or overlapping range: "+`${JSON.stringify(t)}`),this.opt.noMatch(t))):(this.log(`Ignoring invalid range: ${JSON.stringify(t)}`),this.opt.noMatch(t)),{start:s,end:i,valid:r}}checkWhitespaceRanges(t,e,s){let i,r=!0,n=s.length,o=e-n,a=parseInt(t.start,10)-o;return(i=(a=a>n?n:a)+parseInt(t.length,10))>n&&(i=n,this.log(`End range automatically set to the max value of ${n}`)),a<0||i-a<0||a>n||i>n?(r=!1,this.log(`Invalid range: ${JSON.stringify(t)}`),this.opt.noMatch(t)):""===s.substring(a,i).replace(/\s+/g,"")&&(r=!1,this.log("Skipping whitespace only range: "+JSON.stringify(t)),this.opt.noMatch(t)),{start:a,end:i,valid:r}}getTextNodes(t){let e="",s=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,t=>{s.push({start:e.length,end:(e+=t.textContent).length,node:t})},t=>this.matchesExclude(t.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT,()=>{t({value:e,nodes:s})})}matchesExclude(e){return t.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}wrapInHtmlTag(t,e,s){const i=this.opt.element?this.opt.element:"mark",r=t.splitText(e),n=r.splitText(s-e);let o=document.createElement(i);return o.setAttribute("data-markjs","true"),this.opt.className&&o.setAttribute("class",this.opt.className),o.textContent=r.textContent,r.parentNode.replaceChild(o,r),n}getTextNodeOffset(t,e){return t?this.getTextNodeOffset(t.previousSibling,t.length+e):e}addSvgRectangle(t,e,i){const r=t.parentNode,n=t.parentNode.parentNode,o=t.parentNode.parentNode.parentNode,a=r.getAttribute("x").split(" "),h=this.getTextNodeOffset(t.previousSibling,0),c=e+h,l=i+h,d=document.createElementNS("http://www.w3.org/2000/svg","rect");s(d,{x:`${a[c]}px`,y:`${r.getAttribute("y")-parseInt(r.getAttribute("font-size"))}`,width:`${r.getEndPositionOfChar(l-1).x-parseFloat(a[c])}px`,height:r.getAttribute("font-size"),fill:"yellow",transform:n.getAttribute("transform"),"data-markjs":"true"}),o.insertBefore(d,n);const p=r.cloneNode(!0);return s(p,{x:a.slice(c,l).join(" "),fill:"black","data-markjs":"true"}),p.textContent=r.textContent.slice(c,l),n.insertBefore(p,r.nextSibling),t.splitText(e).splitText(i-e)}highlightRangeInTextNode(t,e,s){return"svg:tspan"===t.parentNode.nodeName?this.addSvgRectangle(t,e,s):this.wrapInHtmlTag(t,e,s)}highlightRangeInMappedTextNode(t,e,s,i,r){t.nodes.every((n,o)=>{const a=t.nodes[o+1];if(void 0===a||a.start>e){if(!i(n.node))return!1;const a=e-n.start,h=(s>n.end?n.end:s)-n.start,c=t.value.substr(0,n.start),l=t.value.substr(h+n.start);if(n.node=this.highlightRangeInTextNode(n.node,a,h),t.value=c+l,t.nodes.forEach((e,s)=>{s>=o&&(t.nodes[s].start>0&&s!==o&&(t.nodes[s].start-=h),t.nodes[s].end-=h)}),s-=h,r(n.node.previousSibling,n.start),!(s>n.end))return!1;e=n.end}return!0})}highlightGroups(t,e,s,i){return i((t=this.highlightRangeInTextNode(t,e,e+s)).previousSibling),t}separateGroups(t,e,s,i,r){let n=e.length;for(let s=1;s<n;s++){let n=t.textContent.indexOf(e[s]);e[s]&&n>-1&&i(e[s],t)&&(t=this.highlightGroups(t,n,e[s].length,r))}return t}highlightMatches(t,e,s,i,r){const n=0===e?0:e+1;this.getTextNodes(e=>{e.nodes.forEach(e=>{let r;for(e=e.node;null!==(r=t.exec(e.textContent))&&""!==r[n];){if(this.opt.separateGroups)e=this.separateGroups(e,r,n,s,i);else{if(!s(r[n],e))continue;let t=r.index;if(0!==n)for(let e=1;e<n;e++)t+=r[e].length;e=this.highlightGroups(e,t,r[n].length,i)}t.lastIndex=0}}),r()})}highlightMatchesAcrossElements(t,e,s,i,r){const n=0===e?0:e+1;this.getTextNodes(e=>{let o;for(;null!==(o=t.exec(e.value))&&""!==o[n];){let r=o.index;if(0!==n)for(let t=1;t<n;t++)r+=o[t].length;const a=r+o[n].length;this.highlightRangeInMappedTextNode(e,r,a,t=>s(o[n],t),(e,s)=>{t.lastIndex=s,i(e)})}r()})}highlightRangeFromIndex(t,e,s,i){this.getTextNodes(r=>{const n=r.value.length;t.forEach((t,i)=>{let{start:o,end:a,valid:h}=this.checkWhitespaceRanges(t,n,r.value);h&&this.highlightRangeInMappedTextNode(r,o,a,s=>e(s,t,r.value.substring(o,a),i),e=>{s(e,t)})}),i()})}unhighlightMatches(t){const e=t.parentNode;let s=document.createDocumentFragment();for(;t.firstChild;)"svg:text"===e.nodeName?t.removeChild(t.firstChild):s.appendChild(t.removeChild(t.firstChild));e.replaceChild(s,t),this.ie?this.normalizeTextNode(e):e.normalize()}normalizeTextNode(t){if(t){if(3===t.nodeType)for(;t.nextSibling&&3===t.nextSibling.nodeType;)t.nodeValue+=t.nextSibling.nodeValue,t.parentNode.removeChild(t.nextSibling);else this.normalizeTextNode(t.firstChild);this.normalizeTextNode(t.nextSibling)}}markRegExp(t,e){this.opt=e,this.log(`Searching with expression "${t}"`);let s=0,i="highlightMatches";this.opt.acrossElements&&(i="highlightMatchesAcrossElements"),this[i](t,this.opt.ignoreGroups,(t,e)=>this.opt.filter(e,t,s),t=>{s++,this.opt.each(t)},()=>{0===s&&this.opt.noMatch(t),this.opt.done(s)})}mark(t,s){this.opt=s;let i=0,r="highlightMatches";const{keywords:n,length:o}=this.getSeparatedKeywords("string"==typeof t?[t]:t),a=t=>{const s=new e(this.opt).create(t);let h=0;this.log(`Searching with expression "${s}"`),this[r](s,1,(e,s)=>this.opt.filter(s,t,i,h),t=>{h++,i++,this.opt.each(t)},()=>{0===h&&this.opt.noMatch(t),n[o-1]===t?this.opt.done(i):a(n[n.indexOf(t)+1])})};this.opt.acrossElements&&(r="highlightMatchesAcrossElements"),0===o?this.opt.done(i):a(n[0])}markRanges(t,e){this.opt=e;let s=0,i=this.checkRanges(t);i&&i.length?(this.log("Starting to mark with the following ranges: "+JSON.stringify(i)),this.highlightRangeFromIndex(i,(t,e,s,i)=>this.opt.filter(t,e,s,i),(t,e)=>{s++,this.opt.each(t,e)},()=>{this.opt.done(s)})):this.opt.done(s)}unmark(e){this.opt=e;let s=this.opt.element?this.opt.element:"*";s+="[data-markjs]",this.opt.className&&(s+=`.${this.opt.className}`),this.log(`Removal selector "${s}"`),this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT,t=>{this.unhighlightMatches(t)},e=>{const i=t.matches(e,s),r=this.matchesExclude(e);return!i||r?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},this.opt.done)}}return function(t){const e=new i(t);return this.mark=((t,s)=>(e.mark(t,s),this)),this.markRegExp=((t,s)=>(e.markRegExp(t,s),this)),this.markRanges=((t,s)=>(e.markRanges(t,s),this)),this.unmark=(t=>(e.unmark(t),this)),this}});