@zeesoft/i18n-verify 3.1.5 → 3.1.6
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/dist/executor.mjs +1 -1
- package/package.json +1 -1
package/dist/executor.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{statSync as t,existsSync as n,mkdirSync as a,readFileSync as r,readdirSync as i,writeFileSync as s,appendFileSync as o,copyFileSync as l}from"fs";import c,{join as d,dirname as u,extname as p}from"path";import{Table as m}from"console-table-printer";import g from"chrome-remote-interface";import h from"globalize";import f from"cldr-data";import{en as v,uk as w,nl as y,ru as b,zh as L,ja as E,pt as S,fr as D,es as x,de as I}from"chrono-node";import{DateTime as C}from"luxon";import{detectTextLangEx as T}from"@isdk/detect-text-language";import z from"jimp";const N={NIGHT_WATCH:"nightwatch",WEB_DRIVER_IO:"webio",CYPRESS:"cypress",SELENIUMJS:"seleniumJS"},M="rgb(0, 154, 61)",P="rgb(255, 0, 0)",A="rgb(255, 255, 0)",O="rgb(0, 0, 255)",R="[32m",k="[31m",_="[33m",B="STRING_LOCALIZED",j="STRING_PARTIALLY_LOCALIZED",F="NUMBER_LOCALIZED",U="CURRENCY_LOCALIZED",V="PERCENTAGE_LOCALIZED",W="DATE_LOCALIZED",G="TIME_LOCALIZED",H="DATE_TIME_LOCALIZED",Q="STRING_NOT_LOCALIZED",Z="NUMBER_NOT_LOCALIZED",q="CURRENCY_NOT_LOCALIZED",Y="PERCENTAGE_NOT_LOCALIZED",X="DATE_NOT_LOCALIZED",J="TIME_NOT_LOCALIZED",K="DATE_TIME_NOT_LOCALIZED",ee="TRUNCATION_ISSUE",te="OVERLAPPING_ISSUE",ne="ENCODING_ISSUE",ae="DNT",re="IGNORE",ie="UNTRANSLATED",se="AUTO_IGNORE",oe="USER_INPUT",le="REVIEWED",ce={[B]:0,[Q]:0,[j]:0,[F]:0,[Z]:0,[W]:0,[X]:0,[H]:0,[K]:0,[G]:0,[J]:0,[U]:0,[q]:0,[V]:0,[Y]:0,[ee]:0,[te]:0,[ne]:0,[ae]:0,[re]:0,[ie]:0,[se]:0,[oe]:0,[le]:0},de={[B]:[M,R],[j]:[A,_],[F]:[M,R],[W]:[M,R],[G]:[M,R],[H]:[M,R],[U]:[M,R],[V]:[M,R],[Q]:[P,k],[X]:[P,k],[K]:[P,k],[J]:[P,k],[Z]:[P,k],[q]:[P,k],[Y]:[P,k],[ee]:[P,k],[te]:[P,k],[ne]:[P,k],[ae]:[M,R],[re]:[M,R],[ie]:[M,R],[se]:[M,R],[oe]:[M,R],[le]:[M,R]},ue={[B]:"string is localized",[j]:"string is partially localized",[F]:"number is localized",[W]:"date is localized",[H]:"dateTime is localized",[G]:"time is localized",[U]:"currency is localized",[V]:"percentage is localized",[Q]:"string is not localized",[X]:"date is not localized",[K]:"dateTime is not localized",[J]:"time is not localized",[Z]:"number is not localized",[q]:"currency is not localized",[Y]:"percentage is not localized",[ee]:"truncation issue detected",[te]:"overlapping issue detected",[ne]:"encoding issue detected",[ae]:"do not translate",[re]:"ignored this issue",[ie]:"string is untranslated",[se]:"automatically ignored issue",[oe]:"user input field",[le]:"string has been reviewed"},pe={EMAIL:/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}\b/gi,URL:/^(https?:\/\/)?(www\.)?[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?$/},me=[".txt",".json"],ge="//*[not(self::script or self::style or self::noscript)]\n [string-length(normalize-space(.)) > 0]\n [not(*)]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//*[@placeholder or @title or @alt]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//input[@type='checkbox' or @type='radio']\n /following-sibling::*[string-length(normalize-space(.)) > 0]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//input[@type='text'][@value][string-length(normalize-space(@value)) > 0]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n",he=!0,fe=["☐","�","","â€","î","ÃÂ","ÃÂ","ü","é","é","æ•°","æ®","ラã"],ve=".i18n-verify.rc",we="html",ye="ascii_table",be="metrics.cache",Le={SANITY:"sanity",I18N:"i18n",CONTEXT:"context"},Ee="bookmark",Se="category",De="fileName",xe="screenshot",Ie="strings";class Ce{constructor(e){this.$driver=e}checkIfNullOrEmpty(e){return![null,"",void 0].includes(e)}getElement(){throw new Error("implementation is missing")}getElements(){throw new Error("implementation is missing")}getText(){throw new Error("implementation is missing")}getTitle(){throw new Error("implementation is missing")}getPlaceholderText(){throw new Error("implementation is missing")}getAltText(){throw new Error("implementation is missing")}getWidth(){throw new Error("implementation is missing")}getHeight(){throw new Error("implementation is missing")}getTagName(){throw new Error("implementation is missing")}getValue(){throw new Error("implementation is missing")}getLocation(){throw new Error("implementation is missing")}getElementDimension(){throw new Error("implementation is missing")}getElementID(){throw new Error("implementation is missing")}isElementDisplayed(){throw new Error("implementation is missing")}isElementPresent(){throw new Error("implementation is missing")}getElementString(){throw new Error("implementation is missing")}getElementStrings(){throw new Error("implementation is missing")}captureScreenshot(){throw new Error("implementation is missing")}getElementsCount(){throw new Error("implementation is missing")}switchToIFrame(){throw new Error("implementation is missing")}switchToParentFrame(){throw new Error("implementation is missing")}pause(){throw new Error("implementation is missing")}onScreenStringHighlight(){throw new Error("implementation is missing")}checkIfIframeIsPresent(){throw new Error("implementation is missing")}checkIfIframeBodyIsEmpty(){throw new Error("implementation is missing")}printOnConsole(e){console.log(e)}}const Te=e=>Ne(e)&&t(e).isDirectory(),ze=e=>Ne(e)&&t(e).isFile(),Ne=e=>n(e),Me=(e,t=!0)=>{Ne(e)&&Te(e)||(t&&qe.debug(`Folder doesn't exists, creating [34m${e}[0m`),(e=>{!Ne(e)&&a(e,{recursive:!0})})(e))},Pe=e=>ze(e)?r(e,{encoding:"utf-8"}):"",Ae=e=>Te(e)?i(e):[],Oe=(...e)=>e?.every((e=>"string"==typeof e))?d(...e):"",$e=(e,t,n="utf-8")=>s(e,t,{encoding:n}),Re=e=>e.replace(/[^a-zA-Z0-9]+/g,"_");class ke{#e;#t;#n;#a;#r;#i;constructor({appName:e,reportTime:t,isContext:n,isSanity:a,defaultLocale:r,reportFolderTimestamp:i}){this.#e=e,this.#n=t,this.#a=n,this.#r=a,this.#t=r,this.#i=i}getAppName(){return this.#e}getReportTime(){return this.#n}isSanityRun(){return this.#r}isContextRun(){return this.#a}getDefaultLocale(){return this.#t}getDefaultLocale(){return this.#t}getReportFolderTimestamp(){return this.#i}}class _e{#s;$generatedContent;$type;constructor(e){this.$type=e}generate(e){this.#s=e,this.$generatedContent=this.buildContent()}export(e,t="report"){this.write(e,t,this.$generatedContent),qe.info(`Exported consolidated I18n Verification ${this.$type} report in ${e}`)}write(e,t,n){throw new Error("should be implemented by child report classes")}buildContent(){throw new Error("should be implemented by child report classes")}getMetaInfo(){return{defaultLocale:this.#s.getDefaultLocale(),appName:this.#s.getAppName(),reportTime:this.#s.getReportTime(),isSanity:this.#s.isSanityRun(),isContext:this.#s.isContextRun(),reportFolderTimestamp:this.#s.getReportFolderTimestamp()}}}class Be extends _e{constructor(){super(we)}buildContent(){const e=this.getMetaInfo(),t=`var DataStore;\n // global functions\n function openImageViewer(imageSrc) {\n var imageView = document.getElementById('imageView');\n imageView.src = \`\${window.getProductUrl()}/\${imageSrc}\`;\n document.getElementById('imageViewer').style.display = 'block';\n }\n\n function closeImageViewer() {\n delete document.getElementById('imageView').src;\n document.getElementById('imageViewer').style.display = 'none';\n }\n\n //to dynamically show/hide header based upon query param: ?standalone=<true|false>\n window.getProductUrl = () => {\n if (window.productUrl) {\n return window.productUrl;\n } else {\n window.productUrl = \`\${window.location.protocol}//\${\n window.location.host\n }\${window.location.pathname.match(/^.+[^\\/report\\.html]/g)?.[0]}\`;\n return window.productUrl;\n }\n };\n const params = new URLSearchParams(location.href.match(/(\\?.*$)/g)?.[0]);\n if (params.size) {\n window.isStandaloneReport = params.get('standalone') === 'true';\n if(!window.isStandaloneReport) {\n const overview = document.getElementById('overview');\n if(overview) {\n overview.classList.add('--reduced-zoom');\n }\n }\n } else {\n window.isStandaloneReport = true;\n }\n ${(e=>`// DataStore\n (() => {\n // prepare appData and set DataStore\n var appData = {\n appName: '${e.appName}',\n defaultLocale: '${e.defaultLocale}',\n sanityRun: ${e.isSanity},\n contextRun: ${e.isContext},\n timeStamp: '${e.reportTime}',\n reportFolderTimestamp: '${e.reportFolderTimestamp}',\n pagesByLocale: {},\n metaData: {\n stats: {},\n statsByLocale: {}\n },\n stringsData: {},\n locales: [],\n hasRuntimeError: false\n };\n\n if (window.rawData) {\n for (const entry of Object.entries(window.rawData)) {\n const locale = entry[0];\n const stats = entry[1].metaData.stats;\n const stringsData = entry[1].stringsData;\n\n if (Object.keys(appData.metaData.stats).length) {\n for (const prop in stats) {\n appData.metaData.stats[prop] += stats[prop];\n }\n } else {\n appData.metaData.stats = {...stats};\n }\n\n appData.metaData.statsByLocale[locale] = stats;\n appData.stringsData[locale] = stringsData;\n\n if (Object.keys(stringsData).length) {\n Object.values(stringsData).forEach(values => {\n values.flat().forEach(value => {\n if(!appData.pagesByLocale[locale]) {\n appData.pagesByLocale[locale] = {};\n }\n if(!(value.description in appData.pagesByLocale[locale])) {\n appData.pagesByLocale[locale][value.description] = value.imagePath;\n }\n });\n });\n }\n }\n\n appData.locales = Object.keys(window.rawData);\n }\n else {\n appData.hasRuntimeError = true;\n }\n\n function SimpleDataStore() {\n this._getData = () => appData;\n\n this.ranSuccessfully = () => this.getLocales().length > 0;\n\n this.getTimestamp = () => this._getData().timeStamp;\n\n this.getAppName = () => this._getData().appName;\n\n this.isDefaultLocale = (locale) =>\n locale === this._getData().defaultLocale;\n\n this.getLocalesData = () => this._getData().stringsData || {};\n\n this.getLocales = () => this._getData().locales || [];\n\n this.getStatByLocale = (locale) =>\n this._getData().metaData.statsByLocale[locale] || {};\n\n this.getOverallStats = () => this._getData().metaData.stats || {};\n\n this.getDataByLocale = (locale) =>\n this._getData().stringsData[locale] || {};\n\n this.isSanityRun = () => this._getData().sanityRun;\n\n this.isContextRun = () => this._getData().contextRun;\n\n this.getReportFolderTimestamp = () => this._getData().reportFolderTimestamp;\n\n this.hasRuntimeError = () => this._getData().hasRuntimeError;\n\n this.getPages = (locale) => locale ? {...(this._getData().pagesByLocale[locale] || {})} : {...this._getData().pagesByLocale};\n\n this.hasDefaultLocaleData = () => Object.keys(this.getDataByLocale(this._getData().defaultLocale)).length > 0;\n\n this.getDefaultLocale = () => this._getData().defaultLocale;\n }\n\n DataStore = new SimpleDataStore();\n })();`)(e)}\n (() => {\n // for sidebar menu\n // hide insights tab\n if(DataStore.hasRuntimeError() || !DataStore.ranSuccessfully() || DataStore.isContextRun()) {\n document.querySelector('.menu-item[data-page="insights"]')?.remove();\n }\n\n const loadContent = (section, highlightMenu = false) => {\n if (highlightMenu) {\n document\n .querySelector('li[data-page="overview"]')\n ?.classList.add('selected');\n }\n\n const contents = document.querySelectorAll('.content');\n const wrapper = document.querySelector('.content-wrapper');\n\n contents.forEach((content) => {\n content.id !== section\n ? content.classList.remove('active')\n : content.classList.add('active');\n\n section === 'overview' &&\n window.persistOverviewCharts &&\n window.persistOverviewCharts();\n\n ['log', 'locale'].includes(section) ? wrapper.classList.add('--hide-overflow') : !DataStore.isContextRun() && !DataStore.isSanityRun() && wrapper.classList.remove('--hide-overflow');\n window.currentPage = section;\n });\n };\n\n document.addEventListener('DOMContentLoaded', function () {\n const createMenuItems = (() => {\n if(DataStore.isContextRun()) {\n return;\n }\n const ul = document.querySelector('.sidebar .submenu');\n const createListItems = (locale) => {\n const li = document.createElement('li');\n li.classList.add('sub-menu-item');\n li.dataset.locale = locale;\n\n const a = document.createElement('a');\n a.href = '#';\n a.textContent = locale;\n\n li.appendChild(a);\n return li;\n };\n\n for (const locale of DataStore.getLocales()) {\n ul.appendChild(createListItems(locale));\n }\n })();\n\n const menuItems = document.querySelectorAll('.menu-item'),\n submenuItems = document.querySelectorAll('.sub-menu-item');\n\n menuItems.forEach((item) => {\n item.addEventListener('click', function (event) {\n const selectedMenuItem = document.querySelector(\n '.menu-item.selected'\n ),\n selectedSubmenu = document.querySelector('.submenu.active'),\n page = item.dataset.page;\n\n if (selectedMenuItem) {\n selectedMenuItem.classList.remove('selected');\n }\n\n if (selectedSubmenu) {\n selectedSubmenu.classList.remove('active');\n }\n\n if (item.classList.contains('has-submenu')) {\n event.preventDefault();\n item.classList.add('selected');\n const submenu = item.querySelector('.submenu');\n submenu.classList.add('active');\n } else {\n item.classList.add('selected');\n }\n\n if (selectedSubmenu) {\n selectedSubmenu\n .querySelectorAll('.sub-menu-item')\n .forEach((submenuItem) => {\n submenuItem.textContent !== item.textContent &&\n submenuItem.classList.remove('selected');\n });\n }\n\n loadContent(page);\n });\n\n loadContent('overview', true);\n });\n\n submenuItems.forEach((sItem) => {\n sItem.addEventListener('click', function (event) {\n const selectedSubMenuItem = document.querySelector(\n '.sub-menu-item.selected'\n );\n event.preventDefault();\n event.stopPropagation();\n\n if (selectedSubMenuItem) {\n selectedSubMenuItem.classList.remove('selected');\n window.previousSelectedLocale =\n selectedSubMenuItem.dataset.locale;\n }\n\n sItem.classList.add('selected');\n loadContent('locale');\n });\n });\n\n // update footer info\n const footer = document.querySelector('.sidebar .footer');\n footer.textContent = \`\${new Date().getFullYear()} @ Zeesoft. All rights reserved.\`;\n\n // update project info\n document.querySelector(\n 'header .logo'\n ).innerHTML = \`zAutomate.AI | \${DataStore.getAppName()} [\${DataStore.isSanityRun() ? 'Quick Sanity' : DataStore.isContextRun() ? 'Context Creation' : 'I18n Verification'}]\`;\n document.querySelector(\n 'header .report-timestamp .info'\n ).innerHTML = \`Report generated at: \${DataStore.getTimestamp()}\`;\n });\n })();\n //overview content\n (() => {\n if (DataStore.hasRuntimeError()) {\n document.getElementById('overview').innerHtml =\n 'zAutomate.AI was unable to perform the operation. Please check the logs for more information.';\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n // don't render overview contents for sanity and context run\n if(DataStore.isContextRun() || DataStore.isSanityRun()) {\n document.querySelector('.content-wrapper')?.classList.add('--hide-overflow');\n document.querySelectorAll('#overview .card')?.forEach((el) => {\n if(!el.classList.contains('common-info')) {\n el.remove();\n }\n });\n\n const titleEl = document.querySelector('#overview .common-info .title');\n const infoEl = document.querySelector('#overview .common-info .info');\n\n if(titleEl && infoEl) {\n let title, info;\n\n const renderPagesSection = () => {\n let tilesEl, searchEl, searchOpEl, pages, entries, hasPages, emptyEl, currentLocale = DataStore.getDefaultLocale();\n const operatorOpts = [\n {\n name: 'Contains',\n value: '~'\n },\n {\n name: 'Starts with',\n value: '^'\n },\n {\n name: 'Ends with',\n value: '$'\n },\n {\n name: 'Equals',\n value: '='\n },\n ];\n const valueStates = {locale: DataStore.getDefaultLocale(), search: {op: operatorOpts[0].value, value: ''}};\n const getImagePath = (imagePath, locale = valueStates.locale) => \`\${window.getProductUrl()}/\${locale}/screenshots/reviewed/\${imagePath}\`;\n const renderEmptyContext = (container) => {\n emptyEl = document.createElement('div');\n emptyEl.classList.add('empty-context');\n emptyEl.innerHTML = 'No contexts available.';\n container.appendChild(emptyEl);\n };\n const removeEmptyContext = () => {\n if(emptyEl) {\n emptyEl.remove();\n }\n };\n\n const loadData = () => {\n pages = DataStore.getPages(valueStates.locale);\n\n if (valueStates.search.value.trim().length) {\n const value = valueStates.search.value.trim().toLowerCase();\n const op = valueStates.search.op;\n const predicate = (string) => {\n string = String(string).toLowerCase();\n switch(op) {\n case '~':\n return string.includes(value);\n case '^':\n return string.startsWith(value);\n case '$':\n return string.endsWith(value);\n case '=':\n return string === value;\n default:\n return false;\n }\n };\n const matchedPages = [];\n const matchedEntries = Object.entries(DataStore.getDataByLocale(valueStates.locale)).filter((e) => predicate(e[0]));\n matchedEntries.forEach((e) => e[1].flat().forEach(pages => !matchedPages.includes(pages.imagePath) & matchedPages.push(pages.imagePath)));\n if(matchedPages.length) {\n if(Object.keys(pages).length) {\n for (const name in pages) {\n if(!matchedPages.includes(pages[name])) {\n delete pages[name];\n }\n }\n }\n }\n else {\n pages = {};\n }\n }\n\n entries = Object.entries(pages).sort((a, b) => a[1].localeCompare(b[1]));\n hasPages = entries.length > 0;\n };\n\n const renderContextTiles = () => {\n loadData();\n\n if(!tilesEl) {\n tilesEl = createTilesSection();\n }\n else {\n tilesEl.innerHTML = '';\n }\n\n if(!hasPages) {\n renderEmptyContext(tilesEl);\n return;\n }\n\n removeEmptyContext();\n createPageTiles(tilesEl);\n };\n\n const createTilesSection = () => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('page-tiles');\n const titleSection = document.createElement('div');\n titleSection.classList.add('title-section');\n const tilesListWrapper = document.createElement('div');\n const tilesEl = document.createElement('div');\n tilesListWrapper.classList.add('tiles-list-wrapper');\n tilesListWrapper.appendChild(tilesEl);\n tilesEl.classList.add('tiles-list');\n wrapperEl.appendChild(titleSection);\n wrapperEl.appendChild(tilesListWrapper);\n\n const createTitle = () => {\n const titleWrapper = document.createElement('div');\n titleWrapper.classList.add('title-wrapper');\n const titleEl = document.createElement('span');\n titleEl.classList.add('title');\n titleEl.innerHTML = 'Context Pages'\n titleWrapper.appendChild(titleEl);\n titleSection.appendChild(titleWrapper);\n };\n\n const createSearchField = () => {\n const searchWrapperEl = document.createElement('div');\n searchWrapperEl.classList.add('search-field-wrapper');\n\n const createLabel = () => {\n const label = document.createElement("label");\n label.textContent = "Search string: ";\n label.setAttribute("for", "operator-filter");\n\n searchWrapperEl.appendChild(label);\n };\n\n const createOperatorFilter = () => {\n searchOpEl = document.createElement("select");\n searchOpEl.id = "operator-filter";\n if(valueStates.search.op) {\n searchOpEl.title = operatorOpts.find((op) => op.value === valueStates.search.op).name;\n }\n searchOpEl.addEventListener("change", function () {\n if (valueStates.search.op !== this.value) {\n valueStates.search.op = this.value;\n renderContextTiles();\n }\n });\n\n operatorOpts.forEach((op) => {\n const option = document.createElement("option");\n option.value = op.value;\n option.textContent = op.name;\n if(valueStates.search.op === op.value) {\n option.selected = true;\n }\n searchOpEl.appendChild(option);\n });\n\n searchWrapperEl.appendChild(searchOpEl);\n };\n\n const createSearchField = () => {\n searchEl = document.createElement('input');\n searchEl.type = 'text';\n searchEl.id = 'search-input';\n searchEl.placeholder = 'Search strings...'\n\n searchEl.addEventListener('input', function () {\n if(valueStates.search.value !== this.value) {\n valueStates.search.value = this.value;\n renderContextTiles();\n }\n });\n\n searchWrapperEl.appendChild(searchEl);\n };\n\n createLabel();\n createOperatorFilter();\n createSearchField();\n titleSection.appendChild(searchWrapperEl);\n };\n\n const createLocaleFilter = () => {\n const localeFilterWrapper = document.createElement('div');\n localeFilterWrapper.classList.add('locale-filter-wrapper');\n const label = document.createElement("label");\n label.textContent = "Locale: ";\n label.setAttribute("for", "locale-filter");\n\n const select = document.createElement("select");\n select.id = "locale-filter";\n select.addEventListener("change", function () {\n currentLocale = this.value;\n if (valueStates.locale !== this.value) {\n valueStates.locale = this.value;\n valueStates.search.op = operatorOpts[0].value;\n valueStates.search.value = '';\n if(searchEl) {\n searchEl.value = '';\n }\n if(searchOpEl) {\n searchOpEl.value = operatorOpts[0].value;\n }\n renderContextTiles();\n }\n });\n\n const options = [...DataStore.getLocales()];\n options.forEach((locale) => {\n const option = document.createElement("option");\n option.value = locale;\n option.textContent = locale;\n if(valueStates.locale === locale) {\n option.selected = true;\n }\n select.appendChild(option);\n });\n\n localeFilterWrapper.appendChild(label);\n localeFilterWrapper.appendChild(select);\n titleSection.appendChild(localeFilterWrapper);\n };\n\n createTitle();\n createSearchField();\n createLocaleFilter();\n\n document.querySelector('#overview .common-info .section').appendChild(wrapperEl);\n return tilesEl;\n };\n\n const createTile = (idx, page, imagePath) => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('tile-container');\n\n const imageEl = document.createElement('img');\n imageEl.classList.add('image');\n imageEl.src = getImagePath(imagePath);\n\n const nameWrapper = document.createElement('div');\n const name = page;\n nameWrapper.classList.add('name');\n nameWrapper.title = name\n\n const nameEl = document.createElement('span');\n nameEl.innerHTML = name;\n nameWrapper.appendChild(nameEl);\n\n wrapperEl.appendChild(imageEl);\n wrapperEl.appendChild(nameWrapper);\n wrapperEl.addEventListener('click', () => openContextViewer(idx, valueStates.locale));\n return wrapperEl;\n };\n const createPageTiles = (container) => {\n let counter = 0;\n for(const [page, imagePath] of entries) {\n container.appendChild(createTile(counter, page, imagePath));\n counter++;\n }\n };\n\n const openContextViewer = (idx, locale) => {\n let loaderEl, imageViewerEl, imageViewerMainWrapperEl, imagesSplitViewEl, sourceImageEl, targetImageEl, smartCompareSwitchEl, smartCompareKnobEl, pageCountEl, pageNameEl, zoomInEl, zoomOutEl, disableSmartCompare = false;\n const data = [...entries];\n const pageOpts = [...data.map(d => d[0])];\n const anchorEl = document.createElement('div');\n anchorEl.classList.add('context-image-viewer');\n const fieldStates = {search: {op: operatorOpts[0].value, value: ''}, filters: {page: ''}, smartCompare: false, highlightStrings: false, zoomLevel: 1, offsets: {x: 0, y: 0, moveX: 0, moveY: 0}, dragging: {isActive: false, isSource: false, isTarget: false}};\n const valueStates = {page: '', currentIndex: idx, imagePath: ''};\n const getTotalSize = () => data.length;\n\n const createCloseBtn = () => {\n const closeBtnEl = document.createElement('span');\n closeBtnEl.innerHTML = '×';\n closeBtnEl.classList.add('close-btn');\n closeBtnEl.addEventListener('click', () => anchorEl.remove());\n closeBtnEl.title = 'Close image viewer'\n anchorEl.appendChild(closeBtnEl);\n };\n\n const createLoader = () => {\n loaderEl = document.createElement('div');\n loaderEl.classList.add('loader');\n loaderEl.classList.add('--hidden');\n loaderEl.innerHTML = 'Loading...';\n anchorEl.appendChild(loaderEl);\n };\n\n const showLoader = (show, time = 1) => show ? loaderEl.classList.remove('--hidden') : time > 0 ? setTimeout(()=> loaderEl.classList.add('--hidden'), time) : loaderEl.classList.add('--hidden');\n\n const loadData = () => {\n const updateDependencies = () => {\n if(DataStore.isDefaultLocale(currentLocale)) {\n disableSmartCompare = true;\n }\n else if(!DataStore.hasDefaultLocaleData()) {\n disableSmartCompare = true;\n }\n else {\n const defaultLocaleData = DataStore.getPages(DataStore.getDefaultLocale());\n const defaultLocalePages = Object.keys(defaultLocaleData);\n if(defaultLocalePages.length === 0) {\n disableSmartCompare = true;\n }\n else if(!defaultLocalePages.includes(data[valueStates.currentIndex][0])) {\n disableSmartCompare = true;\n }\n else {\n disableSmartCompare = false;\n }\n }\n };\n const setPageName = () => {\n valueStates.page = data[valueStates.currentIndex][0];\n };\n\n updateDependencies();\n setPageName();\n\n };\n\n const renderData = (loadingMaskRemovalDelay = 0, resetZoom = false, showLoadingMask = true) => {\n const renderPageTitle = () => {\n const renderPageCount = () => {\n pageCountEl.innerHTML = \`#\${valueStates.currentIndex + 1}/\${getTotalSize()}\`;\n };\n const renderPageName = () => {\n pageNameEl.innerHTML = valueStates.page;\n pageNameEl.title = valueStates.page;\n };\n\n renderPageCount();\n renderPageName();\n };\n\n const renderSmartCompareValue = () => {\n if(disableSmartCompare) {\n !smartCompareSwitchEl.parentElement.classList.contains('--hidden') && smartCompareSwitchEl.parentElement.classList.add('--hidden');\n }\n else {\n smartCompareSwitchEl.parentElement.classList.contains('--disabled') && smartCompareSwitchEl.parentElement.classList.remove('--hidden');\n smartCompareSwitchEl.style.backgroundColor = fieldStates.smartCompare ? "green" : "gray";\n smartCompareKnobEl.style.left = fieldStates.smartCompare ? "55%" : "5%";\n }\n };\n\n const renderImages = (loadingMaskDelay) => {\n let hasSourceLoaded, hasTargetLoaded;\n const entry = data[valueStates.currentIndex];\n const cursor = fieldStates.zoomLevel > 1 ? fieldStates.dragging.isActive ? 'grabbing' : 'grab' : 'default';\n const css = fieldStates.zoomLevel === 1 ? 'translate(0, 0) scale(1)'\n : \`translate(\${fieldStates.offsets.moveX}px, \${fieldStates.offsets.moveY}px) scale(\${fieldStates.zoomLevel})\`;\n\n if(fieldStates.smartCompare) {\n sourceImageEl.parentElement.classList.remove('--hidden');\n sourceImageEl.src = getImagePath(entry[1], DataStore.getDefaultLocale());\n sourceImageEl.alt = entry[0];\n sourceImageEl.style.transform = css;\n targetImageEl.parentElement.querySelector('.locale-name-bar').style.left = '50.9rem';\n\n if(showLoadingMask) {\n sourceImageEl.onload = () => {\n let interval;\n interval = setInterval(() => {\n if(hasTargetLoaded) {\n clearInterval(interval);\n showLoader(false, loadingMaskDelay);\n } \n }, 10);\n };\n targetImageEl.onload = () => {\n hasTargetLoaded = true;\n };\n }\n }\n else {\n sourceImageEl.parentElement.classList.add('--hidden');\n targetImageEl.parentElement.querySelector('.locale-name-bar').style.left = '2rem';\n\n if(showLoadingMask) {\n targetImageEl.onload = () => {\n showLoader(false, loadingMaskDelay);\n };\n }\n }\n\n targetImageEl.src = getImagePath(entry[1]);\n targetImageEl.alt = entry[0];\n targetImageEl.style.transform = css;\n\n sourceImageEl.parentElement.style.cursor = cursor;\n targetImageEl.parentElement.style.cursor = cursor;\n };\n\n const resetZoomBtns = () => {\n const zoomOutBtn = document.querySelector('.viewer .zoom-out-btn');\n const zoomInBtn = document.querySelector('.viewer .zoom-in-btn');\n if (!zoomOutBtn.classList.contains('--disbaled')) {\n zoomOutBtn.classList.add('--disabled');\n }\n if (zoomInBtn.classList.contains('--disabled')) {\n zoomInBtn.classList.remove('--disabled');\n }\n fieldStates.zoomLevel = 1;\n fieldStates.offsets.x = 0;\n fieldStates.offsets.y = 0;\n fieldStates.offsets.moveX = 0;\n fieldStates.offsets.moveY = 0;\n };\n\n showLoadingMask && showLoader(true);\n resetZoom && resetZoomBtns();\n loadData();\n renderPageTitle();\n renderSmartCompareValue();\n renderImages(loadingMaskRemovalDelay);\n };\n\n const createImageViewer = () => {\n imageViewerEl = document.createElement('div');\n imageViewerEl.classList.add('viewer');\n\n imageViewerMainWrapperEl = document.createElement('div');\n imageViewerMainWrapperEl.classList.add('wrapper');\n imageViewerEl.appendChild(imageViewerMainWrapperEl);\n anchorEl.appendChild(imageViewerEl);\n\n createNavigationButtons();\n createZoomButtons();\n createToolbar();\n createSplitView();\n renderData(300);\n document.body.appendChild(anchorEl);\n };\n\n const createNavigationButtons = () => {\n let prevBtn, nextBtn;\n\n const createPrevBtn = () => {\n prevBtn = document.createElement('div');\n prevBtn.classList.add('prev-btn');\n prevBtn.title = 'Show previous context';\n\n const prevIconEl = document.createElement('span');\n prevIconEl.innerHTML = '<';\n prevBtn.appendChild(prevIconEl);\n imageViewerEl.appendChild(prevBtn);\n\n prevBtn.addEventListener('click', () => canMovePrev() && movePrev());\n };\n const createNextBtn = () => {\n nextBtn = document.createElement('div');\n nextBtn.classList.add('next-btn');\n nextBtn.title = 'Show next context';\n\n const nextIconEl = document.createElement('span');\n nextIconEl.innerHTML = '>';\n nextBtn.appendChild(nextIconEl);\n imageViewerEl.appendChild(nextBtn);\n nextBtn.addEventListener('click', () => canMoveNext() && moveNext());\n };\n const canMoveNext = () => valueStates.currentIndex < getTotalSize() - 1;\n const canMovePrev = () => valueStates.currentIndex > 0;\n const moveNext = () => {\n valueStates.currentIndex++;\n determineBtnsAccessibility();\n renderData(0, true, true);\n };\n const movePrev = () => {\n valueStates.currentIndex--;\n determineBtnsAccessibility();\n renderData(0, true, true);\n };\n const disableBtn = (btn) => btn === 'prev' ? prevBtn.classList.add('--disabled') : nextBtn.classList.add('--disabled');\n const enableBtn = (btn) => btn === 'prev' ? prevBtn.classList.remove('--disabled') : nextBtn.classList.remove('--disabled');\n const isBtnDisabled = (btn) => btn === 'prev' ? prevBtn.classList.contains('--disabled') : nextBtn.classList.contains('--disabled');\n const determineNextBtnAccessibility = () => {\n if(!canMoveNext()) {\n !isBtnDisabled('next') && disableBtn('next');\n }\n else if(isBtnDisabled('next')) {\n enableBtn('next');\n }\n };\n const determinePrevBtnAccessibility = () => {\n if(!canMovePrev()) {\n !isBtnDisabled('prev') && disableBtn('prev');\n }\n else if(isBtnDisabled('prev')) {\n enableBtn('prev');\n }\n };\n const determineBtnsAccessibility = () => {\n determinePrevBtnAccessibility();\n determineNextBtnAccessibility();\n };\n\n createPrevBtn();\n createNextBtn();\n determineBtnsAccessibility();\n };\n\n const createZoomButtons = () => {\n const maxLevel = 10;\n const baseLevel = 1;\n const levelStep = 1;\n let zoomInBtn, zoomOutBtn;\n\n const canZoomIn = () => fieldStates.zoomLevel + 0.5 <= maxLevel;\n const canZoomOut = () => fieldStates.zoomLevel - 0.5 >= baseLevel;\n const isBtnDisabled = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.contains('--disabled') : zoomOutBtn.classList.contains('--disabled');\n const disableBtn = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.add('--disabled') : zoomOutBtn.classList.add('--disabled');\n const enableBtn = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.remove('--disabled') : zoomOutBtn.classList.remove('--disabled');\n const determineZoomInBtnAccessibility = () => {\n if(!canZoomIn()) {\n !isBtnDisabled('zoom-in') && disableBtn('zoom-in');\n }\n else if(isBtnDisabled('zoom-in')) {\n enableBtn('zoom-in');\n }\n };\n const determineZoomOutBtnAccessibility = () => {\n if(!canZoomOut()) {\n !isBtnDisabled('zoom-out') && disableBtn('zoom-out');\n }\n else if(isBtnDisabled('zoom-out')) {\n enableBtn('zoom-out');\n }\n };\n const determineBtnsAccessibility = () => {\n determineZoomInBtnAccessibility();\n determineZoomOutBtnAccessibility();\n };\n const zoomIn = () => {\n fieldStates.zoomLevel += 0.5;\n determineBtnsAccessibility();\n renderData(0, false, false);\n };\n const zoomOut = () => {\n fieldStates.zoomLevel -= 0.5;\n determineBtnsAccessibility();\n renderData(0, false, false);\n }\n const resetZoomLevel = () => {\n fieldStates.zoomLevel = 1;\n determineBtnsAccessibility();\n };\n const createZoomInBtn =() => {\n zoomInBtn = document.createElement('div');\n zoomInBtn.classList.add('zoom-in-btn');\n zoomInBtn.title = 'Zoom In';\n\n const zoomInIconEl = document.createElement('span');\n zoomInIconEl.innerHTML = '+';\n zoomInBtn.appendChild(zoomInIconEl);\n imageViewerEl.appendChild(zoomInBtn);\n zoomInBtn.addEventListener('click', () => canZoomIn() && zoomIn());\n };\n\n const createZoomOutBtn = () => {\n zoomOutBtn = document.createElement('div');\n zoomOutBtn.classList.add('zoom-out-btn');\n zoomOutBtn.title = 'Zoom Out';\n\n const zoomOutIconEl = document.createElement('span');\n zoomOutIconEl.innerHTML = '-';\n zoomOutBtn.appendChild(zoomOutIconEl);\n imageViewerEl.appendChild(zoomOutBtn);\n zoomOutBtn.addEventListener('click', () => canZoomOut() && zoomOut());\n };\n\n createZoomInBtn();\n createZoomOutBtn();\n resetZoomLevel();\n };\n\n const createToolbar = () => {\n const toolbarEl = document.createElement('div');\n toolbarEl.classList.add('toolbar');\n\n const createPageTitle = () => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('page-title-wrapper');\n\n pageCountEl = document.createElement('div');\n pageCountEl.classList.add('page-count');\n\n const pageNameWrapperEl = document.createElement('div');\n pageNameWrapperEl.classList.add('page-title');\n\n pageNameEl = document.createElement('span');\n pageNameWrapperEl.appendChild(pageNameEl);\n\n wrapperEl.appendChild(pageCountEl);\n wrapperEl.appendChild(pageNameWrapperEl);\n\n toolbarEl.appendChild(wrapperEl);\n };\n\n const createSmartCompateBtn = () => {\n const wrapperEl = document.createElement("div");\n wrapperEl.classList.add('smart-compare-wrapper');\n\n const label = document.createElement("span");\n label.textContent = "Smart compare: ";\n\n smartCompareSwitchEl = document.createElement("div");\n smartCompareSwitchEl.classList.add('switch-wrapper');\n wrapperEl.title = 'Compare with source locale context image';\n\n smartCompareKnobEl = document.createElement("div");\n smartCompareKnobEl.classList.add('switch-knob');\n\n smartCompareSwitchEl.addEventListener("click", function () {\n if(disableSmartCompare) {\n return;\n }\n fieldStates.smartCompare = !fieldStates.smartCompare;\n renderData(300, true, true);\n });\n\n wrapperEl.appendChild(label);\n smartCompareSwitchEl.appendChild(smartCompareKnobEl);\n wrapperEl.appendChild(smartCompareSwitchEl);\n toolbarEl.appendChild(wrapperEl);\n };\n\n createPageTitle();\n createSmartCompateBtn();\n\n imageViewerMainWrapperEl.appendChild(toolbarEl);\n };\n\n const createSplitView = (enableSplitView = false) => {\n const clampImage = (value, min, max) => {\n return Math.max(min, Math.min(value, max));\n };\n\n const startDrag = (event, img) => {\n if(fieldStates.zoomLevel === 1) {\n return;\n }\n\n fieldStates.dragging.isActive = true;\n fieldStates.dragging.isSource = img === 'source';\n fieldStates.dragging.isTarget = img === 'target';\n fieldStates.offsets.x = event.clientX;\n fieldStates.offsets.y = event.clientY;\n sourceImageWrapper.style.cursor = "grabbing";\n targetImageWrapper.style.cursor = "grabbing";\n };\n\n const stopDrag = () => {\n const cursor = fieldStates.zoomLevel === 1 ? 'default' : 'grab';\n fieldStates.dragging.isActive = false;\n fieldStates.dragging.isSource = false;\n fieldStates.dragging.isTarget = false;\n sourceImageWrapper.style.cursor = cursor;\n targetImageWrapper.style.cursor = cursor;\n };\n\n const dragMove = (event) => {\n if (!fieldStates.dragging.isActive || fieldStates.zoomLevel === 1) {\n return;\n }\n\n const dx = (event.clientX - fieldStates.offsets.x) * fieldStates.zoomLevel;\n const dy = (event.clientY - fieldStates.offsets.y) * fieldStates.zoomLevel;\n fieldStates.offsets.moveX += dx;\n fieldStates.offsets.moveY += dy;\n\n const wrapper = fieldStates.dragging.isSource ? sourceImageWrapper : targetImageWrapper;\n const image = fieldStates.dragging.isSource ? sourceImageEl : targetImageEl;\n const viewerWidth = wrapper.clientWidth;\n const viewerHeight = wrapper.clientHeight;\n const imgWidth = image.width * fieldStates.zoomLevel;\n const imgHeight = image.height * fieldStates.zoomLevel;\n\n const minX = -(imgWidth - viewerWidth) / 2;\n const maxX = (imgWidth - viewerWidth) / 2;\n const minY = -(imgHeight - viewerHeight) / 2;\n const maxY = (imgHeight - viewerHeight) / 2;\n\n fieldStates.offsets.moveX = clampImage(fieldStates.offsets.moveX, minX, maxX);\n fieldStates.offsets.moveY = clampImage(fieldStates.offsets.moveY, minY, maxY);\n\n fieldStates.offsets.x = event.clientX;\n fieldStates.offsets.y = event.clientY;\n\n renderData(0, false, false);\n };\n\n const imageSectionEl = document.createElement('div');\n imageSectionEl.classList.add('image-section');\n\n imagesSplitViewEl = document.createElement('div');\n imagesSplitViewEl.classList.add('split-view');\n\n const sourceImageWrapper = document.createElement('div');\n const targetImageWrapper = document.createElement('div');\n const sourceLocaleNameBar = document.createElement('div');\n const sourceLocaleNameEl = document.createElement('span');\n const targetLocaleNameBar = document.createElement('div');\n const targetLocaleNameEl = document.createElement('span');\n sourceLocaleNameBar.classList.add('locale-name-bar');\n targetLocaleNameBar.classList.add('locale-name-bar');\n sourceLocaleNameEl.innerHTML = \`\${DataStore.getDefaultLocale()} Image\`;\n targetLocaleNameEl.innerHTML = \`\${locale} Image\`;\n\n sourceImageWrapper.classList.add('source-image-wrapper');\n targetImageWrapper.classList.add('target-image-wrapper');\n\n\n sourceImageEl = document.createElement('img');\n sourceImageEl.classList.add('source-image');\n sourceImageWrapper.classList.add('--hidden');\n\n targetImageEl = document.createElement('img');\n targetImageEl.classList.add('target-image');\n\n sourceImageWrapper.addEventListener("mousedown", (e) => startDrag(e, 'source'));\n targetImageWrapper.addEventListener("mousedown", (e) => startDrag(e, 'target'));\n sourceImageWrapper.addEventListener("mouseup", (e) => stopDrag(e, 'source'));\n targetImageWrapper.addEventListener("mouseup", (e) => stopDrag(e, 'target'));\n document.addEventListener("mousemove", dragMove);\n\n sourceImageWrapper.appendChild(sourceImageEl);\n targetImageWrapper.appendChild(targetImageEl);\n sourceLocaleNameBar.appendChild(sourceLocaleNameEl);\n sourceImageWrapper.appendChild(sourceLocaleNameBar);\n targetLocaleNameBar.appendChild(targetLocaleNameEl);\n targetImageWrapper.appendChild(targetLocaleNameBar);\n\n imagesSplitViewEl.appendChild(sourceImageWrapper);\n imagesSplitViewEl.appendChild(targetImageWrapper);\n\n imageSectionEl.appendChild(imagesSplitViewEl);\n imageViewerMainWrapperEl.appendChild(imageSectionEl);\n };\n\n loadData();\n createCloseBtn();\n createLoader();\n createImageViewer();\n };\n\n renderContextTiles();\n };\n\n if(DataStore.isContextRun()) {\n title = 'Context Creation Details';\n info = \`\${window.isStandaloneReport ? 'Created' : 'Imported'} context screenshots for the locales \${DataStore.getLocales().map(locale => {\n const localeStats = DataStore.getStatByLocale(locale);\n return \`<strong>\${locale}</strong>(<em>\${localeStats?.context || 0} contexts</em>, <em>\${localeStats?.reviewed || 0} strings</em>)\`;\n }).join(', ')}\${!window.isStandaloneReport ? \`with version <strong>\${DataStore.getReportFolderTimestamp()}</strong>\` : ''}.\`;\n renderPagesSection();\n }\n else {\n const locales = DataStore.getLocales();\n title = 'Sanity run details';\n info = \`Sanity run has successfully covered \${DataStore.getStatByLocale(locales[0])?.reviewed || 0} pages for the locales \${locales.map(locale => {\n return \`<strong>\${locale}</em>\`;\n }).join(', ')}.\`;\n }\n\n titleEl.textContent = title;\n infoEl.innerHTML = info;\n }\n\n return;\n }\n else {\n document.querySelector('#overview .common-info')?.remove();\n\n let overviewChart,\n stringChart,\n dateTimeChart,\n numberChart,\n ignoredChart;\n\n window.persistOverviewCharts = () => {\n if(DataStore.isContextRun() || DataStore.isSanityRun()) {\n return;\n }\n\n const reset = (chart) => {\n chart.resetSeries(true, true);\n };\n\n reset(overviewChart);\n reset(stringChart);\n reset(dateTimeChart);\n reset(numberChart);\n reset(ignoredChart);\n };\n\n renderOverallChart();\n renderStringChart();\n renderIgnoredChart();\n renderNumberChart();\n renderDatetimeChart();\n\n function renderOverallChart() {\n const overallStats = DataStore.getOverallStats();\n const reviewed = overallStats.reviewed;\n const locales = DataStore.getLocales().length;\n const type = {\n I18NED: 'Internationalized',\n NOT_I18NED: 'Not Internationalized',\n SKIPPED: 'Skipped'\n },\n valueMap = {\n [type.I18NED]:\n overallStats.localizedStrings +\n overallStats.partialStrings +\n overallStats.untranslatedStrings +\n overallStats.localizedDateTime +\n overallStats.localizedDate +\n overallStats.localizedTime +\n overallStats.localizedNumber +\n overallStats.localizedCurrency +\n overallStats.localizedPercentage,\n [type.NOT_I18NED]:\n overallStats.notLocalizedStrings +\n overallStats.notLocalizedDateTime +\n overallStats.notLocalizedDate +\n overallStats.notLocalizedTime +\n overallStats.notLocalizedNumber +\n overallStats.notLocalizedCurrency +\n overallStats.notLocalizedPercentage,\n [type.SKIPPED]:\n overallStats.dnt +\n overallStats.ignored +\n overallStats.autoIgnored +\n overallStats.userInput\n },\n percentageCalc = (value) => {\n const val = ((value / reviewed) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n };\n\n var options = {\n series: [\n {\n name: '',\n data: [\n percentageCalc(valueMap[type.I18NED]),\n percentageCalc(valueMap[type.NOT_I18NED]),\n percentageCalc(valueMap[type.SKIPPED])\n ]\n }\n ],\n chart: {\n type: 'bar',\n height: 200,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n plotOptions: {\n bar: {\n horizontal: true,\n borderRadius: 3,\n dataLabels: {\n position: 'right'\n }\n }\n },\n title: {\n text: \`Total Reviewed strings in \${locales} locales: \${reviewed}\`,\n align: 'center',\n floating: true,\n style: {\n fontSize: '14px',\n fontWeight: 'bold'\n }\n },\n dataLabels: {\n enabled: true,\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n xaxis: {\n categories: [type.I18NED, type.NOT_I18NED, type.SKIPPED],\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n yaxis: {\n labels: {\n style: {\n fontSize: '13px'\n }\n }\n },\n colors: [\n (v) => {\n switch (v.dataPointIndex) {\n case 0:\n return '#31E994'; //green\n case 1:\n return '#ff8173'; //red\n case 2:\n return '#f5eb5f'; //yellow\n }\n }\n ],\n tooltip: {\n theme: 'dark',\n x: {\n formatter: (v) => \`Total Number of \${v} instances:\`\n },\n y: {\n formatter: function (v, { dataPointIndex }) {\n let count = 0;\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.I18NED];\n break;\n case 1:\n count = valueMap[type.NOT_I18NED];\n break;\n case 2:\n count = valueMap[type.SKIPPED];\n break;\n }\n return \`\${v}% (\${count} strings)\`;\n }\n }\n }\n };\n\n (overviewChart = new ApexCharts(\n document.querySelector('.overall-quality .graph'),\n options\n )).render();\n }\n\n function renderStringChart() {\n const overallStats = DataStore.getOverallStats();\n const type = {\n LOCALIZED: 'Localized',\n PARTIAL: 'Partially Localized',\n NOT_LOCALIZED: 'Not Localized',\n UNTRANSLATED: 'Untranslated',\n ENCODING: 'Encoding Issue',\n IGNORED: 'Ignored'\n },\n valueMap = {\n [type.LOCALIZED]: overallStats.localizedStrings,\n [type.PARTIAL]: overallStats.partialStrings,\n [type.NOT_LOCALIZED]: overallStats.notLocalizedStrings,\n [type.UNTRANSLATED]: overallStats.untranslatedStrings,\n [type.ENCODING]: overallStats.encoding,\n [type.IGNORED]:\n overallStats.dnt +\n overallStats.ignored +\n overallStats.autoIgnored +\n overallStats.userInput\n },\n total = Object.values(valueMap).reduce(\n (acc, curr) => acc + curr,\n 0\n ),\n data = [\n valueMap[type.LOCALIZED],\n valueMap[type.PARTIAL],\n valueMap[type.NOT_LOCALIZED],\n valueMap[type.IGNORED],\n valueMap[type.ENCODING],\n valueMap[type.UNTRANSLATED]\n ],\n options = {\n series:\n data.reduce((acc, curr) => acc + curr, 0) === 0 ? [] : data,\n chart: {\n width: 430,\n type: 'pie',\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n noData: {\n text: 'No metrics available',\n align: 'left',\n verticalAlign: 'middle',\n offsetX: 0,\n offsetY: -50,\n style: {\n fontSize: '14px'\n }\n },\n labels: [\n type.LOCALIZED,\n type.PARTIAL,\n type.NOT_LOCALIZED,\n type.IGNORED,\n type.ENCODING,\n type.UNTRANSLATED\n ],\n tooltip: {\n theme: 'dark',\n y: {\n formatter: function (v, { config, dataPointIndex }) {\n let count = 0,\n value = Number(\n (v /\n config.series.reduce((acc, curr) => acc + curr, 0)) *\n 100\n ).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.LOCALIZED];\n break;\n case 1:\n count = valueMap[type.PARTIAL];\n break;\n case 2:\n count = valueMap[type.NOT_LOCALIZED];\n break;\n case 3:\n count = valueMap[type.IGNORED];\n break;\n case 4:\n count = valueMap[type.ENCODING];\n break;\n case 5:\n count = valueMap[type.UNTRANSLATED];\n break;\n }\n\n return \`\${value}% (\${count} strings)\`;\n },\n title: {\n formatter: function (a) {\n return \`Total Number of \${a} strings:\`;\n }\n }\n }\n },\n colors: [\n ({ seriesIndex, w }) => {\n const currType = w.config.labels[seriesIndex];\n\n switch (currType) {\n case type.LOCALIZED:\n return '#31E994';\n case type.NOT_LOCALIZED:\n return '#E44242';\n case type.PARTIAL:\n return '#DADF40';\n case type.UNTRANSLATED:\n return '#F44E9E';\n case type.IGNORED:\n return '#AFA6AB';\n case type.ENCODING:\n return '#3AD1F3';\n\n default:\n return '';\n }\n }\n ],\n legend: {\n offsetX: -50,\n width: 170,\n formatter: (seriesName, a, b, c) => {\n let value = ((valueMap[seriesName] / total) * 100).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n return \`\${seriesName} (\${value}%)\`;\n }\n },\n dataLabels: {\n formatter: (v) => \`\${Number(v).toFixed(0)}%\`,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n }\n }\n };\n\n (stringChart = new ApexCharts(\n document.querySelector('.string-quality .graph'),\n options\n )).render();\n }\n\n function renderIgnoredChart() {\n const overallStats = DataStore.getOverallStats();\n const type = {\n DNT: 'DNT',\n IGNORED: 'Ignored',\n USER_INPUTS: 'User Inputs'\n },\n valueMap = {\n [type.DNT]: overallStats.dnt,\n [type.IGNORED]: overallStats.ignored + overallStats.autoIgnored,\n [type.USER_INPUTS]: overallStats.userInput\n },\n total = Object.values(valueMap).reduce(\n (acc, curr) => acc + curr,\n 0\n ),\n data = [\n valueMap[type.DNT],\n valueMap[type.IGNORED],\n valueMap[type.USER_INPUTS]\n ],\n options = {\n series:\n data.reduce((acc, curr) => acc + curr, 0) === 0 ? [] : data,\n chart: {\n width: 400,\n type: 'pie',\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n labels: [type.DNT, type.IGNORED, type.USER_INPUTS],\n tooltip: {\n theme: 'dark',\n y: {\n formatter: function (v, { config, dataPointIndex }) {\n let count = 0,\n value = Number(\n (v /\n config.series.reduce((acc, curr) => acc + curr, 0)) *\n 100\n ).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.DNT];\n break;\n case 1:\n count = valueMap[type.IGNORED];\n break;\n case 2:\n count = valueMap[type.USER_INPUTS];\n break;\n }\n\n return \`\${value}% (\${count} strings)\`;\n },\n title: {\n formatter: function (a) {\n return \`Total Number of \${a} strings:\`;\n }\n }\n }\n },\n colors: [\n ({ seriesIndex, w }) => {\n const currType = w.config.labels[seriesIndex];\n\n switch (currType) {\n case type.DNT:\n return '#31E994';\n case type.IGNORED:\n return '#3AD1F3';\n case type.USER_INPUTS:\n return '#DADF40';\n\n default:\n return '';\n }\n }\n ],\n noData: {\n text: 'No metrics available',\n align: 'center',\n verticalAlign: 'middle',\n offsetX: -70,\n offsetY: -25,\n style: {\n fontSize: '14px'\n }\n },\n legend: {\n offsetX: -40,\n formatter: (seriesName) => {\n let value = ((valueMap[seriesName] / total) * 100).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n return \`\${seriesName} (\${value}%)\`;\n }\n },\n dataLabels: {\n formatter: (v) => \`\${Number(v).toFixed(0)}%\`,\n style: {\n colors: ['#080404b0']\n },\n dropShadow: { enabled: false }\n }\n };\n\n (ignoredChart = new ApexCharts(\n document.querySelector('.skipped-items .graph'),\n options\n )).render();\n }\n\n function renderNumberChart() {\n const overallStats = DataStore.getOverallStats();\n const types = ['Number', 'Currency', 'Percentage'],\n data = {\n number_i18n: overallStats.localizedNumber,\n currency_i18n: overallStats.localizedCurrency,\n percentage_i18n: overallStats.localizedPercentage,\n number_not_i18n: overallStats.notLocalizedNumber,\n currency_not_i18n: overallStats.notLocalizedCurrency,\n percentage_not_i18n: overallStats.notLocalizedPercentage\n },\n total = Object.values(data).reduce((acc, curr) => acc + curr, 0),\n percentageCalc = (v, t) => {\n const val = ((v / t) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n },\n i18nPercentage = percentageCalc(\n data.number_i18n + data.percentage_i18n + data.currency_i18n,\n total\n ),\n nonI18nPercentage = percentageCalc(\n data.number_not_i18n +\n data.percentage_not_i18n +\n data.currency_not_i18n,\n total\n ),\n numberTotal = data.number_i18n + data.number_not_i18n,\n currencyTotal = data.currency_i18n + data.currency_not_i18n,\n percentageTotal = data.percentage_i18n + data.percentage_not_i18n,\n numberI18n = percentageCalc(data.number_i18n, numberTotal),\n currencyI18n = percentageCalc(data.currency_i18n, currencyTotal),\n percentageI18n = percentageCalc(\n data.percentage_i18n,\n currencyTotal\n ),\n numberNotI18n = percentageCalc(data.number_not_i18n, numberTotal),\n currencyNotI18n = percentageCalc(\n data.currency_not_i18n,\n currencyTotal\n ),\n percentageNotI18n = percentageCalc(\n data.percentage_not_i18n,\n currencyTotal\n ),\n options = {\n series: [\n {\n name: 'Internationalized',\n data: [numberI18n, currencyI18n, percentageI18n]\n },\n {\n name: 'Not Internationalized',\n data: [numberNotI18n, currencyNotI18n, percentageNotI18n]\n }\n ],\n chart: {\n type: 'bar',\n height: 250,\n width: 550,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n dataLabels: {\n enabled: true,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n stroke: {\n show: true,\n width: 1,\n colors: ['#fff']\n },\n tooltip: {\n theme: 'dark',\n shared: true,\n intersect: false,\n x: {\n formatter: (type) => \`\${type} instances:\`\n },\n y: {\n formatter: (v, { seriesIndex, dataPointIndex }) => {\n const getI18nCount = (type) => {\n switch (type) {\n case 'number':\n return data.number_i18n;\n case 'currency':\n return data.currency_i18n;\n case 'percentage':\n return data.percentage_i18n;\n }\n },\n getNonI18nCount = (type) => {\n switch (type) {\n case 'number':\n return data.number_not_i18n;\n case 'currency':\n return data.currency_not_i18n;\n case 'percentage':\n return data.percentage_not_i18n;\n }\n },\n getCount = (type) => {\n return seriesIndex === 0\n ? getI18nCount(type)\n : getNonI18nCount(type);\n };\n\n switch (dataPointIndex) {\n case 0:\n return getCount('number');\n case 1:\n return getCount('currency');\n case 2:\n return getCount('percentage');\n }\n }\n }\n },\n xaxis: {\n categories: types\n },\n yaxis: {\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n colors: ['#31E994', '#ff8173'],\n legend: {\n offsetX: -13,\n position: 'right',\n formatter: (seriesName) => {\n if (seriesName === 'Internationalized') {\n return \`\${seriesName} (\${i18nPercentage}%)\`;\n }\n return \`\${seriesName} (\${nonI18nPercentage}%)\`;\n }\n },\n plotOptions: {\n bar: {\n borderRadius: 3,\n columnWidth: '85%'\n }\n }\n };\n\n (numberChart = new ApexCharts(\n document.querySelector('.number-quality .graph'),\n options\n )).render();\n }\n\n function renderDatetimeChart() {\n const overallStats = DataStore.getOverallStats();\n const types = ['Date', 'Time', 'DateTime'],\n data = {\n date_i18n: overallStats.localizedDate,\n time_i18n: overallStats.localizedTime,\n date_time_i18n: overallStats.localizedDateTime,\n date_not_i18n: overallStats.notLocalizedDate,\n time_not_i18n: overallStats.notLocalizedTime,\n date_time_not_i18n: overallStats.notLocalizedDateTime\n },\n total = Object.values(data).reduce((acc, curr) => acc + curr, 0),\n percentageCalc = (v, t) => {\n const val = ((v / t) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n },\n i18nPercentage = percentageCalc(\n data.date_i18n + data.time_i18n + data.date_time_i18n,\n total\n ),\n nonI18nPercentage = percentageCalc(\n data.date_not_i18n + data.time_not_i18n + data.date_time_not_i18n,\n total\n ),\n dateTotal = data.date_i18n + data.date_not_i18n,\n timeTotal = data.time_i18n + data.time_not_i18n,\n dateTimeTotal = data.date_time_i18n + data.date_time_not_i18n,\n dateI18n = percentageCalc(data.date_i18n, dateTotal),\n timeI18n = percentageCalc(data.time_i18n, timeTotal),\n dateTimeI18n = percentageCalc(data.date_time_i18n, dateTimeTotal),\n dateNotI18n = percentageCalc(data.date_not_i18n, dateTotal),\n timeNotI18n = percentageCalc(data.time_not_i18n, timeTotal),\n dateTimeNotI18n = percentageCalc(\n data.date_time_not_i18n,\n dateTimeTotal\n ),\n options = {\n series: [\n {\n name: 'Internationalized',\n data: [dateI18n, timeI18n, dateTimeI18n]\n },\n {\n name: 'Not Internationalized',\n data: [dateNotI18n, timeNotI18n, dateTimeNotI18n]\n }\n ],\n chart: {\n type: 'bar',\n height: 250,\n width: 550,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n dataLabels: {\n enabled: true,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n stroke: {\n show: true,\n width: 1,\n colors: ['#fff']\n },\n tooltip: {\n theme: 'dark',\n shared: true,\n intersect: false,\n x: {\n formatter: (type) => \`\${type} instances:\`\n },\n y: {\n formatter: (v, { seriesIndex, dataPointIndex }) => {\n const getI18nCount = (type) => {\n switch (type) {\n case 'date':\n return data.date_i18n;\n case 'time':\n return data.time_i18n;\n case 'date_time':\n return data.date_time_i18n;\n }\n },\n getNonI18nCount = (type) => {\n switch (type) {\n case 'date':\n return data.date_not_i18n;\n case 'time':\n return data.time_not_i18n;\n case 'date_time':\n return data.date_time_not_i18n;\n }\n },\n getCount = (type) => {\n return seriesIndex === 0\n ? getI18nCount(type)\n : getNonI18nCount(type);\n };\n\n switch (dataPointIndex) {\n case 0:\n return getCount('date');\n case 1:\n return getCount('time');\n case 2:\n return getCount('date_time');\n }\n }\n }\n },\n xaxis: {\n categories: types\n },\n yaxis: {\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n colors: ['#31E994', '#ff8173'],\n legend: {\n offsetX: 4,\n position: 'right',\n formatter: (seriesName) => {\n if (seriesName === 'Internationalized') {\n return \`\${seriesName} (\${i18nPercentage}%)\`;\n }\n return \`\${seriesName} (\${nonI18nPercentage}%)\`;\n }\n },\n plotOptions: {\n bar: {\n borderRadius: 3,\n columnWidth: '85%'\n }\n }\n };\n\n (dateTimeChart = new ApexCharts(\n document.querySelector('.datetime-quality .graph'),\n options\n )).render();\n }\n }\n });\n })();\n // insights content\n (() => {\n if (!DataStore.ranSuccessfully()) {\n document.getElementById('overview').innerHTML =\n 'Unable to prepare zQA automation report for the project.';\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n renderCards();\n\n // helper methods to generate cards and its sections\n function createStatSection(locale, data) {\n const holder = document.createElement('div');\n const table = document.createElement('table');\n const tbody = document.createElement('tbody');\n const name = document.createElement('div');\n const addRow = (name, value) => {\n const row = document.createElement('tr');\n\n const col1 = document.createElement('td');\n col1.classList.add('name');\n col1.textContent = name;\n\n const col2 = document.createElement('td');\n col2.classList.add('value');\n col2.textContent = value;\n\n row.appendChild(col1);\n row.appendChild(col2);\n return row;\n };\n\n if (DataStore.isSanityRun()) {\n tbody.appendChild(addRow('Pages viewed', data.reviewed));\n }\n else {\n const tableData = {\n 'Internationlized Strings':\n data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings,\n 'Not Internationlized Strings': data.notLocalizedStrings,\n 'Internationlized Numbers': data.localizedNumber,\n 'Not Internationlized Numbers': data.notLocalizedNumber,\n 'Internationlized Currency': data.localizedCurrency,\n 'Not Internationlized Currency': data.notLocalizedCurrency,\n 'Internationlized Percentage': data.localizedPercentage,\n 'Not Internationlized Percentage': data.notLocalizedPercentage,\n 'Internationlized Date': data.localizedDate,\n 'Not Internationlized Date': data.notLocalizedDate,\n 'Internationlized Time': data.localizedTime,\n 'Not Internationlized Time': data.notLocalizedTime,\n 'Internationlized DateTime': data.localizedDate,\n 'Not Internationlized DateTime': data.notLocalizedDateTime,\n 'Encoding Issue Strings': data.encoding,\n 'DNT Strings': data.dnt,\n 'Ignored Strings': data.ignored,\n 'Untranslated Strings': data.untranslatedStrings,\n 'Auto Ignored Strings': data.autoIgnored,\n 'User Input Strings': data.userInput,\n 'Total Reviewed': data.reviewed\n };\n\n for (const rowData of Object.entries(tableData)) {\n if (!DataStore.isSanityRun() || !DataStore.isDefaultLocale(locale) || rowData[0] !== 'Untranslated Strings') {\n tbody.appendChild(addRow(rowData[0], rowData[1]));\n }\n }\n }\n\n holder.classList.add('stat');\n name.classList.add('name');\n name.textContent = 'Summary';\n holder.append(name);\n\n table.appendChild(tbody);\n holder.appendChild(table);\n return holder;\n }\n\n function createStatusBarSection(locale, data) {\n const holder = document.createElement('div');\n holder.classList.add('status-bar');\n\n const generateBarEl = (name, total, current) => {\n const getColorClass = (value) => {\n if (value <= 25) {\n return 'red';\n } else if (value <= 50) {\n return 'yellow';\n } else if (value <= 75) {\n return 'blue';\n } else if (value <= 100) {\n return 'green';\n }\n };\n current = (current / total) * 100;\n current = isNaN(current) || !isFinite(current) ? 0 : current;\n const holder = document.createElement('div');\n holder.classList.add('bar-container');\n\n const nameHolder = document.createElement('div');\n nameHolder.classList.add('name');\n nameHolder.textContent = name;\n\n const barHolder = document.createElement('div');\n barHolder.classList.add('bar-holder');\n\n const bar = document.createElement('div');\n bar.classList.add('bar');\n bar.classList.add(\`--\${getColorClass(current)}\`);\n bar.style.width = \`\${current}%\`;\n bar.textContent = \`\${current.toFixed(0)}%\`;\n\n barHolder.appendChild(bar);\n holder.appendChild(nameHolder);\n holder.appendChild(barHolder);\n\n return holder;\n };\n\n const showFull = DataStore.isSanityRun() || DataStore.isDefaultLocale(locale),\n totalI18nCount = showFull\n ? 100\n : data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings +\n data.notLocalizedStrings,\n i18nStrings = showFull\n ? 100\n : data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings,\n totalLocCount = showFull\n ? 100\n : totalI18nCount -\n data.untranslatedStrings -\n data.notLocalizedStrings,\n totalNumberCount = showFull\n ? 100\n : data.localizedNumber +\n data.localizedCurrency +\n data.localizedPercentage +\n data.notLocalizedPercentage +\n data.notLocalizedCurrency +\n data.notLocalizedNumber,\n i18nNumbers = showFull\n ? 100\n : totalNumberCount -\n data.notLocalizedPercentage -\n data.notLocalizedCurrency -\n data.notLocalizedNumber,\n totalI18nDateTimeCount = showFull\n ? 100\n : data.localizedDate +\n data.localizedDateTime +\n data.localizedTime +\n data.notLocalizedDate +\n data.notLocalizedDateTime +\n data.notLocalizedTime,\n i18nDateTime = showFull\n ? 100\n : totalI18nDateTimeCount -\n data.notLocalizedDate -\n data.notLocalizedDateTime -\n data.notLocalizedTime;\n\n holder.appendChild(\n generateBarEl('String I18n level', totalI18nCount, i18nStrings)\n );\n\n holder.appendChild(\n generateBarEl('String L10n level', totalI18nCount, totalLocCount)\n );\n\n holder.appendChild(\n generateBarEl('Number I18n level', totalNumberCount, i18nNumbers)\n );\n\n holder.appendChild(\n generateBarEl(\n 'DateTime I18n level',\n totalI18nDateTimeCount,\n i18nDateTime\n )\n );\n return holder;\n }\n\n function generateCardContents(locale, data) {\n const cardContent = document.createElement('div');\n cardContent.classList.add('card-content');\n\n cardContent.appendChild(createStatusBarSection(locale, data));\n cardContent.appendChild(createStatSection(locale, data));\n return cardContent;\n }\n\n function createCardEl(locale, data) {\n const card = document.createElement('div');\n const title = document.createElement('div');\n card.classList.add('card');\n card.dataset.locale = locale;\n\n if(!window.isStandaloneReport) {\n card.classList.add('--reduced-zoom');\n }\n\n title.classList.add('title');\n title.textContent = \`\${locale} details\`;\n\n // fire menu selection event on clicking the card\n card.addEventListener('click', () => {\n const previousSelectedSubMenu = document.querySelector(\n '.sub-menu-item.selected'\n );\n if (previousSelectedSubMenu) {\n window.previousSelectedLocale =\n previousSelectedSubMenu.dataset.locale;\n }\n\n document\n .querySelector(\`.sub-menu-item[data-locale="\${locale}"]\`)\n .click();\n });\n\n card.appendChild(title);\n card.appendChild(generateCardContents(locale, data));\n return card;\n }\n\n function renderCards() {\n // read from global variable appData;\n const localeInfo = DataStore.getLocalesData();\n\n if (Object.keys(localeInfo).length) {\n const cardHolder = document.querySelector(\n '#insights .cards .holder'\n );\n for (const locale in localeInfo) {\n const cardEl = createCardEl(\n locale,\n DataStore.getStatByLocale(locale)\n );\n cardHolder.appendChild(cardEl);\n }\n }\n }\n });\n })();\n // locale grid\n (() => {\n if (!DataStore.ranSuccessfully()) {\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n let table;\n document.querySelectorAll('.sub-menu-item').forEach((menu) => {\n menu.addEventListener('click', (e) => {\n render(e.currentTarget.dataset.locale);\n });\n });\n\n const updateTitle = (locale) => {\n document.querySelector(\n '#locale .title'\n ).textContent = \`\${locale} \${DataStore.isSanityRun() ? 'Sanity Result' : 'I18n Coverage Details'}\`;\n };\n\n const buildGrid = (locale) => {\n const data = DataStore.getDataByLocale(locale) || {};\n $.fn.dataTable.ext.errMode = 'none';\n const dataSet = [];\n if(DataStore.isSanityRun()) {\n Object.entries(data).map((entry) =>\n entry[1].forEach(\n (v) =>\n dataSet.push([v.description, v.imagePath])\n )\n );\n }\n else {\n Object.entries(data).map((entry) =>\n entry[1].forEach(\n (v) =>\n (v.type !== 'REVIEWED') &&\n dataSet.push([entry[0], v.type, v.description, v.imagePath])\n )\n );\n }\n const containerHeight = document\n .querySelector('#locale')\n .getBoundingClientRect().height;\n const scrollHeight = (75 / 100) * containerHeight;\n table = new DataTable('#locale .coverage-info table', {\n language: {\n lengthMenu: "Show _MENU_ entries per page:"\n },\n columns: DataStore.isSanityRun() ?\n [\n { title: 'Page' },\n { title: 'Screenshot' },\n ] : [\n { title: 'String' },\n { title: 'Type' },\n { title: 'Page' },\n { title: 'Screenshot' }\n ],\n columnDefs: DataStore.isSanityRun() ?\n [\n {\n width: '80%',\n targets: 0\n },\n {\n width: '20%',\n targets: 1,\n orderable: false,\n searchable: false,\n data: null,\n render: function (data) {\n return \`<a href="#" class="view-screenshot-btn" onclick="openImageViewer('\${data[1]}')"><img title="View screenshot for '\${data[0]}'" class="view-screenshot-image"></img></a\`;\n },\n className: 'dt-head-center dt-body-center'\n }\n ] :\n [\n {\n width: '45%',\n targets: 0\n },\n {\n width: '22.5%',\n targets: 1\n },\n {\n width: '22.5%',\n targets: 2\n },\n {\n width: '10%',\n targets: 3,\n orderable: false,\n searchable: false,\n data: null,\n render: function (data) {\n return \`<a href="#" class="view-screenshot-btn" onclick="openImageViewer('\${data[3]}')"><img title="View screenshot for '\${data[2]}'" class="view-screenshot-image"></img></a>\`;\n },\n className: 'dt-head-center dt-body-center'\n }\n ],\n data: dataSet,\n initComplete: function () {\n this.api()\n .columns()\n .every(function () {\n const column = this;\n\n if ((DataStore.isSanityRun() && column[0][0] === 1) || column[0][0] === 3) {\n return;\n }\n\n const select = $(\n '<select class="filter dt-input"><option value="">Filter By</option></select>'\n )\n .appendTo($(column.header()).before())\n .on('change', function () {\n const val = $.fn.dataTable.util.escapeRegex(\n $(this).val()\n );\n\n column\n .search(val ? '^' + val + '$' : '', true, false)\n .draw();\n })\n .on('click', function (e) {\n e.stopPropagation();\n e.stopImmediatePropagation();\n });\n\n column\n .data()\n .unique()\n .sort()\n .each(function (d, j) {\n select.append(\n '<option value="' + d + '">' + d + '</option>'\n );\n });\n });\n },\n sScrollY: scrollHeight,\n pageLength: 25,\n lengthMenu: [10, 25, 50, 100]\n });\n };\n\n const render = (locale) => {\n if (table) {\n table.destroy();\n }\n updateTitle(locale);\n buildGrid(locale);\n };\n });\n })();\n\n // log content\n (() => {\n document.addEventListener('DOMContentLoaded', function () {\n const pre = document.querySelector('#log .log-content pre');\n pre.textContent = 'Loading...';\n document\n .querySelector('li[data-page="log"]')\n ?.addEventListener('click', (e) => {\n pre.textContent = window.logData || (DataStore.hasRuntimeError() ? 'We are facing internal issue. Please try again after sometime.' : 'No log data found!');\n });\n });\n })();`,n=[],a=Oe(Ze.getReportPath(),"logs","log"),r=Ct.isSanityRun()||Ct.isContextRun(),i=[Ct.getQALocale(),...Ct.getOptions()?.supportedLocales||[]].filter(Boolean).map((e=>Re(e))),s=Ae(Ze.getReportPath()).filter((e=>i.includes(e))),o=1===s.length&&s[0]===Re(Ct.getDefaultLocale());let l="";if(s.forEach((t=>(r||t!==e.defaultLocale||t===e.defaultLocale&&o)&&ze(Oe(Ze.getReportPath(),t,be))&&n.push(Oe(t,be).replace(/\\/g,"/")))),ze(a)){const t=[e.appName,...Ct.getL10nReportsPath().split(/[\\/]+/g),Ct.getMode()];t.push(Ze.getReportTimeStamp()),l=Pe(a).replace(new RegExp(Ze.getReportPath().replace(/\\/g,"\\\\"),"g"),Oe(...t)).replace(/\\/g,"\\\\").replace(/`/g,"\\`")}return function({appName:e,reportTime:t,script:n,style:a,body:r,dataFilePaths:i=[],logData:s}){return`<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <meta name="charset" content="charset='UTF-8'" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <meta name="company" content="Zeesoft" />\n <meta name="product" content="zAutomate.AI" />\n <meta name="author" content="Jitendra Maheshwari" />\n <meta name="email" content="jm@zeesoft.com" />\n <meta\n name="description"\n content="zAutomate.AI report"\n />\n <title>${e} - zAutomate.AI report | ${t}</title>\n \x3c!-- 3rd party dependencies --\x3e\n <link\n rel="stylesheet"\n href="https://cdn.datatables.net/2.0.3/css/dataTables.dataTables.min.css"\n />\n ${i.map((e=>`<script\n src="${e}"\n type="text/javascript">\n <\/script>`)).join("")}\n ${s?`<script type="text/javascript" async/>window.logData = \`${s}\`;<\/script>`:""}\n <script\n src="https://code.jquery.com/jquery-3.7.1.slim.min.js"\n integrity="sha256-kmHvs0B+OpCW5GVHUNjv9rOmY0IvSIRcf7zGUDTDQM8="\n crossorigin="anonymous"\n defer\n ><\/script>\n <script src="https://cdn.jsdelivr.net/npm/apexcharts" defer><\/script>\n <script\n src="https://cdn.datatables.net/2.0.3/js/dataTables.min.js"\n defer\n ><\/script>\n <style>\n ${a}\n </style>\n </head>\n <body>\n ${r}\n </body>\n <script type="text/javascript" defer>\n ${n}\n <\/script>\n</html>\n`}({appName:e.appName,reportTime:e.reportTime,body:'<header><div class="logo">zAutomate.AI</div><div class="report-timestamp"><span class="info"></span></div></header>\n <main>\n <div class="sidebar">\n <ul class="menu" id="menu">\n <li class="menu-item" data-page="overview">\n <a href="#">Overview</a>\n </li>\n <li class="menu-item" data-page="log">\n <a href="#">View Log</a>\n </li>\n <li class="menu-item has-submenu" data-page="insights">\n <a href="#" data-page="insights">Insights</a>\n <ul class="submenu"></ul>\n </li>\n </ul>\n <div class="footer"></div>\n </div>\n <div class="content-wrapper">\n <div class="container">\n <div id="overview" class="content">\n <div class="common-info card">\n <span class="title"></span>\n <p class="info"></p>\n <div class="section"></div>\n </div>\n <div class="overall-quality card">\n <span class="title">Overall I18n Coverage</span>\n <div class="section">\n <div class="block">\n <article>\n <p class="desc">\n zI18n-Verify report provides a comprehensive information\n about the I18n and L10n quailty of your application.\n </p>\n <ul class="items">\n <li>\n <h4>Reviewed:</h4>\n <p class="detail">\n Total strings which are captured and processed by\n zI18n-Verify.\n </p>\n </li>\n <li>\n <h4>Internationlized:</h4>\n <p class="detail">\n Different types of value like string, number, date-time\n which are properly Internationalized.\n </p>\n </li>\n <li>\n <h4>Not Internationalized:</h4>\n <p class="detail">\n Values which are not Internationalized. Also includes\n Partially Localized strings and Encoding Issues.\n </p>\n </li>\n <li>\n <h4>Skipped:</h4>\n <p class="detail">\n Values which are skipped from processing because they\n are listed in DNT / Ignored lists or contains Auto\n Ignored pattern / User input pattern.\n </p>\n </li>\n </ul>\n </article>\n </div>\n <div class="block">\n <div class="graph"></div>\n </div>\n </div>\n </div>\n <div class="string-quality card">\n <span class="title">String Coverage</span>\n <div class="graph"></div>\n </div>\n <div class="skipped-items card">\n <span class="title">Skipped Items</span>\n <div class="graph"></div>\n </div>\n <div class="number-quality card">\n <span class="title">Number Coverage</span>\n <div class="graph"></div>\n </div>\n <div class="datetime-quality card">\n <span class="title">DateTime Coverage</span>\n <div class="graph"></div>\n </div>\n </div>\n <div id="page" class="content">\n <span class="title">Pages</span>\n <div class="section">\n <div class="desc"></div>\n </div>\n </div>\n <div id="log" class="content">\n <span class="title">Runtime log</span>\n <div class="log-content"><pre></pre></div>\n </div>\n <div id="insights" class="content">\n <div class="cards">\n <span class="title">Locale Quality</span>\n <div class="holder"></div>\n </div>\n </div>\n <div id="locale" class="content">\n <span class="title">Locale Quality</span>\n <div class="coverage-info">\n <table class="row-border hover order-column stripe"></table>\n </div>\n <div id="imageViewer" class="image-viewer">\n <span class="close-button" onclick="closeImageViewer()" title="close"\n >×</span\n >\n <div class="image-container">\n <img id="imageView" src="" alt="Image" />\n </div>\n </div>\n </div>\n </div>\n </div>\n </main>',script:t,style:"html {\n width: 100vw;\n height: 100vh;\n }\n\n * {\n user-select: none;\n box-sizing: border-box;\n scrollbar-width: thin;\n scroll-behavior: smooth;\n }\n\n body {\n width: 100%;\n height: 100%;\n margin: 0;\n display: flex;\n flex-direction: column;\n font-family: Arial, sans-serif;\n font-family: 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased; /* Webkit-based browsers */\n -moz-osx-font-smoothing: grayscale; /* Firefox on OS X */\n font-smooth: always; /* Non-standard, some browsers */\n }\n\n header {\n background-color: #333;\n height: 8%;\n display: flex;\n }\n\n main {\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 92%\n }\n\n .sidebar {\n width: 16%;\n height: 100%;\n background-color: #333;\n color: #fff;\n padding: 20px;\n box-sizing: border-box;\n float: left;\n }\n\n .logo {\n flex-grow: 1;\n align-content: center;\n margin-left: 2%;\n color: #14cf96;\n font-weight: bold;\n font-size: 1.4em;\n }\n\n .report-timestamp {\n flex-grow: 1;\n height: 100%;\n align-content: center;\n margin-right: 2%;\n }\n\n .report-timestamp .info {\n float: right;\n font-size: 0.9em;\n color: #90a3a9;\n }\n\n .menu {\n list-style-type: none;\n padding: 0;\n margin: 0;\n }\n\n .menu-item {\n margin-bottom: 10px;\n }\n\n .menu-item a {\n color: #fff;\n text-decoration: none;\n display: block;\n padding: 10px;\n border-radius: 5px;\n font-size: 1.05rem;\n }\n\n .menu-item a:hover {\n background-color: #555;\n }\n\n .submenu {\n display: none;\n list-style-type: none;\n padding-left: 20px;\n }\n\n .menu-item.has-submenu > a::after {\n content: '⯆';\n float: right;\n margin-left: 5px;\n }\n\n .menu-item.has-submenu.selected > a::after {\n content: '⯅';\n float: right;\n margin-left: 5px;\n }\n\n .menu-item.selected > a {\n background-color: #555;\n }\n\n .submenu .sub-menu-item {\n margin: 3px auto;\n }\n\n .submenu .sub-menu-item a:hover {\n background-color: #777;\n }\n\n .submenu .sub-menu-item.selected a {\n background-color: #555 !important;\n }\n\n .submenu.active {\n display: block !important;\n }\n\n .view-screenshot-image {\n width: 100px;\n height: 30px;\n content: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgZmlsbD0iIzAwMDAwMCIgaGVpZ2h0PSI4MDBweCIgd2lkdGg9IjgwMHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIA0KCSB2aWV3Qm94PSIwIDAgNDkwIDQ5MCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBkPSJNMjQ1LjIwNCwxMDEuMjY3QzY3LjE3MSwxMDEuMjY3LDAsMjQ1LDAsMjQ1czY2Ljc2MywxNDMuNzMzLDI0NC43OTYsMTQzLjczM0M0MjIuODI5LDM4OC43MzMsNDkwLDI0NSw0OTAsMjQ1DQoJCVM0MjMuMjM4LDEwMS4yNjcsMjQ1LjIwNCwxMDEuMjY3eiBNMjQ0Ljc5NiwzNzMuNDIxYy0xNDYuMzkzLDAtMjEyLjM2Ny0xMDEuMDUzLTIyNy41MDYtMTI4LjMzMw0KCQljMTUuMjQ5LTI2Ljc5Niw4Mi43MzMtMTI4LjUwOCwyMjcuOTE0LTEyOC41MDhjMTQ2LjUxNCwwLDIxMi40NzUsMTAxLjIyLDIyNy41NDMsMTI4LjQNCgkJQzQ1Ny41NzgsMjcyLjIwMywzOTEuMzEzLDM3My40MjEsMjQ0Ljc5NiwzNzMuNDIxeiIvPg0KCTxwYXRoIGQ9Ik0yNDUuMDAzLDE0Ni4xMjRjLTU0LjUyMSwwLTk4Ljg4MSw0NC4zNTItOTguODgxLDk4Ljg3M2MwLDU0LjUyMSw0NC4zNiw5OC44ODEsOTguODgxLDk4Ljg4MXM5OC44NzMtNDQuMzYsOTguODczLTk4Ljg4MQ0KCQlDMzQzLjg3NiwxOTAuNDc3LDI5OS41MjQsMTQ2LjEyNCwyNDUuMDAzLDE0Ni4xMjR6IE0yNDUuMDAzLDMyOC41NjZjLTQ2LjA4LDAtODMuNTY4LTM3LjQ4OS04My41NjgtODMuNTY4DQoJCWMwLTQ2LjA3MiwzNy40ODktODMuNTYxLDgzLjU2OC04My41NjFjNDYuMDcyLDAsODMuNTYxLDM3LjQ4OSw4My41NjEsODMuNTYxQzMyOC41NjQsMjkxLjA3NywyOTEuMDc1LDMyOC41NjYsMjQ1LjAwMywzMjguNTY2eiIvPg0KCTxwYXRoIGQ9Ik0yNDUuMDAzLDE3NS41ODNjLTM4LjI4MSwwLTY5LjQyMiwzMS4xNDEtNjkuNDIyLDY5LjQxNWMwLDM4LjI3NCwzMS4xNDEsNjkuNDE1LDY5LjQyMiw2OS40MTUNCgkJYzM4LjI3NCwwLDY5LjQxNS0zMS4xNDEsNjkuNDE1LTY5LjQxNUMzMTQuNDE3LDIwNi43MjQsMjgzLjI3NywxNzUuNTgzLDI0NS4wMDMsMTc1LjU4M3ogTTI0NS4wMDMsMjk5LjENCgkJYy0yOS44MzMsMC01NC4xMS0yNC4yNy01NC4xMS01NC4xMDJjMC0yOS44MzIsMjQuMjc3LTU0LjEwMiw1NC4xMS01NC4xMDJzNTQuMTAyLDI0LjI3LDU0LjEwMiw1NC4xMDINCgkJQzI5OS4xMDUsMjc0LjgzLDI3NC44MzUsMjk5LjEsMjQ1LjAwMywyOTkuMXoiLz4NCgk8cGF0aCBkPSJNMjQ1LjAwMywyMDYuNjc5Yy0yMS4xMywwLTM4LjMyNiwxNy4xODktMzguMzI2LDM4LjMxOWMwLDIxLjEyOSwxNy4xOTcsMzguMzI2LDM4LjMyNiwzOC4zMjZzMzguMzE5LTE3LjE5NywzOC4zMTktMzguMzI2DQoJCUMyODMuMzIxLDIyMy44NjgsMjY2LjEzMiwyMDYuNjc5LDI0NS4wMDMsMjA2LjY3OXogTTI0NS4wMDMsMjY4LjAxMWMtMTIuNjg4LDAtMjMuMDE0LTEwLjMyNi0yMy4wMTQtMjMuMDE0DQoJCWMwLTEyLjY4OCwxMC4zMjUtMjMuMDA2LDIzLjAxNC0yMy4wMDZjMTIuNjg4LDAsMjMuMDA2LDEwLjMxOCwyMy4wMDYsMjMuMDA2QzI2OC4wMDksMjU3LjY4NiwyNTcuNjkxLDI2OC4wMTEsMjQ1LjAwMywyNjguMDExeiIvPg0KPC9nPg0KPC9zdmc+);\n }\n\n .content-wrapper {\n padding: 1%;\n float: left;\n width: 84%;\n background-color: #f4f4f4;\n overflow: hidden;\n height: 100%;\n }\n\n .content {\n display: none !important;\n height: 100%;\n }\n\n .content .title {\n font-size: 1rem;\n color: #080404e2;\n font-weight: 600;\n }\n\n .content .title::after {\n content: ':';\n }\n\n .content.active {\n display: flex !important;\n flex-direction: column;\n gap: 0.9rem;\n flex-wrap: wrap;\n transition: all 2s ease-in-out;\n }\n\n .container {\n height: 99%;\n overflow: hidden;\n overflow-y: auto;\n }\n\n .sidebar .footer {\n bottom: 1.5%;\n position: absolute;\n font-size: 0.75rem;\n color: #14cf96;\n font-family: system-ui;\n width: inherit;\n }\n\n #overview {\n flex-direction: row;\n justify-content: space-between;\n }\n\n #overview .card {\n display: flex;\n flex-direction: column;\n width: 25rem;\n border: 1px solid #ddd;\n box-shadow: 0 22px 35px -16px rgba(0, 0, 0, 0.3);\n border-radius: 5px;\n background: white;\n padding: 1rem;\n }\n\n #overview .overall-quality {\n flex-basis: 100%;\n line-height: 14px;\n }\n\n #overview .overall-quality .section {\n display: flex;\n flex-direction: row;\n }\n\n #overview .overall-quality .section .block {\n width: 50%;\n }\n\n #overview .overall-quality .section .block article p.desc {\n font-size: 0.85em;\n }\n\n #overview .overall-quality .section .block article .items li {\n font-size: 0.8em;\n }\n\n #overview .overall-quality .section .block article .items li .detail {\n margin-top: -2%;\n }\n\n #overview .overall-quality .section .block .graph {\n margin-top: 1.5em;\n }\n\n #overview .string-quality,\n #overview .number-quality,\n #overview .datetime-quality,\n #overview .skipped-items {\n flex-basis: 48.5%;\n }\n\n #overview .string-quality .graph,\n #overview .number-quality .graph,\n #overview .datetime-quality .graph,\n #overview .skipped-items .graph {\n align-self: center;\n }\n\n #overview .string-quality .graph {\n margin-left: 4rem;\n }\n\n #overview .number-quality .graph,\n #overview .datetime-quality .graph {\n margin-top: -0.9rem;\n margin-left: 4.5rem;\n }\n\n #overview .common-info {\n width: 100%;\n height: 100%;\n }\n\n #overview .common-info .info {\n font-size: 0.9em;\n margin-top: 0.8%;\n }\n\n #overview .common-info .title {\n font-size: 1.1em;\n }\n\n #overview .common-info .section {\n width: 100%;\n height: 100%;\n overflow: hidden;\n padding-top: 3%;\n }\n\n #overview .common-info .section .page-tiles .title-section .locale-filter-wrapper {\n margin-right: 1%;\n font-size: 0.8em;\n font-weight: 600;\n }\n\n #overview .common-info .section .page-tiles .title-section .locale-filter-wrapper #locale-filter {\n font-size: 0.9em;\n height: 1.7em;\n border-radius: 3px\n }\n\n #overview .common-info .section .page-tiles .title-section {\n display: flex;\n align-items: center;\n }\n\n #overview .common-info .section .page-tiles .title-section .title-wrapper {\n flex: 1;\n }\n\n #overview .common-info .section .page-tiles .title-section .search-field-wrapper {\n flex: 0.7;\n font-size: 0.8em;\n font-weight: 600;\n }\n\n #overview .common-info .section .page-tiles {\n display: flex;\n flex-direction: column;\n gap: 1%\n width: 100%;\n height: 100%;\n }\n\n #overview .common-info .section .page-tiles .title {\n flex: 1;\n }\n\n #overview .common-info .section .page-tiles .tiles-list-wrapper {\n width: 100%;\n height: 100%;\n border: 0.5px solid #00000008;\n border-radius: 5px;\n padding: 0;\n margin: 0;\n }\n\n #overview .common-info .section .page-tiles .tiles-list {\n width: 100%;\n height: 100%;\n overflow: hidden;\n overflow-y: auto;\n padding: 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 2.2%;\n justify-content: start;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .empty-context {\n background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA7AAAAOwBeShxvQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALnSURBVFiFxddNiFZlFAfw3zjvSGUfWjRCgdmiTV8IGjlFhBJotQmjdraqqBbZtAjBFoYVBH0tGiQUFCJKDQqpNpW0sJosIvowyEW1s4a+7UOsuS3OubzPvL133hmbefvD5d7nPOd5zsfzP+feS2AEb+MYqnm+jqWtkbTtKvzVB8Od1wmslt5UeAKLzD8W4am0+S7ttPfDeI3T0+bxVmH4hj46UGPhQHryv6FVPO/rs+1boMzAQJ8dqGBBn43+C60G+TW4B4NzZOdvjOFgt8m6MZR40dw3nhe62W3KQH00T2K8d4DTYjXu15DNJgdqjPvv1fEh1uH1k3FgLvAVLm2anC8HlmE5luA7fIZfO3ROzMSBUdkwuuAQHi/GLdyOu3F5h+5xvIlH8F7KbpPk71YFO/Rm9Q+F/nJ8XMx9L96yr+B9EW09N4ahemFTJ1yMtabvA5/jcBofx1IcwWbsF98YNc7GvXgAp6ZjN2OyKQMzRUs78jdwZg/9VZhI/QdpzsAVmmv3MLbm813YLiJfhV9m4PRawYc/cBHdM/Co5rP/XfsMP0nZhmLtQXyA4RyvE5wYLXT25LotTVXwsGD5UJe5LwSpluEyQcb9xfwpWIm3cp/dKSv32o1bcSMnz4E1ue5Ah/xc7cxM5v1ZU494OOXfNmWgiQMVdgrCnZGyzgYzgW0izQP4CQ+ZGuTPeT+r3nQ2HHguda7M8aGOtesFwep+UIlOOFzoXJjyr2n/E5TRnoabRBcsrw2ipomS+1Pw4ZxibW30GdEbPs3x9kLnjpS9LL2rcL3Z49Vcu7WQjYqGU5/5cBq/OseDhc2NA9iEp0W/PiD+E6ZDhecF80fwjsjEtaL8emGbaEJHcAnx8fGY2f2evVZsOJayCdFkmjCYxifFsa1hamlcgBVY2COCSjSbozkewl7BmQovYRc+wo84H9fhPlycgd6ZOnOGBSKtv5k+c1/KyOcL52GLeOcfFbz6RrB9oy6d9R/3ey953ywo7QAAAABJRU5ErkJggg==);\n width: 100%;\n background-repeat: no-repeat;\n height: 100%;\n background-size: 2.5em;\n background-position-x: 50%;\n background-position-y: 43%;\n display: flex;\n justify-content: center;\n align-content: center;\n flex-wrap: wrap;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container {\n width: 23%;\n height: 35%;\n text-align: center;\n display: flex;\n flex-direction: column;\n border: 1px solid #80808030;\n border-radius: 5px;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container:hover {\n transform: scale(1.03);\n box-shadow: 0 22px 35px -16px #00000033;\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .image {\n width: 100%;\n height: 85%;\n object-fit: contain;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .name span {\n width: 75%;\n display: inline-block;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .name {\n width: 100%;\n display: block;\n height: 15%;\n align-content: center;\n background-color: rgb(0 0 0 / 7%);\n border-top: 1px solid #80808030;\n color: #2cb2c1e6;\n -webkit-font-smoothing: antialiased;\n font-variant: all-small-caps;\n font-weight: 600;\n font-size: 0.87em;\n }\n\n #insights .cards {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n }\n\n #insights .cards .holder {\n display: flex;\n flex-direction: row;\n gap: 1.5rem;\n flex-wrap: wrap;\n }\n\n #insights .cards .card {\n display: flex;\n flex-direction: column;\n width: 20rem;\n min-height: 10rem;\n border: 1px solid #ddd;\n box-shadow: 0 22px 35px -16px rgba(0, 0, 0, 0.3);\n border-radius: 5px;\n background: white;\n }\n\n #insights .cards .card:hover {\n transform: scale(1.05);\n box-shadow: 0 22px 35px -16px #07ff7d33;\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n }\n\n #insights .cards .card .title {\n height: 2.5rem;\n font-size: 1rem;\n padding-top: 1rem;\n font-weight: 500;\n background: #0c5ad9d6;\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n padding-left: 1rem;\n color: white;\n }\n\n #insights .cards .card .title::after {\n content: '';\n }\n\n #insights .cards .card .card-content {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n padding: 1rem;\n }\n\n #insights .cards .card .status-bar {\n display: flex;\n flex-direction: column;\n gap: 0.8rem;\n }\n\n #insights .cards .card .status-bar .bar-container .name {\n font-size: 0.78rem;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n #insights .cards .card .status-bar .bar-container .name::after {\n content: ':';\n }\n\n #insights .cards .card .status-bar .bar-container .bar-holder {\n width: 100%;\n height: 10px;\n border: 1px solid #00000038;\n border-radius: 2px;\n }\n\n #insights .cards .card .status-bar .bar-container .bar-holder .bar {\n height: 100%;\n font-size: 0.63rem;\n text-align: right;\n color: rgb(0, 0, 0);\n }\n\n .bar.--green {\n background-color: #1dedaf;\n }\n\n .bar.--red {\n background-color: #fe3e3e;\n }\n\n .bar.--yellow {\n background-color: #faef1f;\n }\n\n .bar.--blue {\n background-color: #77c2ff;\n }\n\n #insights .cards .card .stat {\n display: flex;\n flex-direction: column;\n gap: 0.3rem;\n border: 0.5px solid #00000012;\n border-radius: 6px;\n }\n\n #insights .cards .card .stat table td {\n font-size: 0.78rem;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n #insights .cards .card .stat table td.name {\n width: 70%;\n }\n\n #insights .cards .card .stat table td.value::before {\n content: ': ';\n }\n\n #locale {\n display: flex;\n gap: 0.2em;\n background: white;\n padding: 1em;\n border-radius: 5px;\n flex-wrap: nowrap;\n }\n\n #locale .title {\n content: ':';\n }\n\n #locale .coverage-info {\n width: 100%;\n }\n\n #locale table thead th select.filter {\n margin-left: 1rem;\n max-width: 8rem;\n text-overflow: ellipsis;\n }\n\n #locale .image-viewer {\n display: none;\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.8);\n text-align: center;\n }\n\n #locale .image-container {\n position: relative;\n top: 50%;\n transform: translateY(-50%);\n display: inline-block;\n width: 98vw;\n height: 94vh;\n overflow: hidden;\n overflow-y: auto;\n }\n\n #locale .image-viewer img {\n width: 100%;\n }\n\n #locale .close-button {\n position: absolute;\n top: -0.6rem;\n right: 0.1rem;\n color: #ffffffbf;\n font-size: 2.4rem;\n cursor: pointer;\n z-index: 9999;\n }\n\n #locale .close-button:hover {\n color: white;\n }\n\n #locale table tbody td {\n font-size: 0.9em;\n }\n\n #locale table td .view-screenshot-btn {\n color: blue;\n }\n\n @media (max-width: 768px) {\n .sidebar {\n width: 16rem;\n padding: 10px;\n }\n }\n\n .--hidden {\n display: none !important;\n visibility: hidden !important;\n }\n\n #log {\n display: flex;\n }\n\n #log .title {\n display: block;\n flex: 1;\n }\n\n #log .log-content {\n flex: 99;\n background: white;\n border-radius: 3px;\n overflow: hidden;\n padding: 1em;\n }\n\n #log .log-content pre {\n height: 100%;\n white-space: pre-line;\n word-break: break-all;\n padding: 0;\n margin: 0;\n width: 100%;\n overflow: hidden;\n overflow-y: auto;\n user-select: all;\n }\n\n body .context-image-viewer {\n z-index: 9999;\n width: 100vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n position: fixed;\n top: 0;\n left: 0;\n background: #0000009c;\n }\n\n body .context-image-viewer .close-btn {\n position: absolute;\n right: 1.2%;\n top: 0.65%;\n color: #a7a5a5;\n font-size: 2.8em;\n cursor: pointer;\n z-index: 1;\n }\n\n body .context-image-viewer .close-btn:hover {\n color: #000000a6;\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .loader {\n width: 100vw;\n height: 100vh;\n z-index: 9999;\n top: 0;\n left: 0;\n position: absolute;\n background: #0000009c;\n color: white;\n text-align: center;\n padding-top: 50vh;\n }\n\n body .context-image-viewer .viewer {\n width: 98%;\n height: 96%;\n background-color: white;\n margin: auto;\n position: relative;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn,\n body .context-image-viewer .viewer .zoom-out-btn {\n position: absolute;\n bottom: 10%;\n width: 35px;\n height: 35px;\n border-radius: 50%;\n text-align: center;\n background: #0000001a;\n cursor: pointer;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn {\n left: 51%;\n }\n\n body .context-image-viewer .viewer .zoom-out-btn {\n left: 47%;\n }\n\n body .context-image-viewer .viewer .next-btn,\n body .context-image-viewer .viewer .prev-btn {\n width: 35px;\n height: 35px;\n border-radius: 50%;\n text-align: center;\n background: #0000001a;\n cursor: pointer;\n position: absolute;\n top: 50%;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn:hover,\n body .context-image-viewer .viewer .zoom-out-btn:hover {\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn span,\n body .context-image-viewer .viewer .zoom-out-btn span {\n display: inline-block;\n margin-top: 12%;\n font-size: 1.5em;\n }\n\n body .context-image-viewer .viewer .next-btn:hover,\n body .context-image-viewer .viewer .prev-btn:hover {\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .viewer .next-btn span,\n body .context-image-viewer .viewer .prev-btn span {\n display: inline-block;\n margin-top: 15%;\n font-size: 1.5em;\n }\n\n body .context-image-viewer .viewer .prev-btn {\n left: 1%;\n }\n\n body .context-image-viewer .viewer .next-btn {\n right: 1%;\n }\n\n body .context-image-viewer .viewer .wrapper {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n padding: 1.8%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper .page-title {\n font-weight: 600;\n max-width: 75%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper .page-count {\n font-size: 0.7em;\n }\n\n body #overview .section .page-tiles .search-field-wrapper #search-input {\n width: 55%;\n height: 1.5em;\n margin-left: 0.5%;\n white-space: nowrap;\n text-overflow: ellipsis;\n border-radius: 3px;\n border: 0.1px solid;\n }\n\n body #overview .section .page-tiles .search-field-wrapper #operator-filter {\n width: fit-content;\n height: 1.5em;\n border-radius: 3px;\n }\n\n body #overview .section .page-tiles .search-field-wrapper {\n flex: 33;\n display: flex;\n align-items: center;\n gap: 1%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper .switch-wrapper .switch-knob {\n position: absolute;\n width: 40%;\n height: 86%;\n border-radius: 50%;\n background-color: white;\n top: 50%;\n left: 3%;\n transform: translateY(-50%);\n transition: left 0.3s;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper .switch-wrapper {\n position: relative;\n width: 2.5em;\n height: 1.3em;\n border-radius: 2em;\n background-color: gray;\n cursor: pointer;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper {\n display: flex;\n align-items: center;\n gap: 1%;\n flex: 33;\n justify-content: end;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n flex: 33;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper .locale-name-bar span,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper .locale-name-bar span {\n opacity: 0.6;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper .locale-name-bar,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper .locale-name-bar {\n position: absolute;\n background-color: #8080801f;\n width: 9%;\n height: 3.5%;\n text-align: center;\n margin: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 0.5em;\n bottom: 2.1rem;\n left: 2rem;\n border-radius: 3em;\n color: #00000099;\n font: status-bar;\n font-weight: 500;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper {\n width: 100%;\n height: 100%;\n overflow: hidden;\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n border: 0.1px solid #00000021;\n border-radius: 5px;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper img,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper img {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n transform: translate(0, 0) scale(1);\n transition: transform 0.1s ease-out;\n pointer-events: none;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view {\n width: 100%;\n height: 100%;\n display: flex;\n gap: 0.5%;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section {\n display: flex;\n flex: 95;\n overflow: hidden;\n width: 100%;\n height: 100%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar {\n border: 0.1px solid #00000021;\n border-bottom: 0;\n height: 7%;\n border-radius: 5px;\n padding: 0.5%;\n padding-left: 1%;\n padding-right: 1%;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: stretch;\n flex: 4;\n }\n\n .--reduced-zoom {\n zoom: 0.9123;\n }\n\n .--hide-overflow {\n overflow: hidden;\n }\n\n .--disabled {\n color: #00000014 !important;\n background: #00000014 !important;\n cursor: not-allowed !important;\n transform: none !important;\n transition: none !important;\n }\n\n .--disabled:hover {\n color: #00000014 !important;\n background: #00000014 !important;\n transform: none !important;\n transition: none !important;\n }",dataFilePaths:n,logData:l})}write(e,t,n){$e(Oe(e,`${t}.html`),n,"utf-8")}}class je extends _e{#o;#l=!1;static#c="STATUS";static#d="COUNTS";constructor(){super(ye),this.#u()}#u(){this.#o=new m({style:{headerTop:{left:"[94m╔[0m",mid:"[94m╦[0m",right:"[94m╗[0m",other:"[94m═"},headerBottom:{left:"[94m╟[0m",mid:"[94m╬[0m",right:"[94m╢[0m",other:"[94m═"},tableBottom:{left:"[94m╚[0m",mid:"[94m╩[0m",right:"[94m╝[0m",other:"[94m═[0m"},vertical:"[94m║[0m"},columns:[{name:je.#c,alignment:"left"},{name:je.#d,alignment:"center"}]})}buildContent(){if(ce[le]>0){this.#l=!0;for(const e in ce)e!==le?ce.hasOwnProperty(e)&&this.#o.addRows([{STATUS:e,COUNTS:`${ce[e]}`}]):(this.#o.addRows([{STATUS:"[94m"+"═".repeat(28)+"[0m",COUNTS:"[94m"+"═".repeat(8)+"[0m"}]),this.#o.addRows([{STATUS:"OVERALL_REVIEWED",COUNTS:`${ce[e]}`}]))}}export(){this.#l?this.#o.printTable():qe.info("There are no visbile elements with UI strings")}}class Fe{static#p(e){switch(e){case we:return new Be;case ye:return new je;default:throw new Error("unsupported report type requested")}}static export(e,t,n){const a=Fe.#p(e),r=new ke({appName:Ct.getAppName(),reportTime:(new Date).toLocaleString(),isContext:Ct.isContextRun(),isSanity:Ct.isSanityRun(),defaultLocale:Ct.getDefaultLocale(),reportFolderTimestamp:Ze.getReportTimeStamp()});a.generate(r),a.export(t,n)}}class Ue{static#m;static#g;static init(){Ue.#g=Oe(Ze.getReportPath(),Re(Ct.getLocale())),Ue.#m=Oe(Ue.#g,be),this.#h()}static exists(){return ze(Ue.#m)}static save(e){this.#h();const t=`!window.rawData && (window.rawData={'${Ct.getLocale()}':{}});window.rawData['${Ct.getLocale()}']=${JSON.stringify(e)};`;$e(Ue.#m,t)}static getData(){if(!this.exists())return{};try{return JSON.parse(Pe(Ue.#m).replace(new RegExp(`(\\!window.rawData && \\(window\\.rawData=\\{'${Ct.getLocale()}':\\{\\}\\}\\);window\\.rawData\\['${Ct.getLocale()}'\\]=)|(;$)`,"g"),""))}catch(e){return{}}}static#h(){Me(Ue.#g)}static getCacheDirPath(){return Ue.#g}}class Ve{#f;#v;#w;#y;#b;#L;#E;#S;#D;#x;#I;#C;#T;#z;#N;#M;#P;#A;#O;#$;#R;#k;#_;#B;#j;constructor(e={}){this.#f=e?.localizedStrings||0,this.#v=e?.notLocalizedStrings||0,this.#w=e?.untranslatedStrings||0,this.#y=e?.partialStrings||0,this.#b=e?.localizedDateTime||0,this.#L=e?.localizedDate||0,this.#E=e?.localizedTime||0,this.#S=e?.notLocalizedDateTime||0,this.#D=e?.notLocalizedDate||0,this.#x=e?.notLocalizedTime||0,this.#I=e?.localizedNumber||0,this.#C=e?.notLocalizedNumber||0,this.#T=e?.localizedCurrency||0,this.#z=e?.notLocalizedCurrency||0,this.#N=e?.localizedPercentage||0,this.#M=e?.notLocalizedPercentage||0,this.#P=e?.ignored||0,this.#A=e?.dnt||0,this.#O=e?.autoIgnored||0,this.#$=e?.userInput||0,this.#R=e?.encoding||0,this.#k=e?.truncation||0,this.#_=e?.overlapping||0,this.#B=e?.reviewed||0,this.#j=e?.context||0}getLocalizedStrings(){return this.#f}setLocalizedStrings(e){this.#f=e}getUntranslatedStrings(){return this.#w}setUntranslatedStrings(e){this.#w=e}getNotLocalizedStrings(){return this.#v}setNotLocalizedStrings(e){this.#v=e}getPartialStrings(){return this.#y}setPartialStrings(e){this.#y=e}getLocalizedDateTime(){return this.#b}setLocalizedDateTime(e){this.#b=e}getLocalizedDate(){return this.#L}setLocalizedDate(e){this.#L=e}getLocalizedTime(){return this.#E}setLocalizedTime(e){this.#E=e}getNotLocalizedDateTime(){return this.#S}setNotLocalizedDateTime(e){this.#S=e}getNotLocalizedDate(){return this.#D}setNotLocalizedDate(e){this.#D=e}getNotLocalizedTime(){return this.#x}setNotLocalizedTime(e){this.#x=e}getLocalizedNumber(){return this.#I}setLocalizedNumber(e){this.#I=e}getNotLocalizeNumber(){return this.#C}setNotLocalizeNumber(e){this.#C=e}getCurrency(){return this.#T}setCurrency(e){this.#T=e}getNotLocalizeCurrency(){return this.#z}setNotLocalizeCurrency(e){this.#z=e}getPercentage(){return this.#N}setPercentage(e){this.#N=e}getNotLocalizePercentage(){return this.#M}setNotLocalizePercentage(e){this.#M=e}getIgnored(){return this.#P}setIgnored(e){this.#P=e}getUserInput(){return this.#$}setUserInput(e){this.#$=e}getDnt(){return this.#A}setDnt(e){this.#A=e}getAutoIgnored(){return this.#O}setAutoIgnored(e){this.#O=e}getEncoding(){return this.#R}setEncoding(e){this.#R=e}getTruncation(){return this.#k}setTruncation(e){this.#k=e}getOverlapping(){return this.#_}setOverlapping(e){this.#_=e}getReviewed(){return this.#B}setReviewed(e){this.#B=e}getContext(){return this.#j}setContext(e){this.#j=e}incrementByType(e){switch(e){case B:++this.#f;break;case Q:++this.#v;break;case ie:++this.#w;break;case j:++this.#y;break;case se:++this.#O;break;case re:++this.#P;break;case ae:++this.#A;break;case ne:++this.#R;break;case W:++this.#L;break;case G:++this.#E;break;case H:++this.#b;break;case X:++this.#D;break;case J:++this.#x;break;case K:++this.#S;break;case F:++this.#I;break;case Z:++this.#C;break;case U:++this.#T;break;case q:++this.#z;break;case V:++this.#N;break;case Y:++this.#M;break;case ee:++this.#k;break;case te:++this.#_;break;case le:++this.#B;break;case oe:++this.#$;break;case"context":++this.#j}}toJSON(){return{localizedStrings:this.#f,notLocalizedStrings:this.#v,untranslatedStrings:this.#w,partialStrings:this.#y,localizedDateTime:this.#b,localizedDate:this.#L,localizedTime:this.#E,notLocalizedDateTime:this.#S,notLocalizedDate:this.#D,notLocalizedTime:this.#x,localizedNumber:this.#I,notLocalizedNumber:this.#C,localizedCurrency:this.#T,notLocalizedCurrency:this.#z,localizedPercentage:this.#N,notLocalizedPercentage:this.#M,ignored:this.#P,dnt:this.#A,autoIgnored:this.#O,userInput:this.#$,encoding:this.#R,truncation:this.#k,overlapping:this.#_,reviewed:this.#B,context:this.#j}}}class We{#F;constructor(e={}){this.#F=new Ve(e.stats)}getStats(){return this.#F}setStats(e){this.#F=e}toJSON(){return{stats:this.#F}}}class Ge{#U;#V;#W;constructor(e){this.#U=e?.type||"",this.#V=e?.imagePath||"",this.#W=e?.description||""}getType(){return this.#U}setType(e){this.#U=e}getImagePath(){return this.#V}setImagePath(e){this.#V=e}getDescription(){return this.#W}setDescription(e){this.#W=e}toJSON(){return{type:this.#U,description:this.#W,imagePath:this.#V}}}class He{#G={};constructor(e){if(e)for(const t in e)e[t].forEach((e=>{this.#G[t]||(this.#G[t]=[]),this.#G[t].push(new Ge(e))}))}getStringData(e){return this.#G[e]||[]}setStringData(e,t){return!this.#G[e]&&(this.#G[e]=[]),this.#G[e].push(new Ge(t)),!0}checkAndAdd(e,t){!this.#G[e]&&(this.#G[e]=[]);const n=this.getStringData(e).some((e=>e.getDescription()!==t.description||e.getImagePath()!==t.imagePath||e.getType()!==e.type));return n&&this.setStringData(e,t),n}hasStringData(e){return e in this.#G}static createNew(e,t){return t=Array.isArray(t)?t:[t],new He({data:{[e]:t}})}toJSON(){return this.#G}}class Qe{#H;#Q;constructor(e={}){this.#H=new We(e.metaData),this.#Q=new He(e.stringsData)}getMetaData(){return this.#H}setMetaData(e){this.#H=e}getStringEntry(e){return this.#Q.getStringData(e)}static addString(e,t,n,a){const r=Qe.getInstance(),i=r.#Z(e,t,n,a.replace(/\\/g,"/"));return i&&r.save(),i}#Z(e,t,n,a){let r=!1;const i={type:t,description:n,imagePath:a};return r=this.#Q.hasStringData(e)?this.#Q.checkAndAdd(e,i):this.#Q.setStringData(e,i),r&&this.#H.getStats().incrementByType(t),r}static incrementCountByType(e){const t=Qe.getInstance();t.#H.getStats().incrementByType(e),t.save()}toJSON(){return{metaData:this.#H,stringsData:this.#Q}}save(){Ue.save(this)}remove(){Ue.delete()}static getInstance(){return new Qe(Ue.getData())}}class Ze{static#q;static#Y;static setup(){Ze.#Y||(Ze.#Y=Ze.#X());const e=c.resolve();Ze.#q=Oe(e,Ct.getL10nReportsPath(),Ct.getMode(),Ze.#Y)}static getReportTimeStamp(){return Ze.#Y}static getReportPath(){return Ze.#q}static reviewedReports(e=null,t=null){return Ze.#J(le,e,t)}static getContextPath(){return this.#K()}static[B](e,t){return Ze.#J(B,e,t)}static[Q](e,t){return Ze.#J(Q,e,t)}static[j](e,t){return Ze.#J(j,e,t)}static[ae](e,t){return Ze.#J(ae,e,t)}static[re](e,t){return Ze.#J(re,e,t)}static[se](e,t){return Ze.#J(se,e,t)}static[ie](e,t){return Ze.#J(ie,e,t)}static[ne](e,t){return Ze.#J(ne,e,t)}static[H](e,t){return Ze.#J(H,e,t)}static[K](e,t){return Ze.#J(K,e,t)}static[W](e,t){return Ze.#J(W,e,t)}static[X](e,t){return Ze.#J(X,e,t)}static[G](e,t){return Ze.#J(G,e,t)}static[J](e,t){return Ze.#J(J,e,t)}static[U](e,t){return Ze.#J(U,e,t)}static[q](e,t){return Ze.#J(q,e,t)}static[V](e,t){return Ze.#J(V,e,t)}static[Y](e,t){return Ze.#J(Y,e,t)}static[F](e,t){return Ze.#J(F,e,t)}static[Z](e,t){return Ze.#J(Z,e,t)}static incrementCount(e,t,n,a){ce.hasOwnProperty(e)&&(ce[e]++,Qe.addString(t,e,a,n.replace(Ze.#q,"")))}static#X(){const e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}-${String(e.getHours()).padStart(2,"0")}-${String(e.getMinutes()).padStart(2,"0")}`}static#J=(e,t,n)=>{const a=Oe(Ze.#q,Re(Ct.getLocale()),"screenshots",String(e).toLowerCase());if(Me(a,he),null!=t){const a=Oe(Ze.#q,Re(Ct.getLocale()),"strings");Me(a,he);let r="";const i=Oe(a,e+".txt");if(Ne(i)){Pe(i).includes(n)||(r+=`\n|<<<<<<<<<<<<<------${n}------\x3e>>>>>>>>>>>>|\n`),r+=t}else r+=`\n|<<<<<<<<<<<<<------${n}------\x3e>>>>>>>>>>>>|\n`,r+=t;((e,t,n="utf-8")=>{if(e&&t)try{o(e,t+"\n",{encoding:n,flag:"a"})}catch(e){qe.error(e)}})(i,r)}return a};static#K=()=>{const e=Oe(Ze.#q,Re(Ct.getLocale()),"contexts");return Me(e,he),e};static#ee(e){Fe.export(e,Ze.#q)}static generateHtml(){Ze.#ee(we)}static generateAsciiTable(){Ze.#ee(ye)}}class qe{static#te=!1;static#ne;static#ae=[];static info(e){qe.#re("info",e)}static warn(e){qe.#re("warn",e)}static error(e,t){qe.#re("errr",e,t)}static debug(e){qe.#re("dbug",e)}static#re(e,t,n=""){const a={INFO:"[37m",WARN:"[33m",ERRR:"[31m",DBUG:"[34m"};let r=a[e=e.toUpperCase()]||a.DEFAULT;n=n?` reason: ${n.toString()}`:"";let i=`[${(new Date).toLocaleString()}] [${r}${e}[0m] [${Ct.getLocale()}] - ${t}${n}`;switch(e){case"INFO":console.info(i);break;case"WARN":console.warn(i);break;case"ERRR":console.error(i);break;case"DBUG":console.debug(i);break;default:console.log(i)}i=i.replace(/\x1b\[\d+m/g,""),qe.#ie(`${i}\n`)}static#ie=t=>{if(qe.#te)qe.#ae.push(t),qe.#ne=setInterval((()=>{qe.#te||(clearInterval(qe.#ne),t=qe.#ae.join("\n"),qe.#ae=[],t&&qe.#ie(t))}),10);else{qe.#te=!0;const n=Oe(Ze.getReportPath(),"logs"),a=Oe(n,"log");Me(n,he),e.appendFileSync(a,t),qe.#te=!1}}}class Ye extends Ce{constructor(e){super(e)}async getElement(e,t=!1){return await this.$driver.element.find({selector:e,locateStrategy:e.startsWith("/")?"xpath":"css selector",suppressNotFoundErrors:t})}async getElements(e,t=!1){return await this.$driver.element.findAll({selector:e,locateStrategy:e.startsWith("/")?"xpath":"css selector",suppressNotFoundErrors:t})}async getText(e){return await e.getText()}async getTitle(e){return await e.getAttribute("title")}async getPlaceholderText(e){return await e.getAttribute("placeholder")}async getAltText(e){return await e.getAttribute("alt")}async getTagName(e){return await e.getTagName()}async getValue(e){return await e.getAttribute("value")}async getElementID(e){return await e.getAttribute("id")}async getElementDimension(e){const t=await e.getRect();if(![null,"",void 0].includes(t))return[t.x,t.y,t.height,t.width]}async isElementDisplayed(e){return await this.$driver.isVisible(e)}async isElementPresent(e){return await this.$driver.isPresent(e)}async getElementStrings(e){const t=[];if(this.isElementDisplayed(e)){[{type:"eleTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"titleTxt",value:await this.getTitle(e)},{type:"placeTxt",value:await this.getPlaceholderText(e)},{type:"valueTxt",value:await this.getValue(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)}))}return t}async getElementString(e){let t=null;const n=[this.getText,this.getAltText,this.getTitle,this.getPlaceholderText,this.getValue];for(const a of n)if(t=await a(e),null!==t)return t;return t}async captureScreenshot(e){await Xe(this.$driver,e)}async getElementsCount(e){return e.length}async switchToIFrame(e,t){return new Promise((t=>{this.$driver.frame(e,(function(){t(!0)}))}))}async switchToParentFrame(){return new Promise((e=>this.$driver.frame(null,(function(){e(!0)}))))}pause(e){this.$driver.pause(1e3*e)}async onScreenStringHighlight(e,t){await this.$driver.execute(((e,t)=>{const n=window.getComputedStyle(e).border;e.style.border=`3px solid ${t}`,setTimeout((()=>{e.style.border=n}),40)}),[e,t])}async checkIfIframeIsPresent(){return new Promise((e=>{this.$driver.isPresent({selector:"//iframe",locateStrategy:"xpath",suppressNotFoundErrors:!0},(t=>{e(t.value)}))}))}async checkIfIframeBodyIsEmpty(){return""===(await this.$driver.executeScript((function(){return document.body.innerHTML}))).trim()}}const Xe=async(e,t)=>{const n=await g({host:"localhost",port:Je(e)}),{Page:a}=n;await a.enable();const{data:r}=await a.captureScreenshot({format:"png",captureBeyondViewport:!0}),i=Buffer.from(r,"base64");s(t,i),qe.debug(`Screenshot saved as: ${t}`),await n.close()},Je=e=>{const t=e?.capabilities?.["goog:chromeOptions"]?.debuggerAddress;return t&&t.split(":")?.[1]||9229};class Ke extends Ce{constructor(e){super(e)}async getElement(e){return await this.$driver.$(e)}async getElements(e){return await this.$driver.$$(e)}async getText(e){return await $(e).getText()}async getTitle(e){return await $(e).getAttribute("title")}async getPlaceholderText(e){return await $(e).getAttribute("placeholder")}async getAltText(e){return await $(e).getAttribute("alt")}async getTagName(e){return await $(e).getTagName()}async getValue(e){return await $(e).getValue()}async getElementID(e){return await $(e).getAttribute("id")}async getElementDimension(e){if(!e)return null;try{const t=await e.getLocation(),n=await e.getSize();return t&&n?[t.x,t.y,n.height,n.width]:null}catch(e){return console.error("Error getting element dimensions:",e),null}}async isElementDisplayed(e){return await $(e).isDisplayed()}async isElementPresent(e){return await $(e).isExisting()}async getElementStrings(e){const t=[];return[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"titleTxt",value:await this.getTitle(e)},{type:"placeTxt",value:await this.getPlaceholderText(e)},{type:"valueTxt",value:await this.getValue(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)})),t}async getElementString(e){let t=null;const n=[this.getText,this.getAltText,this.getTitle,this.getPlaceholderText,this.getValue];for(const a of n)if(t=await a(e),null!==t)return t;return t}async captureScreenshot(e){await et(this.$driver,e)}async getElementsCount(e){return e.length}async switchToIFrame(e,t){await this.$driver.switchFrame(t)}async switchToParentFrame(){await this.$driver.switchToParentFrame()}pause(e){this.$driver.pause(1e3*e)}async onScreenStringHighlight(e,t){await this.$driver.execute(((e,t)=>{const n=window.getComputedStyle(e).border;e.style.border=`3px solid ${t}`,setTimeout((()=>{e.style.border=n}),40)}),e,t)}async checkIfIframeIsPresent(){return new Promise((e=>{this.$driver.$("//iframe").isExisting().then((()=>{e(!0)}))}))}async checkIfIframeBodyIsEmpty(){return""===(await this.$driver.executeScript("return document.body.innerHTML;",[])).trim()}}const et=async(t,n)=>{const a=tt(t),r=await g({host:"localhost",port:a}),{Page:i}=r;await i.enable();const{data:s}=await i.captureScreenshot({format:"png",captureBeyondViewport:!0});e.writeFileSync(n,Buffer.from(s,"base64")),await r.close()},tt=e=>{const t=e.capabilities?.["goog:chromeOptions"]?.debuggerAddress;return t&&t.split(":")[1]||9229},nt=({subject:e,selector:t,options:n={},driver:a})=>{if(!Cypress)return void a.printOnConsole("Cypress object is inaccessible");const r={name:"xpath",message:t};if(Cypress.dom.isElement(e)&&e.length>1)throw new Error("xpath() can only be called on a single element. Your subject contained "+e.length+" elements.");const i=()=>{let n,a=[],i=cy.state("withinSubject");n=Cypress.dom.isElement(e)?e[0]:Cypress.dom.isDocument(e)?e:i?i[0]:cy.state("window").document;let s=(n.ownerDocument||n).evaluate(t,n);if(s.resultType===XPathResult.NUMBER_TYPE){const e=(e=>e.numberValue)(s);return r.consoleProps=()=>({XPath:t,type:"number",result:e}),e}if((e=>e.resultType===XPathResult.STRING_TYPE)(s)){const e=(e=>e.stringValue)(s);return r.consoleProps=()=>({XPath:t,type:"string",result:e}),e}if((e=>e.resultType===XPathResult.BOOLEAN_TYPE)(s)){const e=(e=>e.booleanValue)(s);return r.consoleProps=()=>({XPath:t,type:"boolean",result:e}),e}try{let e=s.iterateNext();for(;e;)a.push(e),e=s.iterateNext();return r.consoleProps=()=>({XPath:t,result:1===a.length?a[0]:a}),a}catch(e){return console.error("Document tree modified during iteration",e),null}},s=()=>Cypress.Promise.try(i).then((e=>{var a;return a=e,Cypress._.isNumber(a)||Cypress._.isString(a)||Cypress._.isBoolean(a)||((e=Cypress.$(e)).selector=t),cy.verifyUpcomingAssertions(e,n,{onRetry:s})}));return s().then((e=>(!1!==n.log&&Cypress.log(r),e))).catch((e=>console.log(e)))};class at extends Ce{constructor(e){super(e)}async getElement(e){return"string"!=typeof e?this.$driver.wrap(e):e.startsWith("//")?await this.xpath(e).first():await this.$driver.get(e).first()}async getElements(e){return"string"!=typeof e?this.$driver.wrap(e):e.startsWith("//")?await this.xpath(e):await this.$driver.get(e)}async getText(e){return"string"==typeof e?await this.$driver.get(e).invoke("text"):await this.$driver.wrap(e).invoke("text")}async getTitle(e){return"string"==typeof e?await this.$driver.get(e).title():this.$driver.wrap().title()}async getPlaceholderText(e){return"string"==typeof e?this.$driver.get(e).filter("input").invoke("attr","placeholder").then((e=>e)):this.$driver.$$(e).attr("placeholder")}async getAltText(e){return this.$driver.get(e).invoke("attr","alt")}async getWidth(e){return"string"==typeof e?this.$driver.get(e).invoke("width"):this.$driver.$$(e).width()}async getHeight(e){return"string"==typeof e?this.$driver.get(e).invoke("height"):this.$driver.$$(e).height()}async getTagName(e){return"string"==typeof e?this.$driver.get(e).invoke("prop","tagName").then((e=>e.toLowerCase())):this.$driver.$$(e).tagName}async getValue(e){return"string"==typeof e?this.$driver.get(e).invoke("val"):this.$driver.wrap(e).invoke("val")}async getLocation(e){if("string"==typeof e)return this.$driver.get(e).invoke("offset").then((e=>({x:e.left,y:e.top})));{const t=this.$driver.$$(e).offset();return{x:t.left,y:t.top}}}async isElementDisplayed(e){return"string"==typeof e?this.$driver.get(e).then((e=>e.is(":visible"))):this.$driver.$$(e).is(":visible")}async captureScreenshot(e){this.$driver.screenshot(e)}async getElementsCount(e){return new Promise((t=>"string"==typeof e?this.$driver.get(e).its("length").then((e=>t(e))):this.$driver.wrap(e,{timeout:8e3}).its("length").then((e=>t(e)))))}async getElementStrings(e){const t=[];try{[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"placetxt",value:await this.getPlaceholderText(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)}))}catch(e){qe.error("Error getting text of element:",e)}return t}printOnConsole(e){this.$driver.log(e)}xpath(e,t,n={}){return nt({subject:e,selector:t,options:n,driver:this.$driver})}}class rt extends Ce{constructor(e){super(e)}async getElement(e){return await this.driver.findElement(e)}async getElements(e){return await this.driver.findElements(e)}async getText(e){return await e.getText()}async getTitle(){return await this.driver.getTitle()}async getAttribute(e,t){return await e.getAttribute(t)}async getTagName(e){return await e.getTagName()}async getElementDimension(e){const t=await e.getRect();if(![null,"",void 0].includes(t))return[t.x,t.y,t.height,t.width]}async isElementDisplayed(e){return await e.isDisplayed()}async getElementStrings(e){const t=[];return[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAttribute(e,"alt")},{type:"titleTxt",value:await this.getAttribute(e,"title")},{type:"placeTxt",value:await this.getAttribute(e,"placeholder")},{type:"valueTxt",value:await this.getAttribute(e,"value")}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)})),t}async getElementsCount(e){return e.length}}const it=(e,t=", ")=>e.join(t),st=(e=!0)=>e?"(?:\\s|\\t|(?: ))":/(?:\s|\t|(?: ))/g;class ot{#se={};#U;constructor(e){this.#U=e,this.#oe(),this.buildConfig(),this.#le()}#oe(){this.addToConfig("supportedLocales",Ct.getOptions()?.supportedLocales,[]),this.addToConfig("defaultLocale",Ct.getDefaultLocale()),this.addToConfig("defaultLanguage",Ct.getDefaultLocale()?.split(/[-_ ]/g)[0]),this.addToConfig("language",Ct.getLocale()?.split(/[-_ ]/g)[0]),this.addToConfig("locale",Ct.getLocale()),this.addToConfig("qaLocale",Ct.getQALocale())}buildConfig(){throw new Error(`{{${this.#U}}} buildConfig should be implemented by child class`)}#le(){h.load(f.entireSupplemental()),h.load(f.entireMainFor(this.#ce(this.#se.defaultLocale),this.#ce(this.#se.locale)))}#ce(e){if(this.isQaLocale())return"en";e=e.replace(/[-_ ]/g,"-");const t=f.availableLocales;let n;if(t.find((t=>t===e)))n=e;else{const a=e.split("-")[0];n=t.find((e=>e===a))?a:e}return n}validate(e){throw new Error(`{{${this.#U}}} validate should be implemented by child class`)}isInternationalized(e){throw new Error(`{{${this.#U}}} isInternationalized should be implemented by child class`)}setConfig(e){this.#se=e}addToConfig(e,t,n){this.#se[e]=t??n}getConfig(){return this.#se}isSupportedLocale(){return this.#se.supportedLocales.includes(this.currentLocale())}isQaLocale(){return this.currentLocale()===this.#se?.qaLocale}isDefaultLocale(){return this.currentLocale()===this.defaultLocale()}defaultLocale(){return this.#se?.defaultLocale}defaultLanguage(){return this.#se?.defaultLanguage}currentLocale(){return this.#se?.locale}currentLanguage(){return this.#se.language}getGlobalize(e){return h(this.#ce(e))}}class lt extends ot{constructor(){super("currency")}validate(e){return this.#de(e)||this.isInternationalized(e)}isInternationalized(e){return this.#ue(this.currentLocale(),e)}buildConfig(){}#ue(e,t){const n=this.#pe(e),a=this.#me(e,n);if(!this.#ge(n,a,t))return!1;const r=this.#he(n,a,t);return!isNaN(this.getGlobalize(e).numberParser()(r))}#ge(e,t,n){return new RegExp(`^(?:-)?(?:${e}|\\${t})(?:${st()})?(?:-)?[0-9,. ]+$`,"gi").test(n)||new RegExp(`^(?:[- ]?)[0-9,. ]+(?:${st()})?(?:${e}|\\${t})$`,"gi").test(n)}#he(e,t,n){return n.replace(new RegExp(`^(?:-)(?:${st()})?`,"gi"),"").replace(new RegExp(`^(?:${st()})?(?:${e}|\\${t})`,"gi"),"").replace(new RegExp(`^(?:-)(?:${st()})?`,"gi"),"").replace(new RegExp(`(?:${st()})?(?:${e}|\\${t})$`,"gi"),"").trim()}#fe(e){return(this.getGlobalize(e).cldr.supplemental(["likelySubtags",e])||e).split(/[-_ ]/).pop()}#pe(e){const t=this.#fe(e),n=this.getGlobalize(e).cldr.supplemental("currencyData").region[t];if(n){const e=n.find((e=>{const t=Object.values(e)[0];return!t._tender&&(!t._to||new Date(t._to)>=new Date)}));return e?Object.keys(e)[0]:"USD"}return"USD"}#me(e,t){const n=this.getGlobalize(e).cldr.main(`numbers/currencies/${t}`);return n?.symbol||"$"}#de(e){return this.#ue(this.defaultLocale(),e)}}const ct=(e,t)=>{t=String(t);const n=(e=>{let[t="en",n="us"]=e.split(/[-_ ]/g);switch(n=String(n).toLowerCase(),t){case"en":return"gb"===n?v.GB:v;case"de":return I;case"es":return x;case"fr":return D;case"pt":return S;case"ja":return E;case"zh":return["hans","cn","sg","hans-cn"].includes(n)?L.hans:["hant","tw","hk","mo","hant-tw"].includes(n)?L.hant:L;case"ru":return b;case"nl":return y;case"uk":return w;default:return v}})(e).parse(String(t).trim());return n.forEach((e=>{t=t.replace(e.text,"")})),0===t.replace(/[^\p{L}\p{N}]+/gu,"").trim().length},dt=e=>{try{return e=e.replace("_","-"),Intl.DateTimeFormat.supportedLocalesOf(e)[0]}catch(t){return Logger.warn(`${e} is unsupported by Intl`),!1}},ut=(e,t,n)=>{if(!1!==(e=dt(e))){if(C.fromFormat(n,t,{locale:dt(e)}).isValid)return!0}return!1};class pt extends ot{constructor(){super("date")}validate(e){return this.#ve(e)||this.isInternationalized(e)}isInternationalized(e){return this.#we(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#we(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Le(e,t)}#ve(e){return this.#we(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({date:a})(t))return!0}return!1}#be(e,t){const n=[C.DATE_SHORT,C.DATE_MED,C.DATE_FULL,C.DATE_HUGE,C.DATE_MED_WITH_WEEKDAY,"D","DD","DDD","DDDD"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Le(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class mt extends ot{constructor(){super("dateTime")}validate(e){return this.#Ee(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Se(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#Se(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Le(e,t)}#Ee(e){return this.#Se(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({datetime:a})(t))return!0}return!1}#be(e,t){const n=[C.DATETIME_SHORT,C.DATETIME_SHORT_WITH_SECONDS,C.DATETIME_MED,C.DATETIME_MED_WITH_SECONDS,C.DATETIME_MED_WITH_WEEKDAY,C.DATETIME_FULL,C.DATETIME_FULL_WITH_SECONDS,C.DATETIME_HUGE,C.DATETIME_HUGE_WITH_SECONDS,"f","F","ff","FF"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Le(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class gt extends ot{constructor(){super("number")}validate(e){return this.#De(e)||this.isInternationalized(e)}isInternationalized(e){return this.#xe(this.currentLocale(),e)}buildConfig(){}#xe(e,t){const n=this.getGlobalize(e).numberParser();return!isNaN(n(t))}#De(e){return this.#xe(this.defaultLocale(),e)}}class ht extends ot{constructor(){super("percentage")}validate(e){return this.#Ie(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Ce(this.currentLocale(),e)}buildConfig(){}#Ce(e,t){if(!this.#Te(e,t))return!1;const n=this.#ze(t);return!isNaN(this.getGlobalize(e).numberParser()(n))}#ze(e=""){return e.replace(new RegExp(`(^(?:${st()})*|(?:${st()})*$)`,"g"),"").replace(/^-/,"").replace(/٪|%/g,"").trim()}#Te(e,t){const n="[0-9,. ]+";let a;return a=["fa-IR","fa-ir"].includes(e)||e.startsWith("ar")?new RegExp(`(?:-)?(?:${st()})?(?:٪|%)(?:${st()})?${n}`,"g"):new RegExp(`(?:-)?(?:${st()})?${n}(?:${st()})?(?:٪|%)`,"g"),a.test(t)}#Ie(e){return this.#Ce(this.defaultLocale(),e)}}class ft extends ot{constructor(){super("string")}buildConfig(){const e=Ct.getOptions();this.addToConfig("mockChars",e?.qaMock),this.addToConfig("resource",Ct.getResourceBundle(),{}),this.addToConfig("garbledChars",Ct.getGarbledChars()?.filter((e=>String(e).trim())).filter(Boolean),[])}isInternationalized(e){const t=e.trim().replace(/\s*:\s*$/,""),n=this.getConfig()?.resource?.[this.defaultLocale()];return n?.includes(e)||n?.includes(t)}validate(e){return"string"==typeof e}isDntText(e){return this.getConfig()?.resource?.dnt?.includes(e)}isAutoIgnoredText(e){return this.isDefaultLocale()||this.isUserInputText(e)||this.isEmailText(e)||this.isUrlText(e)}isIgnoredText(e){return this.getConfig()?.resource?.ignore?.includes(e)}isUserInputText(e){return e.startsWith("zqa_")}isEmailText(e){return pe.EMAIL.test(e)}isUrlText(e){return pe.URL.test(e)}isFullQaLanguageText(e){const t=this.getConfig();return(e.slice(0,2)===t?.mockChars?.prefix||e.slice(0,2)===t?.mockChars?.prefix.toUpperCase())&&e.slice(-2)===t?.mockChars.suffix}isPartialQaLanguageText(e){const t=this.getConfig();return e.slice(0,2)===t?.mockChars.prefix||e.slice(0,2)===t?.mockChars.prefix.toUpperCase()||e.slice(-2)===t?.mockChars.suffix}hasEncodingIssue(e){return this.getConfig()?.garbledChars?.length>0&&this.getConfig().garbledChars.some((t=>e.includes(t)))}isPartiallyLocalizedText(e){return this.#Ne(e)===this.currentLanguage()}#Ne(e){const t=Object.keys(T(e)?.scores||{}).filter(Boolean)||[],n=this.currentLanguage();return t.length?t.includes(n)?n:t[0]:this.defaultLanguage()}isLocalized(e){const t=e.trim().replace(/\s*:\s*$/,""),n=this.getConfig()?.resource?.[this.currentLocale()];return n?.includes(e)||n?.includes(t)}}class vt extends ot{constructor(){super("time")}validate(e){return this.#Me(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Pe(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#Pe(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Le(e,t)}#Me(e){return this.#Pe(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({time:a})(t))return!0}return!1}#be(e,t){const n=[C.TIME_SIMPLE,C.TIME_WITH_SECONDS,C.TIME_WITH_SHORT_OFFSET,C.TIME_WITH_LONG_OFFSET,C.TIME_24_SIMPLE,C.TIME_24_WITH_LONG_OFFSET,C.TIME_24_WITH_SECONDS,C.TIME_24_WITH_SHORT_OFFSET,"t","T","tt","TT"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Le(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class wt{static#Ae={};static#Oe;static#$e;static#Re;static#ke;static#X;static#_e;static#Be;static load(){wt.#Oe=new ft,wt.#$e=new gt,wt.#Re=new pt,wt.#ke=new vt,wt.#X=new mt,wt.#_e=new lt,wt.#Be=new ht}static getIssueType(e){const t=Ct.getLocale();if(t){let n=ie;return wt.#Ae[t]||(wt.#Ae[t]={}),wt.#Ae[t][e]?wt.#Ae[t][e]:(this.#Oe.isAutoIgnoredText(e)?n=se:this.#Oe.isDntText(e)?n=ae:this.#Oe.isIgnoredText(e)?n=re:this.#Oe.hasEncodingIssue(e)?n=ne:this.#_e.validate(e)?n=this.#_e.isInternationalized(e)?U:q:this.#Be.validate(e)?n=this.#Be.isInternationalized(e)?V:Y:this.#$e.validate(e)?n=this.#$e.isInternationalized(e)?F:Z:this.#Re.validate(e)?n=this.#Re.isInternationalized(e)?W:X:this.#ke.validate(e)?n=this.#ke.isInternationalized(e)?G:J:this.#X.validate(e)?n=this.#X.isInternationalized(e)?H:K:this.#Oe.validate(e)?this.#Oe.isQaLocale()?n=this.#Oe.isFullQaLanguageText(e)?B:this.#Oe.isPartialQaLanguageText(e)?j:Q:!this.#Oe.isDefaultLocale()&&this.#Oe.isSupportedLocale()&&(n=this.#Oe.isLocalized(e)?B:this.#Oe.isPartiallyLocalizedText(e)?j:this.#Oe.isInternationalized(e)?ie:Q):this.#Oe.isDefaultLocale()&&(n=se),wt.#Ae[t][e]=n,n)}return ie}}class yt{static load(){const e={};if(Ct.getOptions()?.l10nResourcePath){const t=Oe(process.cwd(),Ct.getOptions()?.l10nResourcePath);if(ze(t))yt.#je(t,e);else{if(!Te(t))return void console.error(`${t} invalid resource path`);for(const n of Ae(t)){const a=Oe(t,n);yt.#je(a,e)}}Ct.setResourceBundle(e)}}static#je(e,t){const n=Ct.getLocale(),a=Ct.getDefaultLocale(),r=Pe(e),i=(e=>ze(e)?p(e):"")(e);if(me.includes(i)){if(!r)return void console.error(`${e} is empty or invalid`);if(e.toLowerCase().includes("dnt"))t.dnt=r.split(/\r?\n/);else if(e.toLowerCase().includes("ignore"))t.ignore=r.split(/\r?\n/);else if(e.includes(n)){const e=r.split(/\r?\n/);t[n]=e.map(bt)}else e.includes(a)&&(t[a]=r.split(/\r?\n/))}}}function bt(e){return e.replace(/<\/?[^>]+>/gi,"")}const Lt=()=>{Ct.getDriver()&&!Ct.isLoaded()&&Ct.getLocale()&&(Ct.build(),yt.load(),wt.load(),Ze.setup(),Ue.init(),Ct.setIsLoaded(!0))},Et=()=>{let e=process.cwd(),t=0;for(;t<10;){const n=Oe(e,"package.json");if(ze(n)){const e=Pe(n),t=JSON.parse(e),a={...t?.dependencies||{},...t?.devDependencies||{},...t?.peerDependencies||{}};if("@zeesoft/i18n-verify"!==t.name&&JSON.stringify(a).includes('"@zeesoft/i18n-verify"'))break}e=u(e),t++}return e},St=(e=Et())=>Pe(Oe(e,"package.json")),Dt=(e="")=>`{\n "appName": "${St()?.match(/"name": "([^"]+)",/)?.[1]||""}",\n "type": "${e}", // set automation framework type ${it(Object.values(N))}\n "qaLocale": "", // set the qa locale used in your project\n "l10nResourcePath": "./l10nResources", // set the dir path of l10n resources\n "l10nReportsPath": "./l10nReports/", // set the dir path of l10n reports\n "supportedLocales": ["en_US", "fr_FR", "ja_JP"], // set the list of supported locales\n "defaultLocale": "en_US", // set default locale of the application\n "qaMock": { // set the prefix and suffix characters of qa locale\n "prefix": "à_",\n "suffix": "_海"\n },\n "mode": "${Le.I18N}", // should be one of the following: ${Object.values(Le).join(", ")}\n "garbledChars": [""]\n}`,xt=()=>{const e=Oe(process.cwd(),ve);if(!ze(e))throw new Error(`${ve} file is missing. Please add the file in your project root dir`);let t=Pe(e);if(!t)throw new Error(`invalid ${ve} file. Please add valid file in your project root dir`);t=t.replace(/\/\/.+$/gm,"");try{t=JSON.parse(t)}catch(e){throw new Error(`invalid ${ve} file. Please add valid file in your project root dir`)}return t},It=(e,t="")=>{e&&!ze(e)&&$e(e,Dt(t))};class Ct{static#Fe;static#Ue;static#U;static#Ve;static#We;static#Ge;static#He;static#Qe;static#Ze;static#e;static#qe;static init(){try{const e=xt();(e=>{if("object"!=typeof e||Object.keys(e).length<1)throw new Error(`empty ${ve} file`);const t=[];if(!((e,t,n=[])=>["l10nResourcePath","l10nReportsPath","supportedLocales","defaultLocale","type","mode"].every((e=>(((e,t)=>e in t)(e,t)||n.push(e),!0))))(0,e,t))throw new Error(`following properties are missing in ${ve} file: ${it(t)}`);if(!Object.values(N).includes(e?.type||""))throw new Error(`${ve} has invalid system type`);if(!Object.values(Le).includes(e?.mode||""))throw new Error(`${ve} has invalid mode option`)})(e),this.#U=e?.type,this.#Ve=e?.mode,this.#Ge=e?.qaLocale,this.#e=e?.appName,this.#He=e,this.#Ze=0,this.getGarbledChars()}catch(e){throw qe.error(e.message),e}}static build(){this.#Fe=((e,t)=>{switch(e){case N.NIGHT_WATCH:return new Ye(t);case N.WEB_DRIVER_IO:return new Ke(t);case N.CYPRESS:return new at(t);case N.SELENIUMJS:return new rt(t);default:throw new Error("Unsupported framework")}})(this.#U,this.#Fe),this.resetCounter()}static getGarbledChars(){return[...this.#He.garbledChars||[],...fe]}static getL10nReportsPath(){return this.#He.l10nReportsPath}static setDriver(e){this.#Fe=e}static setType(e){this.#U=e}static setLocale(e){this.#We=e}static setOptions(e){this.#He=e}static getExecutor(){if(Ct.#Ue)return Ct.#Ue;if(!Ct.#Fe)throw new Error("Unable to initialize process executor, driver is missing");return this.#Ue=new At(this.#Fe),this.#Ue}static getDriver(){return this.#Fe}static getLocale(){return this.#We}static getDefaultLocale(){return this.#He.defaultLocale}static getType(){return this.#U}static getOptions(){return this.#He}static setQALocale(e){this.#Ge=e}static getQALocale(){return this.#Ge}static setResourceBundle(e){this.#Qe=e}static getResourceBundle(e){return this.#Qe?.[e]||this.#Qe||[]}static incrementCounter(){this.#Ze++}static resetCounter(){this.#Ze=0}static getSSCount(e=!1){return e?++this.#Ze:this.#Ze}static getMode(){return this.#Ve}static isSanityRun(){return this.#Ve===Le.SANITY}static isI18nVerificationRun(){return this.#Ve===Le.I18N}static isContextRun(){return this.#Ve===Le.CONTEXT}static getAppName(){return this.#e}static isLoaded(){return this.#qe}static setIsLoaded(e){this.#qe=e}}class Tt{#Fe;#Ye;#Xe;#W;#Je=[];#Ke;#et;#tt;#nt;#at;constructor(e,t,n,a,r){this.#Fe=e,this.#Ye=t,this.#Xe=n,this.#at=a,this.#W=r}async run(){try{await this.#rt(),this.shouldCollectStrings()?await this.#it():await this.#st()}catch(e){qe.error("unable to run the process",e)}finally{await this.#ot()}}async process(e,t,n,a){throw new Error("should be implemeneted by the child class")}shouldCollectStrings(){return!0}async preProcess(){}async postProcess(){}async captureScreen(e){try{return await this.#Fe.captureScreenshot(e),qe.debug("reviewed image has been saved"),!0}catch(e){return qe.error("unable to capture screenshot"),!1}}getBaseImageName(){return this.#et}getDescription(){return this.#W}getVerificationArea(){return this.#Ye}getVerificationAreaEl(){return this.#tt}getVerificationAreaOffsets(){return this.#nt}getDriver(){return this.#Fe}async getElementOffsets(e){const t=await this.#Fe.getElementDimension(e);return{x:t[0],y:t[1],height:t[2],width:t[3]}}async#it(){this.#Je.length?(await this.#lt(),await this.#ct()):qe.debug("No locators provided")}async#st(){await this.process(this.#W)}async#rt(){this.#dt(),this.#ut(),await this.#pt(),await this.#mt(),await this.preProcess()}async#ot(){await this.postProcess()}#dt(){const e=this.#Ye||"";if(this.#Xe?.length>0)for(const t of this.#Xe)this.#Je.push(e+t);this.#at&&(this.#Ke=""!==e?e+ge:ge,this.#Je.push(this.#Ke.toString()))}#ut(){this.#et=`${Ct.getSSCount(!0).toString().padStart(3,"0")}_${this.#W.replace(/[^\w\s]/gi,"").replace(/\s+/g,"")}.png`}async#mt(){const e=Oe(Ze.reviewedReports(),this.#et);await this.captureScreen(e)}async#pt(){if(this.#tt=await this.#Fe.getElement(this.#Ye),!this.#tt)throw new Error("invalid verification area selector is supplied");this.#nt=await this.getElementOffsets(this.#tt)}async#lt(){let e=!1;for(const t of this.#Je){const n=await this.#Fe.getElements(t,!0);if(n?.length){!e&&(e=!0);for(const e of n)await this.#gt(e,!0)}}e||qe.debug("No UI elements found from the provided locators")}async#ct(){if(await this.#Fe.checkIfIframeIsPresent()){const e=await this.#Fe.getElements("//iframe");qe.debug(`Total ${e.length} iframes are located on the ${this.#W}`);for(let t=0;t<e.length;t++){const n=e[t],a=await this.getElementOffsets(n);qe.debug(`Switching to iFrame: ${t}`),await this.#Fe.switchToIFrame(t,n),this.#Fe.pause(1);const r=await this.#Fe.getElement("//body");await this.#Fe.isElementPresent(r);let i=!1;if(!await this.#Fe.checkIfIframeBodyIsEmpty()){const e=await this.#Fe.getElements(ge,!0);if(e?.length)for(const n of e){const e=await this.#gt(n,!0,a,t);!i&&e&&(i=!0)}}!i&&qe.debug("No valid UI elements found in the iFrame"),await this.#Fe.switchToParentFrame()}}}async#gt(e,t=!1,n,a){if(await this.#ht(e)){const r=t?await this.#Fe.getElementStrings(e):[await this.#Fe.getElementString(e)];if(r?.length){for(const t of r)try{await this.process(t,e,n,a)}catch(e){qe.error(`unable to process the ${a?`iframe-[${a}] `:""}string "${t}"`,e)}return!0}return!1}return!1}async#ht(e){return e&&await this.#Fe.isElementDisplayed(e)}}class zt extends Tt{#G={screenshot:"",category:"",fileName:"",bookmark:!0,pageTitle:"",strings:{}};#ft=0;#vt="";constructor(...e){super(...e)}async preProcess(){this.#wt(),this.#yt(),this.#bt(),this.#Lt(),this.#Et()}async process(e,t,n){Ze.incrementCount(le,e,this.getBaseImageName(),this.getDescription()),qe.debug(`context creation for "${e}"`),await this.#St(e,t,n)}async postProcess(){this.#Dt();const e=this.#xt();$e(Oe(Ze.getContextPath(),`${this.#G.fileName}.json`),JSON.stringify(e)),Qe.incrementCountByType("context")}#wt(){this.#ft=Ae(Ze.getContextPath()).length+1}#yt(){this.#G.category=this.getDescription()}#bt(){this.#G.pageTitle=`TC-${Ct.getAppName()}-${this.getDescription().trim().slice(0,2).toUpperCase()}-${`${this.#ft}`.padStart(3,"0")}`}#Lt(){this.#G.fileName=`${Re(Ct.getAppName())}-${`${this.#ft}`.padStart(3,"0")}`}#Et(){this.#vt=`${Re(Ct.getAppName())}_${`${this.#ft}`.padStart(3,"0")}`}#Dt(){var e;this.#G.screenshot=`data:image/png;base64,${e=Oe(Ze.reviewedReports(),this.getBaseImageName()),r(e).toString("base64")}`}#xt(){return{[Ee]:this.#G.bookmark,[Se]:this.#G.category,[De]:this.#G.fileName,[xe]:this.#G.screenshot,[Ie]:JSON.stringify(this.#G.strings)}}async#St(e,t,n){const a=await this.getElementOffsets(t),r=`${this.#vt}_${`${Object.keys(this.#G.strings).length+1}`.padStart(3,"0")}`;if(n&&Object.keys(n||{}).length){const e=n,t=["x","y"];for(const n of t)n in a&&n in e&&(a[n]+=e[n])}this.#G.strings[r]={text:e,coordinates:{...a}}}}class Nt{static async drawRectangle(t,n,a){const r=t?.x-2,i=t?.y-2,s=t?.width+2,o=t?.height+2;try{const t=await z.read(a),l=z.cssColorToHex(n);this.drawRectangleOutline(t,r,i,s,o,l,2);const d=c.dirname(a),u=await t.getBufferAsync(z.MIME_PNG);e.existsSync(d)||(qe.debug("folder not found"),e.mkdirSync(d,{recursive:!0})),e.writeFileSync(a,u)}catch(e){qe.error(`Error: ${e}`)}}static async drawRectangleOutline(e,t,n,a,r,i,s){for(let o=t;o<t+a;o++)for(let t=0;t<s;t++)e.setPixelColor(i,o,n+t),e.setPixelColor(i,o,n+r-1-t);for(let o=n;o<n+r;o++)for(let n=0;n<s;n++)e.setPixelColor(i,t+n,o),e.setPixelColor(i,t+a-1-n,o)}}class Mt extends Tt{constructor(...e){super(...e)}async process(e,t,a){if("string"!=typeof e||!e||0===Number(e))return;const r=Oe(Ze.reviewedReports(e,this.getBaseImageName()),this.getBaseImageName()),i=wt.getIssueType(e),s=Oe(Ze[i](e,this.getBaseImageName()),this.getBaseImageName());Ze.incrementCount(le,e,this.getBaseImageName(),this.getDescription()),n(r)||l(Oe(Ze.reviewedReports(),this.getBaseImageName()),r),n(r)&&!n(s)&&l(r,s);const o=this.#It(i,e);Ze.incrementCount(i,e,s,this.getDescription()),await this.#Ct(t,o,s,a)}async#Ct(e,t,n,a){await this.#Tt(e,t),await this.#zt(e,t,n,a)}async#Tt(e,t){await this.getDriver().onScreenStringHighlight(e,t)}async#zt(e,t,n,a){let r;const i=await this.getElementOffsets(e),s=O;if(a){r=a;const e=["x","y"];for(const t of e)t in i&&t in r&&(i[t]=r[t]+i[t])}else r=this.getVerificationAreaOffsets();await Nt.drawRectangle(r,s,n),await Nt.drawRectangle(i,t,n)}#It(e,t){return qe.debug(`"${t}" - ${de[e][1]}${ue[e]}[0m`),de[e][0]}}class Pt extends Tt{constructor(...e){super(...e)}shouldCollectStrings(){return!1}async process(e){qe.info(`${e} loaded`);const t=Oe(Ze.reviewedReports(e,this.getBaseImageName()),this.getBaseImageName());Ze.incrementCount(le,e,t,this.getDescription()),n(t)||l(Oe(Ze.reviewedReports(),this.getBaseImageName()),t)}}class At{#Fe;constructor(e){this.#Fe=e}async start({verificationArea:e,staticLocators:t,useSmartSelector:n,description:a}){const r=((e,t,n,a,r)=>{switch(Ct.getMode()){case Le.I18N:return new Mt(e,t,n,a,r);case Le.CONTEXT:return new zt(e,t,n,a,r);case Le.SANITY:return new Pt(e,t,n,a,r);default:throw new Error("unsupported runner type")}})(this.#Fe,e,t,n,a);await r.run()}}export{Ct as C,At as E,qe as L,Ze as R,N as S,St as a,ve as b,It as c,Et as g,Oe as j,Lt as l};
|
|
1
|
+
import e,{statSync as t,existsSync as n,mkdirSync as a,readFileSync as r,readdirSync as i,writeFileSync as s,appendFileSync as o,copyFileSync as l}from"fs";import c,{join as d,dirname as u,extname as p}from"path";import{Table as m}from"console-table-printer";import g from"chrome-remote-interface";import h from"globalize";import f from"cldr-data";import{en as v,uk as w,nl as y,ru as b,zh as E,ja as L,pt as S,fr as x,es as D,de as I}from"chrono-node";import{DateTime as C}from"luxon";import{detectTextLangEx as T}from"@isdk/detect-text-language";import z from"jimp";const N={NIGHT_WATCH:"nightwatch",WEB_DRIVER_IO:"webio",CYPRESS:"cypress",SELENIUMJS:"seleniumJS"},M="rgb(0, 154, 61)",P="rgb(255, 0, 0)",A="rgb(255, 255, 0)",O="rgb(0, 0, 255)",R="[32m",k="[31m",_="[33m",B="STRING_LOCALIZED",j="STRING_PARTIALLY_LOCALIZED",F="NUMBER_LOCALIZED",U="CURRENCY_LOCALIZED",V="PERCENTAGE_LOCALIZED",W="DATE_LOCALIZED",G="TIME_LOCALIZED",q="DATE_TIME_LOCALIZED",H="STRING_NOT_LOCALIZED",Q="NUMBER_NOT_LOCALIZED",Z="CURRENCY_NOT_LOCALIZED",Y="PERCENTAGE_NOT_LOCALIZED",X="DATE_NOT_LOCALIZED",J="TIME_NOT_LOCALIZED",K="DATE_TIME_NOT_LOCALIZED",ee="TRUNCATION_ISSUE",te="OVERLAPPING_ISSUE",ne="ENCODING_ISSUE",ae="DNT",re="IGNORE",ie="UNTRANSLATED",se="AUTO_IGNORE",oe="USER_INPUT",le="REVIEWED",ce={[B]:0,[H]:0,[j]:0,[F]:0,[Q]:0,[W]:0,[X]:0,[q]:0,[K]:0,[G]:0,[J]:0,[U]:0,[Z]:0,[V]:0,[Y]:0,[ee]:0,[te]:0,[ne]:0,[ae]:0,[re]:0,[ie]:0,[se]:0,[oe]:0,[le]:0},de={[B]:[M,R],[j]:[A,_],[F]:[M,R],[W]:[M,R],[G]:[M,R],[q]:[M,R],[U]:[M,R],[V]:[M,R],[H]:[P,k],[X]:[P,k],[K]:[P,k],[J]:[P,k],[Q]:[P,k],[Z]:[P,k],[Y]:[P,k],[ee]:[P,k],[te]:[P,k],[ne]:[P,k],[ae]:[M,R],[re]:[M,R],[ie]:[M,R],[se]:[M,R],[oe]:[M,R],[le]:[M,R]},ue={[B]:"string is localized",[j]:"string is partially localized",[F]:"number is localized",[W]:"date is localized",[q]:"dateTime is localized",[G]:"time is localized",[U]:"currency is localized",[V]:"percentage is localized",[H]:"string is not localized",[X]:"date is not localized",[K]:"dateTime is not localized",[J]:"time is not localized",[Q]:"number is not localized",[Z]:"currency is not localized",[Y]:"percentage is not localized",[ee]:"truncation issue detected",[te]:"overlapping issue detected",[ne]:"encoding issue detected",[ae]:"do not translate",[re]:"ignored this issue",[ie]:"string is untranslated",[se]:"automatically ignored issue",[oe]:"user input field",[le]:"string has been reviewed"},pe={EMAIL:/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}\b/gi,URL:/^(https?:\/\/)?(www\.)?[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?$/},me=[".txt",".json"],ge="//*[not(self::script or self::style or self::noscript)]\n [string-length(normalize-space(.)) > 0]\n [not(*)]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//*[@placeholder or @title or @alt]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//input[@type='checkbox' or @type='radio']\n /following-sibling::*[string-length(normalize-space(.)) > 0]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n|\n//input[@type='text'][@value][string-length(normalize-space(@value)) > 0]\n [not(ancestor::*[\n translate(@style, ' ', '') = 'display:none;' \n or contains(@style, 'display:none') \n or contains(@style, 'visibility:hidden') \n or contains(@aria-hidden, 'true') \n or @hidden\n ])]\n",he=!0,fe=["☐","�","","â€","î","ÃÂ","ÃÂ","ü","é","é","æ•°","æ®","ラã"],ve=".i18n-verify.rc",we="html",ye="ascii_table",be="metrics.cache",Ee={SANITY:"sanity",I18N:"i18n",CONTEXT:"context"},Le="bookmark",Se="category",xe="fileName",De="screenshot",Ie="strings";class Ce{constructor(e){this.$driver=e}checkIfNullOrEmpty(e){return![null,"",void 0].includes(e)}getElement(){throw new Error("implementation is missing")}getElements(){throw new Error("implementation is missing")}getText(){throw new Error("implementation is missing")}getTitle(){throw new Error("implementation is missing")}getPlaceholderText(){throw new Error("implementation is missing")}getAltText(){throw new Error("implementation is missing")}getWidth(){throw new Error("implementation is missing")}getHeight(){throw new Error("implementation is missing")}getTagName(){throw new Error("implementation is missing")}getValue(){throw new Error("implementation is missing")}getLocation(){throw new Error("implementation is missing")}getElementDimension(){throw new Error("implementation is missing")}getElementID(){throw new Error("implementation is missing")}isElementDisplayed(){throw new Error("implementation is missing")}isElementPresent(){throw new Error("implementation is missing")}getElementString(){throw new Error("implementation is missing")}getElementStrings(){throw new Error("implementation is missing")}captureScreenshot(){throw new Error("implementation is missing")}getElementsCount(){throw new Error("implementation is missing")}switchToIFrame(){throw new Error("implementation is missing")}switchToParentFrame(){throw new Error("implementation is missing")}pause(){throw new Error("implementation is missing")}onScreenStringHighlight(){throw new Error("implementation is missing")}checkIfIframeIsPresent(){throw new Error("implementation is missing")}checkIfIframeBodyIsEmpty(){throw new Error("implementation is missing")}printOnConsole(e){console.log(e)}}const Te=e=>Ne(e)&&t(e).isDirectory(),ze=e=>Ne(e)&&t(e).isFile(),Ne=e=>n(e),Me=(e,t=!0)=>{Ne(e)&&Te(e)||(t&&Ze.debug(`Folder doesn't exists, creating [34m${e}[0m`),(e=>{!Ne(e)&&a(e,{recursive:!0})})(e))},Pe=e=>ze(e)?r(e,{encoding:"utf-8"}):"",Ae=e=>Te(e)?i(e):[],Oe=(...e)=>e?.every((e=>"string"==typeof e))?d(...e):"",$e=(e,t,n="utf-8")=>s(e,t,{encoding:n}),Re=e=>e.replace(/[^a-zA-Z0-9]+/g,"_");class ke{#e;#t;#n;#a;#r;#i;constructor({appName:e,reportTime:t,isContext:n,isSanity:a,defaultLocale:r,reportFolderTimestamp:i}){this.#e=e,this.#n=t,this.#a=n,this.#r=a,this.#t=r,this.#i=i}getAppName(){return this.#e}getReportTime(){return this.#n}isSanityRun(){return this.#r}isContextRun(){return this.#a}getDefaultLocale(){return this.#t}getDefaultLocale(){return this.#t}getReportFolderTimestamp(){return this.#i}}class _e{#s;$generatedContent;$type;constructor(e){this.$type=e}generate(e){this.#s=e,this.$generatedContent=this.buildContent()}export(e,t="report"){this.write(e,t,this.$generatedContent),Ze.info(`Exported consolidated I18n Verification ${this.$type} report in ${e}`)}write(e,t,n){throw new Error("should be implemented by child report classes")}buildContent(){throw new Error("should be implemented by child report classes")}getMetaInfo(){return{defaultLocale:this.#s.getDefaultLocale(),appName:this.#s.getAppName(),reportTime:this.#s.getReportTime(),isSanity:this.#s.isSanityRun(),isContext:this.#s.isContextRun(),reportFolderTimestamp:this.#s.getReportFolderTimestamp()}}}class Be extends _e{constructor(){super(we)}buildContent(){const e=this.getMetaInfo(),t=`var DataStore;\n // global functions\n function openImageViewer(imageSrc) {\n var imageView = document.getElementById('imageView');\n imageView.src = \`\${window.getProductUrl()}/\${imageSrc}\`;\n document.getElementById('imageViewer').style.display = 'block';\n }\n\n function closeImageViewer() {\n delete document.getElementById('imageView').src;\n document.getElementById('imageViewer').style.display = 'none';\n }\n\n document.addEventListener('DOMContentLoaded', () => {\n document.addEventListener('keydown', (e) => {\n const viewer = document.getElementById('imageViewer');\n if (e.key === 'Escape' && viewer && viewer.style.display === 'block') {\n closeImageViewer();\n }\n });\n });\n\n //to dynamically show/hide header based upon query param: ?standalone=<true|false>\n window.getProductUrl = () => {\n if (window.productUrl) {\n return window.productUrl;\n } else {\n window.productUrl = \`\${window.location.protocol}//\${\n window.location.host\n }\${window.location.pathname.match(/^.+[^\\/report\\.html]/g)?.[0]}\`;\n return window.productUrl;\n }\n };\n const params = new URLSearchParams(location.href.match(/(\\?.*$)/g)?.[0]);\n if (params.size) {\n window.isStandaloneReport = params.get('standalone') === 'true';\n if(!window.isStandaloneReport) {\n const overview = document.getElementById('overview');\n if(overview) {\n overview.classList.add('--reduced-zoom');\n }\n }\n } else {\n window.isStandaloneReport = true;\n }\n ${(e=>`// DataStore\n (() => {\n // prepare appData and set DataStore\n var appData = {\n appName: '${e.appName}',\n defaultLocale: '${e.defaultLocale}',\n sanityRun: ${e.isSanity},\n contextRun: ${e.isContext},\n timeStamp: '${e.reportTime}',\n reportFolderTimestamp: '${e.reportFolderTimestamp}',\n pagesByLocale: {},\n metaData: {\n stats: {},\n statsByLocale: {}\n },\n stringsData: {},\n locales: [],\n hasRuntimeError: false\n };\n\n if (window.rawData) {\n for (const entry of Object.entries(window.rawData)) {\n const locale = entry[0];\n const stats = entry[1].metaData.stats;\n const stringsData = entry[1].stringsData;\n\n if (Object.keys(appData.metaData.stats).length) {\n for (const prop in stats) {\n appData.metaData.stats[prop] += stats[prop];\n }\n } else {\n appData.metaData.stats = {...stats};\n }\n\n appData.metaData.statsByLocale[locale] = stats;\n appData.stringsData[locale] = stringsData;\n\n if (Object.keys(stringsData).length) {\n Object.values(stringsData).forEach(values => {\n values.flat().forEach(value => {\n if(!appData.pagesByLocale[locale]) {\n appData.pagesByLocale[locale] = {};\n }\n if(!(value.description in appData.pagesByLocale[locale])) {\n appData.pagesByLocale[locale][value.description] = value.imagePath;\n }\n });\n });\n }\n }\n\n appData.locales = Object.keys(window.rawData);\n }\n else {\n appData.hasRuntimeError = true;\n }\n\n function SimpleDataStore() {\n this._getData = () => appData;\n\n this.ranSuccessfully = () => this.getLocales().length > 0;\n\n this.getTimestamp = () => this._getData().timeStamp;\n\n this.getAppName = () => this._getData().appName;\n\n this.isDefaultLocale = (locale) =>\n locale === this._getData().defaultLocale;\n\n this.getLocalesData = () => this._getData().stringsData || {};\n\n this.getLocales = () => this._getData().locales || [];\n\n this.getStatByLocale = (locale) =>\n this._getData().metaData.statsByLocale[locale] || {};\n\n this.getOverallStats = () => this._getData().metaData.stats || {};\n\n this.getDataByLocale = (locale) =>\n this._getData().stringsData[locale] || {};\n\n this.isSanityRun = () => this._getData().sanityRun;\n\n this.isContextRun = () => this._getData().contextRun;\n\n this.getReportFolderTimestamp = () => this._getData().reportFolderTimestamp;\n\n this.hasRuntimeError = () => this._getData().hasRuntimeError;\n\n this.getPages = (locale) => locale ? {...(this._getData().pagesByLocale[locale] || {})} : {...this._getData().pagesByLocale};\n\n this.hasDefaultLocaleData = () => Object.keys(this.getDataByLocale(this._getData().defaultLocale)).length > 0;\n\n this.getDefaultLocale = () => this._getData().defaultLocale;\n }\n\n DataStore = new SimpleDataStore();\n })();`)(e)}\n (() => {\n // for sidebar menu\n // hide insights tab\n if(DataStore.hasRuntimeError() || !DataStore.ranSuccessfully() || DataStore.isContextRun()) {\n document.querySelector('.menu-item[data-page="insights"]')?.remove();\n }\n\n const loadContent = (section, highlightMenu = false) => {\n if (highlightMenu) {\n document\n .querySelector('li[data-page="overview"]')\n ?.classList.add('selected');\n }\n\n const contents = document.querySelectorAll('.content');\n const wrapper = document.querySelector('.content-wrapper');\n\n contents.forEach((content) => {\n content.id !== section\n ? content.classList.remove('active')\n : content.classList.add('active');\n\n section === 'overview' &&\n window.persistOverviewCharts &&\n window.persistOverviewCharts();\n\n ['log', 'locale'].includes(section) ? wrapper.classList.add('--hide-overflow') : !DataStore.isContextRun() && !DataStore.isSanityRun() && wrapper.classList.remove('--hide-overflow');\n window.currentPage = section;\n });\n };\n\n document.addEventListener('DOMContentLoaded', function () {\n const createMenuItems = (() => {\n if(DataStore.isContextRun()) {\n return;\n }\n const ul = document.querySelector('.sidebar .submenu');\n const createListItems = (locale) => {\n const li = document.createElement('li');\n li.classList.add('sub-menu-item');\n li.dataset.locale = locale;\n\n const a = document.createElement('a');\n a.href = '#';\n a.textContent = locale;\n\n li.appendChild(a);\n return li;\n };\n\n for (const locale of DataStore.getLocales()) {\n ul.appendChild(createListItems(locale));\n }\n })();\n\n const menuItems = document.querySelectorAll('.menu-item'),\n submenuItems = document.querySelectorAll('.sub-menu-item');\n\n menuItems.forEach((item) => {\n item.addEventListener('click', function (event) {\n const selectedMenuItem = document.querySelector(\n '.menu-item.selected'\n ),\n selectedSubmenu = document.querySelector('.submenu.active'),\n page = item.dataset.page;\n\n if (selectedMenuItem) {\n selectedMenuItem.classList.remove('selected');\n }\n\n if (selectedSubmenu) {\n selectedSubmenu.classList.remove('active');\n }\n\n if (item.classList.contains('has-submenu')) {\n event.preventDefault();\n item.classList.add('selected');\n const submenu = item.querySelector('.submenu');\n submenu.classList.add('active');\n } else {\n item.classList.add('selected');\n }\n\n if (selectedSubmenu) {\n selectedSubmenu\n .querySelectorAll('.sub-menu-item')\n .forEach((submenuItem) => {\n submenuItem.textContent !== item.textContent &&\n submenuItem.classList.remove('selected');\n });\n }\n\n loadContent(page);\n });\n\n loadContent('overview', true);\n });\n\n submenuItems.forEach((sItem) => {\n sItem.addEventListener('click', function (event) {\n const selectedSubMenuItem = document.querySelector(\n '.sub-menu-item.selected'\n );\n event.preventDefault();\n event.stopPropagation();\n\n if (selectedSubMenuItem) {\n selectedSubMenuItem.classList.remove('selected');\n window.previousSelectedLocale =\n selectedSubMenuItem.dataset.locale;\n }\n\n sItem.classList.add('selected');\n loadContent('locale');\n });\n });\n\n // update footer info\n const footer = document.querySelector('.sidebar .footer');\n footer.textContent = \`\${new Date().getFullYear()} @ Zeesoft. All rights reserved.\`;\n\n // update project info\n document.querySelector(\n 'header .logo'\n ).innerHTML = \`zAutomate.AI | \${DataStore.getAppName()} [\${DataStore.isSanityRun() ? 'Quick Sanity' : DataStore.isContextRun() ? 'Context Creation' : 'I18n Verification'}]\`;\n document.querySelector(\n 'header .report-timestamp .info'\n ).innerHTML = \`Report generated at: \${DataStore.getTimestamp()}\`;\n });\n })();\n //overview content\n (() => {\n if (DataStore.hasRuntimeError()) {\n document.getElementById('overview').innerHtml =\n 'zAutomate.AI was unable to perform the operation. Please check the logs for more information.';\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n // don't render overview contents for sanity and context run\n if(DataStore.isContextRun() || DataStore.isSanityRun()) {\n document.querySelector('.content-wrapper')?.classList.add('--hide-overflow');\n document.querySelectorAll('#overview .card')?.forEach((el) => {\n if(!el.classList.contains('common-info')) {\n el.remove();\n }\n });\n\n const titleEl = document.querySelector('#overview .common-info .title');\n const infoEl = document.querySelector('#overview .common-info .info');\n\n if(titleEl && infoEl) {\n let title, info;\n\n const renderPagesSection = () => {\n let tilesEl, searchEl, searchOpEl, pages, entries, hasPages, emptyEl, currentLocale = DataStore.getDefaultLocale();\n const operatorOpts = [\n {\n name: 'Contains',\n value: '~'\n },\n {\n name: 'Starts with',\n value: '^'\n },\n {\n name: 'Ends with',\n value: '$'\n },\n {\n name: 'Equals',\n value: '='\n },\n ];\n const valueStates = {locale: DataStore.getLocales()[0], search: {op: operatorOpts[0].value, value: ''}};\n const getImagePath = (imagePath, locale = valueStates.locale) => \`\${window.getProductUrl()}/\${locale}/screenshots/reviewed/\${imagePath}\`;\n const renderEmptyContext = (container) => {\n emptyEl = document.createElement('div');\n emptyEl.classList.add('empty-context');\n emptyEl.innerHTML = 'No contexts available.';\n container.appendChild(emptyEl);\n };\n const removeEmptyContext = () => {\n if(emptyEl) {\n emptyEl.remove();\n }\n };\n\n const loadData = () => {\n pages = DataStore.getPages(valueStates.locale);\n\n if (valueStates.search.value.trim().length) {\n const value = valueStates.search.value.trim().toLowerCase();\n const op = valueStates.search.op;\n const predicate = (string) => {\n string = String(string).toLowerCase();\n switch(op) {\n case '~':\n return string.includes(value);\n case '^':\n return string.startsWith(value);\n case '$':\n return string.endsWith(value);\n case '=':\n return string === value;\n default:\n return false;\n }\n };\n const matchedPages = [];\n const matchedEntries = Object.entries(DataStore.getDataByLocale(valueStates.locale)).filter((e) => predicate(e[0]));\n matchedEntries.forEach((e) => e[1].flat().forEach(pages => !matchedPages.includes(pages.imagePath) & matchedPages.push(pages.imagePath)));\n if(matchedPages.length) {\n if(Object.keys(pages).length) {\n for (const name in pages) {\n if(!matchedPages.includes(pages[name])) {\n delete pages[name];\n }\n }\n }\n }\n else {\n pages = {};\n }\n }\n\n entries = Object.entries(pages).sort((a, b) => a[1].localeCompare(b[1]));\n hasPages = entries.length > 0;\n };\n\n const renderContextTiles = () => {\n loadData();\n\n if(!tilesEl) {\n tilesEl = createTilesSection();\n }\n else {\n tilesEl.innerHTML = '';\n }\n\n if(!hasPages) {\n renderEmptyContext(tilesEl);\n return;\n }\n\n removeEmptyContext();\n createPageTiles(tilesEl);\n };\n\n const createTilesSection = () => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('page-tiles');\n const titleSection = document.createElement('div');\n titleSection.classList.add('title-section');\n const tilesListWrapper = document.createElement('div');\n const tilesEl = document.createElement('div');\n tilesListWrapper.classList.add('tiles-list-wrapper');\n tilesListWrapper.appendChild(tilesEl);\n tilesEl.classList.add('tiles-list');\n wrapperEl.appendChild(titleSection);\n wrapperEl.appendChild(tilesListWrapper);\n\n const createTitle = () => {\n const titleWrapper = document.createElement('div');\n titleWrapper.classList.add('title-wrapper');\n const titleEl = document.createElement('span');\n titleEl.classList.add('title');\n titleEl.innerHTML = 'Context Pages'\n titleWrapper.appendChild(titleEl);\n titleSection.appendChild(titleWrapper);\n };\n\n const createSearchField = () => {\n const searchWrapperEl = document.createElement('div');\n searchWrapperEl.classList.add('search-field-wrapper');\n\n const createLabel = () => {\n const label = document.createElement("label");\n label.textContent = "Search string: ";\n label.setAttribute("for", "operator-filter");\n\n searchWrapperEl.appendChild(label);\n };\n\n const createOperatorFilter = () => {\n searchOpEl = document.createElement("select");\n searchOpEl.id = "operator-filter";\n if(valueStates.search.op) {\n searchOpEl.title = operatorOpts.find((op) => op.value === valueStates.search.op).name;\n }\n searchOpEl.addEventListener("change", function () {\n if (valueStates.search.op !== this.value) {\n valueStates.search.op = this.value;\n renderContextTiles();\n }\n });\n\n operatorOpts.forEach((op) => {\n const option = document.createElement("option");\n option.value = op.value;\n option.textContent = op.name;\n if(valueStates.search.op === op.value) {\n option.selected = true;\n }\n searchOpEl.appendChild(option);\n });\n\n searchWrapperEl.appendChild(searchOpEl);\n };\n\n const createSearchField = () => {\n searchEl = document.createElement('input');\n searchEl.type = 'text';\n searchEl.id = 'search-input';\n searchEl.placeholder = 'Search strings...'\n\n searchEl.addEventListener('input', function () {\n if(valueStates.search.value !== this.value) {\n valueStates.search.value = this.value;\n renderContextTiles();\n }\n });\n\n searchWrapperEl.appendChild(searchEl);\n };\n\n createLabel();\n createOperatorFilter();\n createSearchField();\n titleSection.appendChild(searchWrapperEl);\n };\n\n const createLocaleFilter = () => {\n const localeFilterWrapper = document.createElement('div');\n localeFilterWrapper.classList.add('locale-filter-wrapper');\n const label = document.createElement("label");\n label.textContent = "Locale: ";\n label.setAttribute("for", "locale-filter");\n\n const select = document.createElement("select");\n select.id = "locale-filter";\n select.addEventListener("change", function () {\n currentLocale = this.value;\n if (valueStates.locale !== this.value) {\n valueStates.locale = this.value;\n valueStates.search.op = operatorOpts[0].value;\n valueStates.search.value = '';\n if(searchEl) {\n searchEl.value = '';\n }\n if(searchOpEl) {\n searchOpEl.value = operatorOpts[0].value;\n }\n renderContextTiles();\n }\n });\n\n const options = [...DataStore.getLocales()];\n options.forEach((locale) => {\n const option = document.createElement("option");\n option.value = locale;\n option.textContent = locale;\n if(valueStates.locale === locale) {\n option.selected = true;\n }\n select.appendChild(option);\n });\n\n localeFilterWrapper.appendChild(label);\n localeFilterWrapper.appendChild(select);\n titleSection.appendChild(localeFilterWrapper);\n };\n\n createTitle();\n createSearchField();\n createLocaleFilter();\n\n document.querySelector('#overview .common-info .section').appendChild(wrapperEl);\n return tilesEl;\n };\n\n const createTile = (idx, page, imagePath) => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('tile-container');\n\n const imageEl = document.createElement('img');\n imageEl.classList.add('image');\n imageEl.src = getImagePath(imagePath);\n\n const nameWrapper = document.createElement('div');\n const name = page;\n nameWrapper.classList.add('name');\n nameWrapper.title = name\n\n const nameEl = document.createElement('span');\n nameEl.innerHTML = name;\n nameWrapper.appendChild(nameEl);\n\n wrapperEl.appendChild(imageEl);\n wrapperEl.appendChild(nameWrapper);\n wrapperEl.addEventListener('click', () => openContextViewer(idx, valueStates.locale));\n return wrapperEl;\n };\n const createPageTiles = (container) => {\n let counter = 0;\n for(const [page, imagePath] of entries) {\n container.appendChild(createTile(counter, page, imagePath));\n counter++;\n }\n };\n\n const openContextViewer = (idx, locale) => {\n let loaderEl, imageViewerEl, imageViewerMainWrapperEl, imagesSplitViewEl, sourceImageEl, targetImageEl, smartCompareSwitchEl, smartCompareKnobEl, pageCountEl, pageNameEl, zoomInEl, zoomOutEl, disableSmartCompare = false;\n const data = [...entries];\n const pageOpts = [...data.map(d => d[0])];\n const anchorEl = document.createElement('div');\n anchorEl.classList.add('context-image-viewer');\n const fieldStates = {search: {op: operatorOpts[0].value, value: ''}, filters: {page: ''}, smartCompare: false, highlightStrings: false, zoomLevel: 1, offsets: {x: 0, y: 0, moveX: 0, moveY: 0}, dragging: {isActive: false, isSource: false, isTarget: false}};\n const valueStates = {page: '', currentIndex: idx, imagePath: ''};\n const getTotalSize = () => data.length;\n\n\n const updateContextSwitcher = () => {\n const selectEl = document.getElementById("context-switcher");\n if (!selectEl) return;\n selectEl.value = valueStates.page;\n };\n\n const createCloseBtn = () => {\n const closeBtnEl = document.createElement('span');\n closeBtnEl.innerHTML = '×';\n closeBtnEl.classList.add('close-btn');\n closeBtnEl.title = 'Close image viewer'\n\n const closeViewer = () => anchorEl.remove();\n\n closeBtnEl.addEventListener('click', closeViewer);\n anchorEl.appendChild(closeBtnEl);\n keyAction('Escape', closeViewer);\n };\n\n const keyAction = (key, action) => {\n document.addEventListener('keydown', (event) => {\n if (event.key === key) {\n action();\n }\n });\n };\n\n const createLoader = () => {\n loaderEl = document.createElement('div');\n loaderEl.classList.add('loader');\n loaderEl.classList.add('--hidden');\n loaderEl.innerHTML = 'Loading...';\n anchorEl.appendChild(loaderEl);\n };\n\n const showLoader = (show, time = 1) => show ? loaderEl.classList.remove('--hidden') : time > 0 ? setTimeout(()=> loaderEl.classList.add('--hidden'), time) : loaderEl.classList.add('--hidden');\n\n const loadData = () => {\n const updateDependencies = () => {\n if(DataStore.isDefaultLocale(currentLocale)) {\n disableSmartCompare = true;\n }\n else if(!DataStore.hasDefaultLocaleData()) {\n disableSmartCompare = true;\n }\n else {\n const defaultLocaleData = DataStore.getPages(DataStore.getDefaultLocale());\n const defaultLocalePages = Object.keys(defaultLocaleData);\n if(defaultLocalePages.length === 0) {\n disableSmartCompare = true;\n }\n else if(!defaultLocalePages.includes(data[valueStates.currentIndex][0])) {\n disableSmartCompare = true;\n }\n else {\n disableSmartCompare = false;\n }\n }\n };\n const setPageName = () => {\n valueStates.page = data[valueStates.currentIndex][0];\n };\n\n updateDependencies();\n setPageName();\n\n };\n\n const renderData = (loadingMaskRemovalDelay = 0, resetZoom = false, showLoadingMask = true) => {\n const renderPageTitle = () => {\n const renderPageCount = () => {\n pageCountEl.innerHTML = \`#\${valueStates.currentIndex + 1}/\${getTotalSize()}\`;\n };\n const renderPageName = () => {\n pageNameEl.innerHTML = valueStates.page;\n pageNameEl.title = valueStates.page;\n };\n\n renderPageCount();\n renderPageName();\n };\n\n const renderSmartCompareValue = () => {\n if(disableSmartCompare) {\n !smartCompareSwitchEl.parentElement.classList.contains('--hidden') && smartCompareSwitchEl.parentElement.classList.add('--hidden');\n }\n else {\n smartCompareSwitchEl.parentElement.classList.contains('--disabled') && smartCompareSwitchEl.parentElement.classList.remove('--hidden');\n smartCompareSwitchEl.style.backgroundColor = fieldStates.smartCompare ? "green" : "gray";\n smartCompareKnobEl.style.left = fieldStates.smartCompare ? "55%" : "5%";\n }\n };\n\n const renderImages = (loadingMaskDelay) => {\n let hasSourceLoaded, hasTargetLoaded;\n const entry = data[valueStates.currentIndex];\n const cursor = fieldStates.zoomLevel > 1 ? fieldStates.dragging.isActive ? 'grabbing' : 'grab' : 'default';\n const css = fieldStates.zoomLevel === 1 ? 'translate(0, 0) scale(1)'\n : \`translate(\${fieldStates.offsets.moveX}px, \${fieldStates.offsets.moveY}px) scale(\${fieldStates.zoomLevel})\`;\n\n if(fieldStates.smartCompare) {\n sourceImageEl.parentElement.classList.remove('--hidden');\n sourceImageEl.src = getImagePath(entry[1], DataStore.getDefaultLocale());\n sourceImageEl.alt = entry[0];\n sourceImageEl.style.transform = css;\n targetImageEl.parentElement.querySelector('.locale-name-bar').style.left = '50.9rem';\n\n if(showLoadingMask) {\n sourceImageEl.onload = () => {\n let interval;\n interval = setInterval(() => {\n if(hasTargetLoaded) {\n clearInterval(interval);\n showLoader(false, loadingMaskDelay);\n } \n }, 10);\n };\n targetImageEl.onload = () => {\n hasTargetLoaded = true;\n };\n }\n }\n else {\n sourceImageEl.parentElement.classList.add('--hidden');\n targetImageEl.parentElement.querySelector('.locale-name-bar').style.left = '2rem';\n\n if(showLoadingMask) {\n targetImageEl.onload = () => {\n showLoader(false, loadingMaskDelay);\n };\n }\n }\n\n targetImageEl.src = getImagePath(entry[1]);\n targetImageEl.alt = entry[0];\n targetImageEl.style.transform = css;\n\n sourceImageEl.parentElement.style.cursor = cursor;\n targetImageEl.parentElement.style.cursor = cursor;\n };\n\n const resetZoomBtns = () => {\n const zoomOutBtn = document.querySelector('.viewer .zoom-out-btn');\n const zoomInBtn = document.querySelector('.viewer .zoom-in-btn');\n if (!zoomOutBtn.classList.contains('--disbaled')) {\n zoomOutBtn.classList.add('--disabled');\n }\n if (zoomInBtn.classList.contains('--disabled')) {\n zoomInBtn.classList.remove('--disabled');\n }\n fieldStates.zoomLevel = 1;\n fieldStates.offsets.x = 0;\n fieldStates.offsets.y = 0;\n fieldStates.offsets.moveX = 0;\n fieldStates.offsets.moveY = 0;\n };\n\n showLoadingMask && showLoader(true);\n resetZoom && resetZoomBtns();\n loadData();\n renderPageTitle();\n renderSmartCompareValue();\n renderImages(loadingMaskRemovalDelay);\n };\n\n const createImageViewer = () => {\n imageViewerEl = document.createElement('div');\n imageViewerEl.classList.add('viewer');\n\n imageViewerMainWrapperEl = document.createElement('div');\n imageViewerMainWrapperEl.classList.add('wrapper');\n imageViewerEl.appendChild(imageViewerMainWrapperEl);\n anchorEl.appendChild(imageViewerEl);\n\n createNavigationButtons();\n createZoomButtons();\n createToolbar();\n createSplitView();\n renderData(300);\n document.body.appendChild(anchorEl);\n };\n\n const createNavigationButtons = () => {\n let prevBtn, nextBtn;\n\n const createPrevBtn = () => {\n prevBtn = document.createElement('div');\n prevBtn.classList.add('prev-btn');\n prevBtn.title = 'Show previous context';\n\n const prevIconEl = document.createElement('span');\n prevIconEl.innerHTML = '<';\n prevBtn.appendChild(prevIconEl);\n imageViewerEl.appendChild(prevBtn);\n\n const movePrevAction = () => canMovePrev() && movePrev();\n prevBtn.addEventListener('click', movePrevAction);\n keyAction("ArrowLeft", () => {\n if (valueStates.currentIndex > 0) {\n movePrev();\n updateContextSwitcher();\n }\n });\n };\n const createNextBtn = () => {\n nextBtn = document.createElement('div');\n nextBtn.classList.add('next-btn');\n nextBtn.title = 'Show next context';\n\n const nextIconEl = document.createElement('span');\n nextIconEl.innerHTML = '>';\n nextBtn.appendChild(nextIconEl);\n imageViewerEl.appendChild(nextBtn);\n \n const moveNextAction = () => canMoveNext() && moveNext();\n nextBtn.addEventListener('click', moveNextAction);\n keyAction("ArrowRight", () => {\n if (valueStates.currentIndex < pageOpts.length - 1) {\n moveNext();\n updateContextSwitcher();\n }\n });\n };\n const canMoveNext = () => valueStates.currentIndex < getTotalSize() - 1;\n const canMovePrev = () => valueStates.currentIndex > 0;\n const moveNext = () => {\n valueStates.currentIndex++;\n determineBtnsAccessibility();\n renderData(0, true, true);\n updateContextSwitcher();\n };\n const movePrev = () => {\n valueStates.currentIndex--;\n determineBtnsAccessibility();\n renderData(0, true, true);\n updateContextSwitcher();\n };\n const disableBtn = (btn) => btn === 'prev' ? prevBtn.classList.add('--disabled') : nextBtn.classList.add('--disabled');\n const enableBtn = (btn) => btn === 'prev' ? prevBtn.classList.remove('--disabled') : nextBtn.classList.remove('--disabled');\n const isBtnDisabled = (btn) => btn === 'prev' ? prevBtn.classList.contains('--disabled') : nextBtn.classList.contains('--disabled');\n const determineNextBtnAccessibility = () => {\n if(!canMoveNext()) {\n !isBtnDisabled('next') && disableBtn('next');\n }\n else if(isBtnDisabled('next')) {\n enableBtn('next');\n }\n };\n const determinePrevBtnAccessibility = () => {\n if(!canMovePrev()) {\n !isBtnDisabled('prev') && disableBtn('prev');\n }\n else if(isBtnDisabled('prev')) {\n enableBtn('prev');\n }\n };\n const determineBtnsAccessibility = () => {\n determinePrevBtnAccessibility();\n determineNextBtnAccessibility();\n };\n\n createPrevBtn();\n createNextBtn();\n determineBtnsAccessibility();\n };\n\n const createZoomButtons = () => {\n const maxLevel = 10;\n const baseLevel = 1;\n const levelStep = 1;\n let zoomInBtn, zoomOutBtn;\n\n const canZoomIn = () => fieldStates.zoomLevel + 0.5 <= maxLevel;\n const canZoomOut = () => fieldStates.zoomLevel - 0.5 >= baseLevel;\n const isBtnDisabled = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.contains('--disabled') : zoomOutBtn.classList.contains('--disabled');\n const disableBtn = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.add('--disabled') : zoomOutBtn.classList.add('--disabled');\n const enableBtn = (btn) => btn === 'zoom-in' ? zoomInBtn.classList.remove('--disabled') : zoomOutBtn.classList.remove('--disabled');\n const determineZoomInBtnAccessibility = () => {\n if(!canZoomIn()) {\n !isBtnDisabled('zoom-in') && disableBtn('zoom-in');\n }\n else if(isBtnDisabled('zoom-in')) {\n enableBtn('zoom-in');\n }\n };\n const determineZoomOutBtnAccessibility = () => {\n if(!canZoomOut()) {\n !isBtnDisabled('zoom-out') && disableBtn('zoom-out');\n }\n else if(isBtnDisabled('zoom-out')) {\n enableBtn('zoom-out');\n }\n };\n const determineBtnsAccessibility = () => {\n determineZoomInBtnAccessibility();\n determineZoomOutBtnAccessibility();\n };\n const zoomIn = () => {\n fieldStates.zoomLevel += 0.5;\n determineBtnsAccessibility();\n renderData(0, false, false);\n };\n const zoomOut = () => {\n fieldStates.zoomLevel -= 0.5;\n determineBtnsAccessibility();\n renderData(0, false, false);\n }\n const resetZoomLevel = () => {\n fieldStates.zoomLevel = 1;\n determineBtnsAccessibility();\n };\n const createZoomInBtn =() => {\n zoomInBtn = document.createElement('div');\n zoomInBtn.classList.add('zoom-in-btn');\n zoomInBtn.title = 'Zoom In';\n\n const zoomInIconEl = document.createElement('span');\n zoomInIconEl.innerHTML = '+';\n zoomInBtn.appendChild(zoomInIconEl);\n imageViewerEl.appendChild(zoomInBtn);\n\n const zoomInAction = () => canZoomIn() && zoomIn();\n zoomInBtn.addEventListener('click', zoomInAction);\n keyAction('=', zoomInAction);\n };\n\n const createZoomOutBtn = () => {\n zoomOutBtn = document.createElement('div');\n zoomOutBtn.classList.add('zoom-out-btn');\n zoomOutBtn.title = 'Zoom Out';\n\n const zoomOutIconEl = document.createElement('span');\n zoomOutIconEl.innerHTML = '-';\n zoomOutBtn.appendChild(zoomOutIconEl);\n imageViewerEl.appendChild(zoomOutBtn);\n\n const performZoomOut = () => canZoomOut() && zoomOut();\n zoomOutBtn.addEventListener('click', performZoomOut);\n keyAction('-', performZoomOut);\n \n };\n\n createZoomInBtn();\n createZoomOutBtn();\n resetZoomLevel();\n };\n\n const createToolbar = () => {\n const toolbarEl = document.createElement('div');\n toolbarEl.classList.add('toolbar');\n\n const createPageTitle = () => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('page-title-wrapper');\n\n pageCountEl = document.createElement('div');\n pageCountEl.classList.add('page-count');\n\n const pageNameWrapperEl = document.createElement('div');\n pageNameWrapperEl.classList.add('page-title');\n\n pageNameEl = document.createElement('span');\n pageNameWrapperEl.appendChild(pageNameEl);\n\n wrapperEl.appendChild(pageCountEl);\n wrapperEl.appendChild(pageNameWrapperEl);\n\n toolbarEl.appendChild(wrapperEl);\n };\n\n const createContextSwitcher = () => {\n const wrapperEl = document.createElement('div');\n wrapperEl.classList.add('context-switcher-wrapper');\n\n const label = document.createElement("label");\n label.textContent = "Context Switcher: ";\n label.setAttribute("for", "context-switcher");\n\n const selectEl = document.createElement("select");\n selectEl.id = "context-switcher";\n selectEl.classList.add("context-dropdown");\n\n const isTextClipped = (element) => {\n return element.offsetWidth < element.scrollWidth;\n };\n\n pageOpts.forEach(page => {\n const optionEl = document.createElement("option");\n optionEl.value = page;\n optionEl.textContent = page;\n\n if (page === valueStates.page) {\n optionEl.selected = true;\n }\n\n optionEl.addEventListener("mouseover", function () {\n if (isTextClipped(optionEl)) {\n optionEl.setAttribute("title", page);\n } else {\n optionEl.removeAttribute("title");\n }\n });\n selectEl.appendChild(optionEl);\n });\n\n selectEl.addEventListener("change", (event) => {\n const selectedPage = event.target.value;\n valueStates.page = selectedPage;\n valueStates.currentIndex = pageOpts.indexOf(selectedPage);\n renderData(0, true);\n });\n\n wrapperEl.appendChild(label);\n wrapperEl.appendChild(selectEl);\n toolbarEl.appendChild(wrapperEl);\n };\n\n const createSmartCompateBtn = () => {\n const wrapperEl = document.createElement("div");\n wrapperEl.classList.add('smart-compare-wrapper');\n\n const label = document.createElement("span");\n label.textContent = "Smart compare: ";\n\n smartCompareSwitchEl = document.createElement("div");\n smartCompareSwitchEl.classList.add('switch-wrapper');\n wrapperEl.title = 'Compare with source locale context image';\n\n smartCompareKnobEl = document.createElement("div");\n smartCompareKnobEl.classList.add('switch-knob');\n\n smartCompareSwitchEl.addEventListener("click", function () {\n if(disableSmartCompare) {\n return;\n }\n fieldStates.smartCompare = !fieldStates.smartCompare;\n renderData(300, true, true);\n });\n\n wrapperEl.appendChild(label);\n smartCompareSwitchEl.appendChild(smartCompareKnobEl);\n wrapperEl.appendChild(smartCompareSwitchEl);\n toolbarEl.appendChild(wrapperEl);\n };\n\n createPageTitle();\n createContextSwitcher();\n createSmartCompateBtn();\n\n imageViewerMainWrapperEl.appendChild(toolbarEl);\n };\n\n const createSplitView = (enableSplitView = false) => {\n const clampImage = (value, min, max) => {\n return Math.max(min, Math.min(value, max));\n };\n\n const startDrag = (event, img) => {\n if(fieldStates.zoomLevel === 1) {\n return;\n }\n\n fieldStates.dragging.isActive = true;\n fieldStates.dragging.isSource = img === 'source';\n fieldStates.dragging.isTarget = img === 'target';\n fieldStates.offsets.x = event.clientX;\n fieldStates.offsets.y = event.clientY;\n sourceImageWrapper.style.cursor = "grabbing";\n targetImageWrapper.style.cursor = "grabbing";\n };\n\n const stopDrag = () => {\n const cursor = fieldStates.zoomLevel === 1 ? 'default' : 'grab';\n fieldStates.dragging.isActive = false;\n fieldStates.dragging.isSource = false;\n fieldStates.dragging.isTarget = false;\n sourceImageWrapper.style.cursor = cursor;\n targetImageWrapper.style.cursor = cursor;\n };\n\n const dragMove = (event) => {\n if (!fieldStates.dragging.isActive || fieldStates.zoomLevel === 1) {\n return;\n }\n\n const dx = (event.clientX - fieldStates.offsets.x) * fieldStates.zoomLevel;\n const dy = (event.clientY - fieldStates.offsets.y) * fieldStates.zoomLevel;\n fieldStates.offsets.moveX += dx;\n fieldStates.offsets.moveY += dy;\n\n const wrapper = fieldStates.dragging.isSource ? sourceImageWrapper : targetImageWrapper;\n const image = fieldStates.dragging.isSource ? sourceImageEl : targetImageEl;\n const viewerWidth = wrapper.clientWidth;\n const viewerHeight = wrapper.clientHeight;\n const imgWidth = image.width * fieldStates.zoomLevel;\n const imgHeight = image.height * fieldStates.zoomLevel;\n\n const minX = -(imgWidth - viewerWidth) / 2;\n const maxX = (imgWidth - viewerWidth) / 2;\n const minY = -(imgHeight - viewerHeight) / 2;\n const maxY = (imgHeight - viewerHeight) / 2;\n\n fieldStates.offsets.moveX = clampImage(fieldStates.offsets.moveX, minX, maxX);\n fieldStates.offsets.moveY = clampImage(fieldStates.offsets.moveY, minY, maxY);\n\n fieldStates.offsets.x = event.clientX;\n fieldStates.offsets.y = event.clientY;\n\n renderData(0, false, false);\n };\n\n const imageSectionEl = document.createElement('div');\n imageSectionEl.classList.add('image-section');\n\n imagesSplitViewEl = document.createElement('div');\n imagesSplitViewEl.classList.add('split-view');\n\n const sourceImageWrapper = document.createElement('div');\n const targetImageWrapper = document.createElement('div');\n const sourceLocaleNameBar = document.createElement('div');\n const sourceLocaleNameEl = document.createElement('span');\n const targetLocaleNameBar = document.createElement('div');\n const targetLocaleNameEl = document.createElement('span');\n sourceLocaleNameBar.classList.add('locale-name-bar');\n targetLocaleNameBar.classList.add('locale-name-bar');\n sourceLocaleNameEl.innerHTML = \`\${DataStore.getDefaultLocale()} Image\`;\n targetLocaleNameEl.innerHTML = \`\${locale} Image\`;\n\n sourceImageWrapper.classList.add('source-image-wrapper');\n targetImageWrapper.classList.add('target-image-wrapper');\n\n\n sourceImageEl = document.createElement('img');\n sourceImageEl.classList.add('source-image');\n sourceImageWrapper.classList.add('--hidden');\n\n targetImageEl = document.createElement('img');\n targetImageEl.classList.add('target-image');\n\n sourceImageWrapper.addEventListener("mousedown", (e) => startDrag(e, 'source'));\n targetImageWrapper.addEventListener("mousedown", (e) => startDrag(e, 'target'));\n sourceImageWrapper.addEventListener("mouseup", (e) => stopDrag(e, 'source'));\n targetImageWrapper.addEventListener("mouseup", (e) => stopDrag(e, 'target'));\n document.addEventListener("mousemove", dragMove);\n\n sourceImageWrapper.appendChild(sourceImageEl);\n targetImageWrapper.appendChild(targetImageEl);\n sourceLocaleNameBar.appendChild(sourceLocaleNameEl);\n sourceImageWrapper.appendChild(sourceLocaleNameBar);\n targetLocaleNameBar.appendChild(targetLocaleNameEl);\n targetImageWrapper.appendChild(targetLocaleNameBar);\n\n imagesSplitViewEl.appendChild(sourceImageWrapper);\n imagesSplitViewEl.appendChild(targetImageWrapper);\n\n imageSectionEl.appendChild(imagesSplitViewEl);\n imageViewerMainWrapperEl.appendChild(imageSectionEl);\n };\n\n loadData();\n createCloseBtn();\n createLoader();\n createImageViewer();\n };\n\n renderContextTiles();\n };\n\n if(DataStore.isContextRun()) {\n title = 'Context Creation Details';\n info = \`\${window.isStandaloneReport ? 'Created' : 'Imported'} context screenshots for the locales \${DataStore.getLocales().map(locale => {\n const localeStats = DataStore.getStatByLocale(locale);\n return \`<strong>\${locale}</strong>(<em>\${localeStats?.context || 0} contexts</em>, <em>\${localeStats?.reviewed || 0} strings</em>)\`;\n }).join(', ')}\${!window.isStandaloneReport ? \`with version <strong>\${DataStore.getReportFolderTimestamp()}</strong>\` : ''}.\`;\n renderPagesSection();\n }\n else {\n const locales = DataStore.getLocales();\n title = 'Sanity run details';\n info = \`Sanity run has successfully covered \${DataStore.getStatByLocale(locales[0])?.reviewed || 0} pages for the locales \${locales.map(locale => {\n return \`<strong>\${locale}</em>\`;\n }).join(', ')}.\`;\n }\n\n titleEl.textContent = title;\n infoEl.innerHTML = info;\n }\n\n return;\n }\n else {\n document.querySelector('#overview .common-info')?.remove();\n\n let overviewChart,\n stringChart,\n dateTimeChart,\n numberChart,\n ignoredChart;\n\n window.persistOverviewCharts = () => {\n if(DataStore.isContextRun() || DataStore.isSanityRun()) {\n return;\n }\n\n const reset = (chart) => {\n chart.resetSeries(true, true);\n };\n\n reset(overviewChart);\n reset(stringChart);\n reset(dateTimeChart);\n reset(numberChart);\n reset(ignoredChart);\n };\n\n renderOverallChart();\n renderStringChart();\n renderIgnoredChart();\n renderNumberChart();\n renderDatetimeChart();\n\n function renderOverallChart() {\n const overallStats = DataStore.getOverallStats();\n const reviewed = overallStats.reviewed;\n const locales = DataStore.getLocales().length;\n const type = {\n I18NED: 'Internationalized',\n NOT_I18NED: 'Not Internationalized',\n SKIPPED: 'Skipped'\n },\n valueMap = {\n [type.I18NED]:\n overallStats.localizedStrings +\n overallStats.partialStrings +\n overallStats.untranslatedStrings +\n overallStats.localizedDateTime +\n overallStats.localizedDate +\n overallStats.localizedTime +\n overallStats.localizedNumber +\n overallStats.localizedCurrency +\n overallStats.localizedPercentage,\n [type.NOT_I18NED]:\n overallStats.notLocalizedStrings +\n overallStats.notLocalizedDateTime +\n overallStats.notLocalizedDate +\n overallStats.notLocalizedTime +\n overallStats.notLocalizedNumber +\n overallStats.notLocalizedCurrency +\n overallStats.notLocalizedPercentage,\n [type.SKIPPED]:\n overallStats.dnt +\n overallStats.ignored +\n overallStats.autoIgnored +\n overallStats.userInput\n },\n percentageCalc = (value) => {\n const val = ((value / reviewed) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n };\n\n var options = {\n series: [\n {\n name: '',\n data: [\n percentageCalc(valueMap[type.I18NED]),\n percentageCalc(valueMap[type.NOT_I18NED]),\n percentageCalc(valueMap[type.SKIPPED])\n ]\n }\n ],\n chart: {\n type: 'bar',\n height: 200,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n plotOptions: {\n bar: {\n horizontal: true,\n borderRadius: 3,\n dataLabels: {\n position: 'right'\n }\n }\n },\n title: {\n text: \`Total Reviewed strings in \${locales} locales: \${reviewed}\`,\n align: 'center',\n floating: true,\n style: {\n fontSize: '14px',\n fontWeight: 'bold'\n }\n },\n dataLabels: {\n enabled: true,\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n xaxis: {\n categories: [type.I18NED, type.NOT_I18NED, type.SKIPPED],\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n yaxis: {\n labels: {\n style: {\n fontSize: '13px'\n }\n }\n },\n colors: [\n (v) => {\n switch (v.dataPointIndex) {\n case 0:\n return '#31E994'; //green\n case 1:\n return '#ff8173'; //red\n case 2:\n return '#f5eb5f'; //yellow\n }\n }\n ],\n tooltip: {\n theme: 'dark',\n x: {\n formatter: (v) => \`Total Number of \${v} instances:\`\n },\n y: {\n formatter: function (v, { dataPointIndex }) {\n let count = 0;\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.I18NED];\n break;\n case 1:\n count = valueMap[type.NOT_I18NED];\n break;\n case 2:\n count = valueMap[type.SKIPPED];\n break;\n }\n return \`\${v}% (\${count} strings)\`;\n }\n }\n }\n };\n\n (overviewChart = new ApexCharts(\n document.querySelector('.overall-quality .graph'),\n options\n )).render();\n }\n\n function renderStringChart() {\n const overallStats = DataStore.getOverallStats();\n const type = {\n LOCALIZED: 'Localized',\n PARTIAL: 'Partially Localized',\n NOT_LOCALIZED: 'Not Localized',\n UNTRANSLATED: 'Untranslated',\n ENCODING: 'Encoding Issue',\n IGNORED: 'Ignored'\n },\n valueMap = {\n [type.LOCALIZED]: overallStats.localizedStrings,\n [type.PARTIAL]: overallStats.partialStrings,\n [type.NOT_LOCALIZED]: overallStats.notLocalizedStrings,\n [type.UNTRANSLATED]: overallStats.untranslatedStrings,\n [type.ENCODING]: overallStats.encoding,\n [type.IGNORED]:\n overallStats.dnt +\n overallStats.ignored +\n overallStats.autoIgnored +\n overallStats.userInput\n },\n total = Object.values(valueMap).reduce(\n (acc, curr) => acc + curr,\n 0\n ),\n data = [\n valueMap[type.LOCALIZED],\n valueMap[type.PARTIAL],\n valueMap[type.NOT_LOCALIZED],\n valueMap[type.IGNORED],\n valueMap[type.ENCODING],\n valueMap[type.UNTRANSLATED]\n ],\n options = {\n series:\n data.reduce((acc, curr) => acc + curr, 0) === 0 ? [] : data,\n chart: {\n width: 430,\n type: 'pie',\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n noData: {\n text: 'No metrics available',\n align: 'left',\n verticalAlign: 'middle',\n offsetX: 0,\n offsetY: -50,\n style: {\n fontSize: '14px'\n }\n },\n labels: [\n type.LOCALIZED,\n type.PARTIAL,\n type.NOT_LOCALIZED,\n type.IGNORED,\n type.ENCODING,\n type.UNTRANSLATED\n ],\n tooltip: {\n theme: 'dark',\n y: {\n formatter: function (v, { config, dataPointIndex }) {\n let count = 0,\n value = Number(\n (v /\n config.series.reduce((acc, curr) => acc + curr, 0)) *\n 100\n ).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.LOCALIZED];\n break;\n case 1:\n count = valueMap[type.PARTIAL];\n break;\n case 2:\n count = valueMap[type.NOT_LOCALIZED];\n break;\n case 3:\n count = valueMap[type.IGNORED];\n break;\n case 4:\n count = valueMap[type.ENCODING];\n break;\n case 5:\n count = valueMap[type.UNTRANSLATED];\n break;\n }\n\n return \`\${value}% (\${count} strings)\`;\n },\n title: {\n formatter: function (a) {\n return \`Total Number of \${a} strings:\`;\n }\n }\n }\n },\n colors: [\n ({ seriesIndex, w }) => {\n const currType = w.config.labels[seriesIndex];\n\n switch (currType) {\n case type.LOCALIZED:\n return '#31E994';\n case type.NOT_LOCALIZED:\n return '#E44242';\n case type.PARTIAL:\n return '#DADF40';\n case type.UNTRANSLATED:\n return '#F44E9E';\n case type.IGNORED:\n return '#AFA6AB';\n case type.ENCODING:\n return '#3AD1F3';\n\n default:\n return '';\n }\n }\n ],\n legend: {\n offsetX: -50,\n width: 170,\n formatter: (seriesName, a, b, c) => {\n let value = ((valueMap[seriesName] / total) * 100).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n return \`\${seriesName} (\${value}%)\`;\n }\n },\n dataLabels: {\n formatter: (v) => \`\${Number(v).toFixed(0)}%\`,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n }\n }\n };\n\n (stringChart = new ApexCharts(\n document.querySelector('.string-quality .graph'),\n options\n )).render();\n }\n\n function renderIgnoredChart() {\n const overallStats = DataStore.getOverallStats();\n const type = {\n DNT: 'DNT',\n IGNORED: 'Ignored',\n USER_INPUTS: 'User Inputs'\n },\n valueMap = {\n [type.DNT]: overallStats.dnt,\n [type.IGNORED]: overallStats.ignored + overallStats.autoIgnored,\n [type.USER_INPUTS]: overallStats.userInput\n },\n total = Object.values(valueMap).reduce(\n (acc, curr) => acc + curr,\n 0\n ),\n data = [\n valueMap[type.DNT],\n valueMap[type.IGNORED],\n valueMap[type.USER_INPUTS]\n ],\n options = {\n series:\n data.reduce((acc, curr) => acc + curr, 0) === 0 ? [] : data,\n chart: {\n width: 400,\n type: 'pie',\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n labels: [type.DNT, type.IGNORED, type.USER_INPUTS],\n tooltip: {\n theme: 'dark',\n y: {\n formatter: function (v, { config, dataPointIndex }) {\n let count = 0,\n value = Number(\n (v /\n config.series.reduce((acc, curr) => acc + curr, 0)) *\n 100\n ).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n\n switch (dataPointIndex) {\n case 0:\n count = valueMap[type.DNT];\n break;\n case 1:\n count = valueMap[type.IGNORED];\n break;\n case 2:\n count = valueMap[type.USER_INPUTS];\n break;\n }\n\n return \`\${value}% (\${count} strings)\`;\n },\n title: {\n formatter: function (a) {\n return \`Total Number of \${a} strings:\`;\n }\n }\n }\n },\n colors: [\n ({ seriesIndex, w }) => {\n const currType = w.config.labels[seriesIndex];\n\n switch (currType) {\n case type.DNT:\n return '#31E994';\n case type.IGNORED:\n return '#3AD1F3';\n case type.USER_INPUTS:\n return '#DADF40';\n\n default:\n return '';\n }\n }\n ],\n noData: {\n text: 'No metrics available',\n align: 'center',\n verticalAlign: 'middle',\n offsetX: -70,\n offsetY: -25,\n style: {\n fontSize: '14px'\n }\n },\n legend: {\n offsetX: -40,\n formatter: (seriesName) => {\n let value = ((valueMap[seriesName] / total) * 100).toFixed(0);\n value = isNaN(value) || !isFinite(value) ? 0 : value;\n return \`\${seriesName} (\${value}%)\`;\n }\n },\n dataLabels: {\n formatter: (v) => \`\${Number(v).toFixed(0)}%\`,\n style: {\n colors: ['#080404b0']\n },\n dropShadow: { enabled: false }\n }\n };\n\n (ignoredChart = new ApexCharts(\n document.querySelector('.skipped-items .graph'),\n options\n )).render();\n }\n\n function renderNumberChart() {\n const overallStats = DataStore.getOverallStats();\n const types = ['Number', 'Currency', 'Percentage'],\n data = {\n number_i18n: overallStats.localizedNumber,\n currency_i18n: overallStats.localizedCurrency,\n percentage_i18n: overallStats.localizedPercentage,\n number_not_i18n: overallStats.notLocalizedNumber,\n currency_not_i18n: overallStats.notLocalizedCurrency,\n percentage_not_i18n: overallStats.notLocalizedPercentage\n },\n total = Object.values(data).reduce((acc, curr) => acc + curr, 0),\n percentageCalc = (v, t) => {\n const val = ((v / t) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n },\n i18nPercentage = percentageCalc(\n data.number_i18n + data.percentage_i18n + data.currency_i18n,\n total\n ),\n nonI18nPercentage = percentageCalc(\n data.number_not_i18n +\n data.percentage_not_i18n +\n data.currency_not_i18n,\n total\n ),\n numberTotal = data.number_i18n + data.number_not_i18n,\n currencyTotal = data.currency_i18n + data.currency_not_i18n,\n percentageTotal = data.percentage_i18n + data.percentage_not_i18n,\n numberI18n = percentageCalc(data.number_i18n, numberTotal),\n currencyI18n = percentageCalc(data.currency_i18n, currencyTotal),\n percentageI18n = percentageCalc(\n data.percentage_i18n,\n currencyTotal\n ),\n numberNotI18n = percentageCalc(data.number_not_i18n, numberTotal),\n currencyNotI18n = percentageCalc(\n data.currency_not_i18n,\n currencyTotal\n ),\n percentageNotI18n = percentageCalc(\n data.percentage_not_i18n,\n currencyTotal\n ),\n options = {\n series: [\n {\n name: 'Internationalized',\n data: [numberI18n, currencyI18n, percentageI18n]\n },\n {\n name: 'Not Internationalized',\n data: [numberNotI18n, currencyNotI18n, percentageNotI18n]\n }\n ],\n chart: {\n type: 'bar',\n height: 250,\n width: 550,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n dataLabels: {\n enabled: true,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n stroke: {\n show: true,\n width: 1,\n colors: ['#fff']\n },\n tooltip: {\n theme: 'dark',\n shared: true,\n intersect: false,\n x: {\n formatter: (type) => \`\${type} instances:\`\n },\n y: {\n formatter: (v, { seriesIndex, dataPointIndex }) => {\n const getI18nCount = (type) => {\n switch (type) {\n case 'number':\n return data.number_i18n;\n case 'currency':\n return data.currency_i18n;\n case 'percentage':\n return data.percentage_i18n;\n }\n },\n getNonI18nCount = (type) => {\n switch (type) {\n case 'number':\n return data.number_not_i18n;\n case 'currency':\n return data.currency_not_i18n;\n case 'percentage':\n return data.percentage_not_i18n;\n }\n },\n getCount = (type) => {\n return seriesIndex === 0\n ? getI18nCount(type)\n : getNonI18nCount(type);\n };\n\n switch (dataPointIndex) {\n case 0:\n return getCount('number');\n case 1:\n return getCount('currency');\n case 2:\n return getCount('percentage');\n }\n }\n }\n },\n xaxis: {\n categories: types\n },\n yaxis: {\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n colors: ['#31E994', '#ff8173'],\n legend: {\n offsetX: -13,\n position: 'right',\n formatter: (seriesName) => {\n if (seriesName === 'Internationalized') {\n return \`\${seriesName} (\${i18nPercentage}%)\`;\n }\n return \`\${seriesName} (\${nonI18nPercentage}%)\`;\n }\n },\n plotOptions: {\n bar: {\n borderRadius: 3,\n columnWidth: '85%'\n }\n }\n };\n\n (numberChart = new ApexCharts(\n document.querySelector('.number-quality .graph'),\n options\n )).render();\n }\n\n function renderDatetimeChart() {\n const overallStats = DataStore.getOverallStats();\n const types = ['Date', 'Time', 'DateTime'],\n data = {\n date_i18n: overallStats.localizedDate,\n time_i18n: overallStats.localizedTime,\n date_time_i18n: overallStats.localizedDateTime,\n date_not_i18n: overallStats.notLocalizedDate,\n time_not_i18n: overallStats.notLocalizedTime,\n date_time_not_i18n: overallStats.notLocalizedDateTime\n },\n total = Object.values(data).reduce((acc, curr) => acc + curr, 0),\n percentageCalc = (v, t) => {\n const val = ((v / t) * 100).toFixed(0);\n return isNaN(val) || !isFinite(val) ? 0 : val;\n },\n i18nPercentage = percentageCalc(\n data.date_i18n + data.time_i18n + data.date_time_i18n,\n total\n ),\n nonI18nPercentage = percentageCalc(\n data.date_not_i18n + data.time_not_i18n + data.date_time_not_i18n,\n total\n ),\n dateTotal = data.date_i18n + data.date_not_i18n,\n timeTotal = data.time_i18n + data.time_not_i18n,\n dateTimeTotal = data.date_time_i18n + data.date_time_not_i18n,\n dateI18n = percentageCalc(data.date_i18n, dateTotal),\n timeI18n = percentageCalc(data.time_i18n, timeTotal),\n dateTimeI18n = percentageCalc(data.date_time_i18n, dateTimeTotal),\n dateNotI18n = percentageCalc(data.date_not_i18n, dateTotal),\n timeNotI18n = percentageCalc(data.time_not_i18n, timeTotal),\n dateTimeNotI18n = percentageCalc(\n data.date_time_not_i18n,\n dateTimeTotal\n ),\n options = {\n series: [\n {\n name: 'Internationalized',\n data: [dateI18n, timeI18n, dateTimeI18n]\n },\n {\n name: 'Not Internationalized',\n data: [dateNotI18n, timeNotI18n, dateTimeNotI18n]\n }\n ],\n chart: {\n type: 'bar',\n height: 250,\n width: 550,\n toolbar: {\n show: false\n },\n animations: {\n enabled: true,\n easing: 'easeinout',\n speed: 800,\n animateGradually: {\n enabled: true,\n delay: 150\n },\n dynamicAnimation: {\n enabled: true,\n speed: 350\n }\n }\n },\n dataLabels: {\n enabled: true,\n dropShadow: { enabled: false },\n style: {\n colors: ['#080404b0']\n },\n formatter: (val) => {\n return \`\${val}%\`;\n }\n },\n stroke: {\n show: true,\n width: 1,\n colors: ['#fff']\n },\n tooltip: {\n theme: 'dark',\n shared: true,\n intersect: false,\n x: {\n formatter: (type) => \`\${type} instances:\`\n },\n y: {\n formatter: (v, { seriesIndex, dataPointIndex }) => {\n const getI18nCount = (type) => {\n switch (type) {\n case 'date':\n return data.date_i18n;\n case 'time':\n return data.time_i18n;\n case 'date_time':\n return data.date_time_i18n;\n }\n },\n getNonI18nCount = (type) => {\n switch (type) {\n case 'date':\n return data.date_not_i18n;\n case 'time':\n return data.time_not_i18n;\n case 'date_time':\n return data.date_time_not_i18n;\n }\n },\n getCount = (type) => {\n return seriesIndex === 0\n ? getI18nCount(type)\n : getNonI18nCount(type);\n };\n\n switch (dataPointIndex) {\n case 0:\n return getCount('date');\n case 1:\n return getCount('time');\n case 2:\n return getCount('date_time');\n }\n }\n }\n },\n xaxis: {\n categories: types\n },\n yaxis: {\n min: 0,\n max: 100,\n labels: {\n formatter: (v) => \`\${v}%\`\n }\n },\n colors: ['#31E994', '#ff8173'],\n legend: {\n offsetX: 4,\n position: 'right',\n formatter: (seriesName) => {\n if (seriesName === 'Internationalized') {\n return \`\${seriesName} (\${i18nPercentage}%)\`;\n }\n return \`\${seriesName} (\${nonI18nPercentage}%)\`;\n }\n },\n plotOptions: {\n bar: {\n borderRadius: 3,\n columnWidth: '85%'\n }\n }\n };\n\n (dateTimeChart = new ApexCharts(\n document.querySelector('.datetime-quality .graph'),\n options\n )).render();\n }\n }\n });\n })();\n // insights content\n (() => {\n if (!DataStore.ranSuccessfully()) {\n document.getElementById('overview').innerHTML =\n 'Unable to prepare zQA automation report for the project.';\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n renderCards();\n\n // helper methods to generate cards and its sections\n function createStatSection(locale, data) {\n const holder = document.createElement('div');\n const table = document.createElement('table');\n const tbody = document.createElement('tbody');\n const name = document.createElement('div');\n const addRow = (name, value) => {\n const row = document.createElement('tr');\n\n const col1 = document.createElement('td');\n col1.classList.add('name');\n col1.textContent = name;\n\n const col2 = document.createElement('td');\n col2.classList.add('value');\n col2.textContent = value;\n\n row.appendChild(col1);\n row.appendChild(col2);\n return row;\n };\n\n if (DataStore.isSanityRun()) {\n tbody.appendChild(addRow('Pages viewed', data.reviewed));\n }\n else {\n const tableData = {\n 'Internationlized Strings':\n data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings,\n 'Not Internationlized Strings': data.notLocalizedStrings,\n 'Internationlized Numbers': data.localizedNumber,\n 'Not Internationlized Numbers': data.notLocalizedNumber,\n 'Internationlized Currency': data.localizedCurrency,\n 'Not Internationlized Currency': data.notLocalizedCurrency,\n 'Internationlized Percentage': data.localizedPercentage,\n 'Not Internationlized Percentage': data.notLocalizedPercentage,\n 'Internationlized Date': data.localizedDate,\n 'Not Internationlized Date': data.notLocalizedDate,\n 'Internationlized Time': data.localizedTime,\n 'Not Internationlized Time': data.notLocalizedTime,\n 'Internationlized DateTime': data.localizedDate,\n 'Not Internationlized DateTime': data.notLocalizedDateTime,\n 'Encoding Issue Strings': data.encoding,\n 'DNT Strings': data.dnt,\n 'Ignored Strings': data.ignored,\n 'Untranslated Strings': data.untranslatedStrings,\n 'Auto Ignored Strings': data.autoIgnored,\n 'User Input Strings': data.userInput,\n 'Total Reviewed': data.reviewed\n };\n\n for (const rowData of Object.entries(tableData)) {\n if (!DataStore.isSanityRun() || !DataStore.isDefaultLocale(locale) || rowData[0] !== 'Untranslated Strings') {\n tbody.appendChild(addRow(rowData[0], rowData[1]));\n }\n }\n }\n\n holder.classList.add('stat');\n name.classList.add('name');\n name.textContent = 'Summary';\n holder.append(name);\n\n table.appendChild(tbody);\n holder.appendChild(table);\n return holder;\n }\n\n function createStatusBarSection(locale, data) {\n const holder = document.createElement('div');\n holder.classList.add('status-bar');\n\n const generateBarEl = (name, total, current) => {\n const getColorClass = (value) => {\n if (value <= 25) {\n return 'red';\n } else if (value <= 50) {\n return 'yellow';\n } else if (value <= 75) {\n return 'blue';\n } else if (value <= 100) {\n return 'green';\n }\n };\n current = (current / total) * 100;\n current = isNaN(current) || !isFinite(current) ? 0 : current;\n const holder = document.createElement('div');\n holder.classList.add('bar-container');\n\n const nameHolder = document.createElement('div');\n nameHolder.classList.add('name');\n nameHolder.textContent = name;\n\n const barHolder = document.createElement('div');\n barHolder.classList.add('bar-holder');\n\n const bar = document.createElement('div');\n bar.classList.add('bar');\n bar.classList.add(\`--\${getColorClass(current)}\`);\n bar.style.width = \`\${current}%\`;\n bar.textContent = \`\${current.toFixed(0)}%\`;\n\n barHolder.appendChild(bar);\n holder.appendChild(nameHolder);\n holder.appendChild(barHolder);\n\n return holder;\n };\n\n const showFull = DataStore.isSanityRun() || DataStore.isDefaultLocale(locale),\n totalI18nCount = showFull\n ? 100\n : data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings +\n data.notLocalizedStrings,\n i18nStrings = showFull\n ? 100\n : data.localizedStrings +\n data.untranslatedStrings +\n data.partialStrings,\n totalLocCount = showFull\n ? 100\n : totalI18nCount -\n data.untranslatedStrings -\n data.notLocalizedStrings,\n totalNumberCount = showFull\n ? 100\n : data.localizedNumber +\n data.localizedCurrency +\n data.localizedPercentage +\n data.notLocalizedPercentage +\n data.notLocalizedCurrency +\n data.notLocalizedNumber,\n i18nNumbers = showFull\n ? 100\n : totalNumberCount -\n data.notLocalizedPercentage -\n data.notLocalizedCurrency -\n data.notLocalizedNumber,\n totalI18nDateTimeCount = showFull\n ? 100\n : data.localizedDate +\n data.localizedDateTime +\n data.localizedTime +\n data.notLocalizedDate +\n data.notLocalizedDateTime +\n data.notLocalizedTime,\n i18nDateTime = showFull\n ? 100\n : totalI18nDateTimeCount -\n data.notLocalizedDate -\n data.notLocalizedDateTime -\n data.notLocalizedTime;\n\n holder.appendChild(\n generateBarEl('String I18n level', totalI18nCount, i18nStrings)\n );\n\n holder.appendChild(\n generateBarEl('String L10n level', totalI18nCount, totalLocCount)\n );\n\n holder.appendChild(\n generateBarEl('Number I18n level', totalNumberCount, i18nNumbers)\n );\n\n holder.appendChild(\n generateBarEl(\n 'DateTime I18n level',\n totalI18nDateTimeCount,\n i18nDateTime\n )\n );\n return holder;\n }\n\n function generateCardContents(locale, data) {\n const cardContent = document.createElement('div');\n cardContent.classList.add('card-content');\n\n cardContent.appendChild(createStatusBarSection(locale, data));\n cardContent.appendChild(createStatSection(locale, data));\n return cardContent;\n }\n\n function createCardEl(locale, data) {\n const card = document.createElement('div');\n const title = document.createElement('div');\n card.classList.add('card');\n card.dataset.locale = locale;\n\n if(!window.isStandaloneReport) {\n card.classList.add('--reduced-zoom');\n }\n\n title.classList.add('title');\n title.textContent = \`\${locale} details\`;\n\n // fire menu selection event on clicking the card\n card.addEventListener('click', () => {\n const previousSelectedSubMenu = document.querySelector(\n '.sub-menu-item.selected'\n );\n if (previousSelectedSubMenu) {\n window.previousSelectedLocale =\n previousSelectedSubMenu.dataset.locale;\n }\n\n document\n .querySelector(\`.sub-menu-item[data-locale="\${locale}"]\`)\n .click();\n });\n\n card.appendChild(title);\n card.appendChild(generateCardContents(locale, data));\n return card;\n }\n\n function renderCards() {\n // read from global variable appData;\n const localeInfo = DataStore.getLocalesData();\n\n if (Object.keys(localeInfo).length) {\n const cardHolder = document.querySelector(\n '#insights .cards .holder'\n );\n for (const locale in localeInfo) {\n const cardEl = createCardEl(\n locale,\n DataStore.getStatByLocale(locale)\n );\n cardHolder.appendChild(cardEl);\n }\n }\n }\n });\n })();\n // locale grid\n (() => {\n if (!DataStore.ranSuccessfully()) {\n return;\n }\n\n document.addEventListener('DOMContentLoaded', function () {\n let table;\n document.querySelectorAll('.sub-menu-item').forEach((menu) => {\n menu.addEventListener('click', (e) => {\n render(e.currentTarget.dataset.locale);\n });\n });\n\n const updateTitle = (locale) => {\n document.querySelector(\n '#locale .title'\n ).textContent = \`\${locale} \${DataStore.isSanityRun() ? 'Sanity Result' : 'I18n Coverage Details'}\`;\n };\n\n const buildGrid = (locale) => {\n const data = DataStore.getDataByLocale(locale) || {};\n $.fn.dataTable.ext.errMode = 'none';\n const dataSet = [];\n if(DataStore.isSanityRun()) {\n Object.entries(data).map((entry) =>\n entry[1].forEach(\n (v) =>\n dataSet.push([v.description, v.imagePath])\n )\n );\n }\n else {\n Object.entries(data).map((entry) =>\n entry[1].forEach(\n (v) =>\n (v.type !== 'REVIEWED') &&\n dataSet.push([entry[0], v.type, v.description, v.imagePath])\n )\n );\n }\n const containerHeight = document\n .querySelector('#locale')\n .getBoundingClientRect().height;\n const scrollHeight = (75 / 100) * containerHeight;\n table = new DataTable('#locale .coverage-info table', {\n language: {\n lengthMenu: "Show _MENU_ entries per page"\n },\n columns: DataStore.isSanityRun() ?\n [\n { title: 'Page' },\n { title: 'Screenshot' },\n ] : [\n { title: 'String' },\n { title: 'Status' },\n { title: 'Page' },\n { title: 'Screenshot' }\n ],\n columnDefs: DataStore.isSanityRun() ?\n [\n {\n width: '80%',\n targets: 0\n },\n {\n width: '20%',\n targets: 1,\n orderable: false,\n searchable: false,\n data: null,\n render: function (data) {\n return \`<a href="#" class="view-screenshot-btn" onclick="openImageViewer('\${data[1]}')"><img title="View screenshot for '\${data[0]}'" class="view-screenshot-image"></img></a\`;\n },\n className: 'dt-head-center dt-body-center'\n }\n ] :\n [\n {\n width: '45%',\n targets: 0\n },\n {\n width: '22.5%',\n targets: 1\n },\n {\n width: '22.5%',\n targets: 2\n },\n {\n width: '10%',\n targets: 3,\n orderable: false,\n searchable: false,\n data: null,\n render: function (data) {\n return \`<a href="#" class="view-screenshot-btn" onclick="openImageViewer('\${data[3]}')"><img title="View screenshot for '\${data[2]}'" class="view-screenshot-image"></img></a>\`;\n },\n className: 'dt-head-center dt-body-center'\n }\n ],\n data: dataSet,\n initComplete: function () {\n const tableApi = this.api();\n const updateFilters = () => {\n tableApi.columns().every(function () {\n const column = this;\n const columnIdx = column.index();\n\n if ((DataStore.isSanityRun() && columnIdx === 1) || columnIdx === 3 || columnIdx === 0) {\n return;\n }\n\n const select = $(\`select.filter[data-column="\${columnIdx}"]\`); \n const selectedValue = select.val();\n\n select.empty().append('<option value="">Filter By</option>');\n\n const uniqueValues = new Set();\n tableApi.column(columnIdx, { search: 'applied' }).data().each((d) => uniqueValues.add(d));\n\n uniqueValues.forEach((value) => {\n select.append(\`<option value="\${value}">\${value}</option>\`);\n });\n\n if (selectedValue && uniqueValues.has(selectedValue)) {\n select.val(selectedValue);\n }\n });\n };\n\n tableApi.columns().every(function () {\n const column = this;\n const columnIdx = column.index();\n\n if ((DataStore.isSanityRun() && columnIdx === 1) || columnIdx === 3 || columnIdx === 0) {\n return;\n }\n\n const header = $(column.header());\n let wrapper = header.find('.header-wrapper');\n\n if (!wrapper.length) {\n wrapper = $('<div class="header-wrapper"></div>');\n header.contents().appendTo(wrapper);\n header.append(wrapper);\n }\n\n let select = wrapper.find('select.filter');\n if (!select.length) {\n select = $(\`<select class="filter dt-input" data-column="\${columnIdx}"><option value="">Filter By</option></select>\`)\n .appendTo(wrapper)\n .on('change', function () {\n const val = $.fn.dataTable.util.escapeRegex($(this).val());\n column.search(val ? \`^\${val}$\` : '', true, false).draw();\n updateFilters();\n });\n\n column.data().unique().sort().each((d) => {\n select.append(\`<option value="\${d}">\${d}</option>\`);\n });\n }\n });\n\n $('select.filter').on('click', function (event) {\n event.stopPropagation();\n });\n\n tableApi.on('draw', updateFilters);\n \n const firstParent = document.querySelector('.dt-layout-row');\n if (firstParent) {\n const startCell = firstParent.querySelector('.dt-layout-cell.dt-start');\n const endCell = firstParent.querySelector('.dt-layout-cell.dt-end');\n if (startCell && endCell) { \n let middleCell = firstParent.querySelector('.dt-layout-cell.dt-middle');\n if (!middleCell) {\n middleCell = document.createElement('div');\n middleCell.classList.add('dt-layout-cell', 'dt-middle');\n startCell.insertAdjacentElement('afterend', middleCell);\n }\n const resetButton = document.createElement('button');\n resetButton.textContent = 'Reset All';\n resetButton.classList.add('reset-filters-btn'); \n resetButton.setAttribute('title', 'Resets all applied filters and clears search results');\n\n resetButton.addEventListener('click', function () {\n $('select.filter').val('');\n const tableApi = $('.dataTable').DataTable();\n tableApi.search('').columns().search('').draw();\n });\n middleCell.appendChild(resetButton);\n }\n }\n },\n sScrollY: scrollHeight,\n pageLength: 25,\n lengthMenu: [10, 25, 50, 100]\n });\n };\n\n const render = (locale) => {\n if (table) {\n table.destroy();\n }\n updateTitle(locale);\n buildGrid(locale);\n };\n });\n })();\n\n // log content\n (() => {\n document.addEventListener('DOMContentLoaded', function () {\n const pre = document.querySelector('#log .log-content pre');\n pre.textContent = 'Loading...';\n document\n .querySelector('li[data-page="log"]')\n ?.addEventListener('click', (e) => {\n pre.textContent = window.logData || (DataStore.hasRuntimeError() ? 'We are facing internal issue. Please try again after sometime.' : 'No log data found!');\n });\n });\n })();`,n=[],a=Oe(Qe.getReportPath(),"logs","log"),r=Ct.isSanityRun()||Ct.isContextRun(),i=[Ct.getQALocale(),...Ct.getOptions()?.supportedLocales||[]].filter(Boolean).map((e=>Re(e))),s=Ae(Qe.getReportPath()).filter((e=>i.includes(e))),o=1===s.length&&s[0]===Re(Ct.getDefaultLocale());let l="";if(s.forEach((t=>(r||t!==e.defaultLocale||t===e.defaultLocale&&o)&&ze(Oe(Qe.getReportPath(),t,be))&&n.push(Oe(t,be).replace(/\\/g,"/")))),ze(a)){const t=[e.appName,...Ct.getL10nReportsPath().split(/[\\/]+/g),Ct.getMode()];t.push(Qe.getReportTimeStamp()),l=Pe(a).replace(new RegExp(Qe.getReportPath().replace(/\\/g,"\\\\"),"g"),Oe(...t)).replace(/\\/g,"\\\\").replace(/`/g,"\\`")}return function({appName:e,reportTime:t,script:n,style:a,body:r,dataFilePaths:i=[],logData:s}){return`<!DOCTYPE html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <meta name="charset" content="charset='UTF-8'" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <meta name="company" content="Zeesoft" />\n <meta name="product" content="zAutomate.AI" />\n <meta name="author" content="Jitendra Maheshwari" />\n <meta name="email" content="jm@zeesoft.com" />\n <meta\n name="description"\n content="zAutomate.AI report"\n />\n <title>${e} - zAutomate.AI report | ${t}</title>\n \x3c!-- 3rd party dependencies --\x3e\n <link\n rel="stylesheet"\n href="https://cdn.datatables.net/2.0.3/css/dataTables.dataTables.min.css"\n />\n ${i.map((e=>`<script\n src="${e}"\n type="text/javascript">\n <\/script>`)).join("")}\n ${s?`<script type="text/javascript" async/>window.logData = \`${s}\`;<\/script>`:""}\n <script\n src="https://code.jquery.com/jquery-3.7.1.slim.min.js"\n integrity="sha256-kmHvs0B+OpCW5GVHUNjv9rOmY0IvSIRcf7zGUDTDQM8="\n crossorigin="anonymous"\n defer\n ><\/script>\n <script src="https://cdn.jsdelivr.net/npm/apexcharts" defer><\/script>\n <script\n src="https://cdn.datatables.net/2.0.3/js/dataTables.min.js"\n defer\n ><\/script>\n <style>\n ${a}\n </style>\n </head>\n <body>\n ${r}\n </body>\n <script type="text/javascript" defer>\n ${n}\n <\/script>\n</html>\n`}({appName:e.appName,reportTime:e.reportTime,body:'<header><div class="logo">zAutomate.AI</div><div class="report-timestamp"><span class="info"></span></div></header>\n <main>\n <div class="sidebar">\n <ul class="menu" id="menu">\n <li class="menu-item" data-page="overview">\n <a href="#">Overview</a>\n </li>\n <li class="menu-item" data-page="log">\n <a href="#">View Log</a>\n </li>\n <li class="menu-item has-submenu" data-page="insights">\n <a href="#" data-page="insights">Insights</a>\n <ul class="submenu"></ul>\n </li>\n </ul>\n <div class="footer"></div>\n </div>\n <div class="content-wrapper">\n <div class="container">\n <div id="overview" class="content">\n <div class="common-info card">\n <span class="title"></span>\n <p class="info"></p>\n <div class="section"></div>\n </div>\n <div class="overall-quality card">\n <span class="title">Overall I18n Coverage</span>\n <div class="section">\n <div class="block">\n <article>\n <p class="desc">\n zI18n-Verify report provides a comprehensive information\n about the I18n and L10n quailty of your application.\n </p>\n <ul class="items">\n <li>\n <h4>Reviewed:</h4>\n <p class="detail">\n Total strings which are captured and processed by\n zI18n-Verify.\n </p>\n </li>\n <li>\n <h4>Internationlized:</h4>\n <p class="detail">\n Different types of value like string, number, date-time\n which are properly Internationalized.\n </p>\n </li>\n <li>\n <h4>Not Internationalized:</h4>\n <p class="detail">\n Values which are not Internationalized. Also includes\n Partially Localized strings and Encoding Issues.\n </p>\n </li>\n <li>\n <h4>Skipped:</h4>\n <p class="detail">\n Values which are skipped from processing because they\n are listed in DNT / Ignored lists or contains Auto\n Ignored pattern / User input pattern.\n </p>\n </li>\n </ul>\n </article>\n </div>\n <div class="block">\n <div class="graph"></div>\n </div>\n </div>\n </div>\n <div class="string-quality card">\n <span class="title">String Coverage</span>\n <div class="graph"></div>\n </div>\n <div class="skipped-items card">\n <span class="title">Skipped Items</span>\n <div class="graph"></div>\n </div>\n <div class="number-quality card">\n <span class="title">Number Coverage</span>\n <div class="graph"></div>\n </div>\n <div class="datetime-quality card">\n <span class="title">DateTime Coverage</span>\n <div class="graph"></div>\n </div>\n </div>\n <div id="page" class="content">\n <span class="title">Pages</span>\n <div class="section">\n <div class="desc"></div>\n </div>\n </div>\n <div id="log" class="content">\n <span class="title">Runtime log</span>\n <div class="log-content"><pre></pre></div>\n </div>\n <div id="insights" class="content">\n <div class="cards">\n <span class="title">Locale Quality</span>\n <div class="holder"></div>\n </div>\n </div>\n <div id="locale" class="content">\n <span class="title">Locale Quality</span>\n <div class="coverage-info">\n <table class="row-border hover order-column stripe"></table>\n </div>\n <div id="imageViewer" class="image-viewer">\n <span class="close-button" onclick="closeImageViewer()" title="close"\n >×</span\n >\n <div class="image-container">\n <img id="imageView" src="" alt="Image" />\n </div>\n </div>\n </div>\n </div>\n </div>\n </main>',script:t,style:"html {\n width: 100vw;\n height: 100vh;\n }\n\n * {\n user-select: none;\n box-sizing: border-box;\n scrollbar-width: thin;\n scroll-behavior: smooth;\n }\n\n body {\n width: 100%;\n height: 100%;\n margin: 0;\n display: flex;\n flex-direction: column;\n font-family: Arial, sans-serif;\n font-family: 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased; /* Webkit-based browsers */\n -moz-osx-font-smoothing: grayscale; /* Firefox on OS X */\n font-smooth: always; /* Non-standard, some browsers */\n }\n\n header {\n background-color: #333;\n height: 8%;\n display: flex;\n }\n\n main {\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 92%\n }\n\n .sidebar {\n width: 16%;\n height: 100%;\n background-color: #333;\n color: #fff;\n padding: 20px;\n box-sizing: border-box;\n float: left;\n }\n\n .logo {\n flex-grow: 1;\n align-content: center;\n margin-left: 2%;\n color: #14cf96;\n font-weight: bold;\n font-size: 1.4em;\n }\n\n .report-timestamp {\n flex-grow: 1;\n height: 100%;\n align-content: center;\n margin-right: 2%;\n }\n\n .report-timestamp .info {\n float: right;\n font-size: 0.9em;\n color: #90a3a9;\n }\n\n .menu {\n list-style-type: none;\n padding: 0;\n margin: 0;\n }\n\n .menu-item {\n margin-bottom: 10px;\n }\n\n .menu-item a {\n color: #fff;\n text-decoration: none;\n display: block;\n padding: 10px;\n border-radius: 5px;\n font-size: 1.05rem;\n }\n\n .menu-item a:hover {\n background-color: #555;\n }\n\n .submenu {\n display: none;\n list-style-type: none;\n padding-left: 20px;\n }\n\n .menu-item.has-submenu > a::after {\n content: '⯆';\n float: right;\n margin-left: 5px;\n }\n\n .menu-item.has-submenu.selected > a::after {\n content: '⯅';\n float: right;\n margin-left: 5px;\n }\n\n .menu-item.selected > a {\n background-color: #555;\n }\n\n .submenu .sub-menu-item {\n margin: 3px auto;\n }\n\n .submenu .sub-menu-item a:hover {\n background-color: #777;\n }\n\n .submenu .sub-menu-item.selected a {\n background-color: #555 !important;\n }\n\n .submenu.active {\n display: block !important;\n }\n\n .view-screenshot-image {\n width: 100px;\n height: 30px;\n content: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgZmlsbD0iIzAwMDAwMCIgaGVpZ2h0PSI4MDBweCIgd2lkdGg9IjgwMHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIA0KCSB2aWV3Qm94PSIwIDAgNDkwIDQ5MCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBkPSJNMjQ1LjIwNCwxMDEuMjY3QzY3LjE3MSwxMDEuMjY3LDAsMjQ1LDAsMjQ1czY2Ljc2MywxNDMuNzMzLDI0NC43OTYsMTQzLjczM0M0MjIuODI5LDM4OC43MzMsNDkwLDI0NSw0OTAsMjQ1DQoJCVM0MjMuMjM4LDEwMS4yNjcsMjQ1LjIwNCwxMDEuMjY3eiBNMjQ0Ljc5NiwzNzMuNDIxYy0xNDYuMzkzLDAtMjEyLjM2Ny0xMDEuMDUzLTIyNy41MDYtMTI4LjMzMw0KCQljMTUuMjQ5LTI2Ljc5Niw4Mi43MzMtMTI4LjUwOCwyMjcuOTE0LTEyOC41MDhjMTQ2LjUxNCwwLDIxMi40NzUsMTAxLjIyLDIyNy41NDMsMTI4LjQNCgkJQzQ1Ny41NzgsMjcyLjIwMywzOTEuMzEzLDM3My40MjEsMjQ0Ljc5NiwzNzMuNDIxeiIvPg0KCTxwYXRoIGQ9Ik0yNDUuMDAzLDE0Ni4xMjRjLTU0LjUyMSwwLTk4Ljg4MSw0NC4zNTItOTguODgxLDk4Ljg3M2MwLDU0LjUyMSw0NC4zNiw5OC44ODEsOTguODgxLDk4Ljg4MXM5OC44NzMtNDQuMzYsOTguODczLTk4Ljg4MQ0KCQlDMzQzLjg3NiwxOTAuNDc3LDI5OS41MjQsMTQ2LjEyNCwyNDUuMDAzLDE0Ni4xMjR6IE0yNDUuMDAzLDMyOC41NjZjLTQ2LjA4LDAtODMuNTY4LTM3LjQ4OS04My41NjgtODMuNTY4DQoJCWMwLTQ2LjA3MiwzNy40ODktODMuNTYxLDgzLjU2OC04My41NjFjNDYuMDcyLDAsODMuNTYxLDM3LjQ4OSw4My41NjEsODMuNTYxQzMyOC41NjQsMjkxLjA3NywyOTEuMDc1LDMyOC41NjYsMjQ1LjAwMywzMjguNTY2eiIvPg0KCTxwYXRoIGQ9Ik0yNDUuMDAzLDE3NS41ODNjLTM4LjI4MSwwLTY5LjQyMiwzMS4xNDEtNjkuNDIyLDY5LjQxNWMwLDM4LjI3NCwzMS4xNDEsNjkuNDE1LDY5LjQyMiw2OS40MTUNCgkJYzM4LjI3NCwwLDY5LjQxNS0zMS4xNDEsNjkuNDE1LTY5LjQxNUMzMTQuNDE3LDIwNi43MjQsMjgzLjI3NywxNzUuNTgzLDI0NS4wMDMsMTc1LjU4M3ogTTI0NS4wMDMsMjk5LjENCgkJYy0yOS44MzMsMC01NC4xMS0yNC4yNy01NC4xMS01NC4xMDJjMC0yOS44MzIsMjQuMjc3LTU0LjEwMiw1NC4xMS01NC4xMDJzNTQuMTAyLDI0LjI3LDU0LjEwMiw1NC4xMDINCgkJQzI5OS4xMDUsMjc0LjgzLDI3NC44MzUsMjk5LjEsMjQ1LjAwMywyOTkuMXoiLz4NCgk8cGF0aCBkPSJNMjQ1LjAwMywyMDYuNjc5Yy0yMS4xMywwLTM4LjMyNiwxNy4xODktMzguMzI2LDM4LjMxOWMwLDIxLjEyOSwxNy4xOTcsMzguMzI2LDM4LjMyNiwzOC4zMjZzMzguMzE5LTE3LjE5NywzOC4zMTktMzguMzI2DQoJCUMyODMuMzIxLDIyMy44NjgsMjY2LjEzMiwyMDYuNjc5LDI0NS4wMDMsMjA2LjY3OXogTTI0NS4wMDMsMjY4LjAxMWMtMTIuNjg4LDAtMjMuMDE0LTEwLjMyNi0yMy4wMTQtMjMuMDE0DQoJCWMwLTEyLjY4OCwxMC4zMjUtMjMuMDA2LDIzLjAxNC0yMy4wMDZjMTIuNjg4LDAsMjMuMDA2LDEwLjMxOCwyMy4wMDYsMjMuMDA2QzI2OC4wMDksMjU3LjY4NiwyNTcuNjkxLDI2OC4wMTEsMjQ1LjAwMywyNjguMDExeiIvPg0KPC9nPg0KPC9zdmc+);\n }\n\n .content-wrapper {\n padding: 1%;\n float: left;\n width: 84%;\n background-color: #f4f4f4;\n overflow: hidden;\n height: 100%;\n }\n\n .content {\n display: none !important;\n height: 100%;\n }\n\n .content .title {\n font-size: 1rem;\n color: #080404e2;\n font-weight: 600;\n }\n\n .content .title::after {\n content: ':';\n }\n\n .content.active {\n display: flex !important;\n flex-direction: column;\n gap: 0.9rem;\n flex-wrap: wrap;\n transition: all 2s ease-in-out;\n }\n\n .container {\n height: 99%;\n overflow: hidden;\n overflow-y: auto;\n }\n\n .sidebar .footer {\n bottom: 1.5%;\n position: absolute;\n font-size: 0.75rem;\n color: #14cf96;\n font-family: system-ui;\n width: inherit;\n }\n\n #overview {\n flex-direction: row;\n justify-content: space-between;\n }\n\n #overview .card {\n display: flex;\n flex-direction: column;\n width: 25rem;\n border: 1px solid #ddd;\n box-shadow: 0 22px 35px -16px rgba(0, 0, 0, 0.3);\n border-radius: 5px;\n background: white;\n padding: 1rem;\n }\n\n #overview .overall-quality {\n flex-basis: 100%;\n line-height: 14px;\n }\n\n #overview .overall-quality .section {\n display: flex;\n flex-direction: row;\n }\n\n #overview .overall-quality .section .block {\n width: 50%;\n }\n\n #overview .overall-quality .section .block article p.desc {\n font-size: 0.85em;\n }\n\n #overview .overall-quality .section .block article .items li {\n font-size: 0.8em;\n }\n\n #overview .overall-quality .section .block article .items li .detail {\n margin-top: -2%;\n }\n\n #overview .overall-quality .section .block .graph {\n margin-top: 1.5em;\n }\n\n #overview .string-quality,\n #overview .number-quality,\n #overview .datetime-quality,\n #overview .skipped-items {\n flex-basis: 48.5%;\n }\n\n #overview .string-quality .graph,\n #overview .number-quality .graph,\n #overview .datetime-quality .graph,\n #overview .skipped-items .graph {\n align-self: center;\n }\n\n #overview .string-quality .graph {\n margin-left: 4rem;\n }\n\n #overview .number-quality .graph,\n #overview .datetime-quality .graph {\n margin-top: -0.9rem;\n margin-left: 4.5rem;\n }\n\n #overview .common-info {\n width: 100%;\n height: 100%;\n }\n\n #overview .common-info .info {\n font-size: 0.9em;\n margin-top: 0.8%;\n }\n\n #overview .common-info .title {\n font-size: 1.1em;\n }\n\n #overview .common-info .section {\n width: 100%;\n height: 100%;\n overflow: hidden;\n padding-top: 3%;\n }\n\n #overview .common-info .section .page-tiles .title-section .locale-filter-wrapper {\n margin-right: 1%;\n font-size: 0.8em;\n font-weight: 600;\n }\n\n #overview .common-info .section .page-tiles .title-section .locale-filter-wrapper #locale-filter {\n font-size: 0.9em;\n height: 1.7em;\n border-radius: 3px\n }\n\n #overview .common-info .section .page-tiles .title-section {\n display: flex;\n align-items: center;\n }\n\n #overview .common-info .section .page-tiles .title-section .title-wrapper {\n flex: 1;\n }\n\n #overview .common-info .section .page-tiles .title-section .search-field-wrapper {\n flex: 0.7;\n font-size: 0.8em;\n font-weight: 600;\n }\n\n #overview .common-info .section .page-tiles {\n display: flex;\n flex-direction: column;\n gap: 1%;\n width: 100%;\n height: 92%;\n }\n\n #overview .common-info .section .page-tiles .title {\n flex: 1;\n }\n\n #overview .common-info .section .page-tiles .tiles-list-wrapper {\n width: 100%;\n height: 100%;\n border: 0.5px solid #00000008;\n border-radius: 5px;\n padding: 0;\n margin: 0;\n }\n\n #overview .common-info .section .page-tiles .tiles-list {\n width: 100%;\n height: 100%;\n overflow: hidden;\n overflow-y: auto;\n padding: 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 2.2%;\n justify-content: start;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .empty-context {\n background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAA7AAAAOwBeShxvQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALnSURBVFiFxddNiFZlFAfw3zjvSGUfWjRCgdmiTV8IGjlFhBJotQmjdraqqBbZtAjBFoYVBH0tGiQUFCJKDQqpNpW0sJosIvowyEW1s4a+7UOsuS3OubzPvL133hmbefvD5d7nPOd5zsfzP+feS2AEb+MYqnm+jqWtkbTtKvzVB8Od1wmslt5UeAKLzD8W4am0+S7ttPfDeI3T0+bxVmH4hj46UGPhQHryv6FVPO/rs+1boMzAQJ8dqGBBn43+C60G+TW4B4NzZOdvjOFgt8m6MZR40dw3nhe62W3KQH00T2K8d4DTYjXu15DNJgdqjPvv1fEh1uH1k3FgLvAVLm2anC8HlmE5luA7fIZfO3ROzMSBUdkwuuAQHi/GLdyOu3F5h+5xvIlH8F7KbpPk71YFO/Rm9Q+F/nJ8XMx9L96yr+B9EW09N4ahemFTJ1yMtabvA5/jcBofx1IcwWbsF98YNc7GvXgAp6ZjN2OyKQMzRUs78jdwZg/9VZhI/QdpzsAVmmv3MLbm813YLiJfhV9m4PRawYc/cBHdM/Co5rP/XfsMP0nZhmLtQXyA4RyvE5wYLXT25LotTVXwsGD5UJe5LwSpluEyQcb9xfwpWIm3cp/dKSv32o1bcSMnz4E1ue5Ah/xc7cxM5v1ZU494OOXfNmWgiQMVdgrCnZGyzgYzgW0izQP4CQ+ZGuTPeT+r3nQ2HHguda7M8aGOtesFwep+UIlOOFzoXJjyr2n/E5TRnoabRBcsrw2ipomS+1Pw4ZxibW30GdEbPs3x9kLnjpS9LL2rcL3Z49Vcu7WQjYqGU5/5cBq/OseDhc2NA9iEp0W/PiD+E6ZDhecF80fwjsjEtaL8emGbaEJHcAnx8fGY2f2evVZsOJayCdFkmjCYxifFsa1hamlcgBVY2COCSjSbozkewl7BmQovYRc+wo84H9fhPlycgd6ZOnOGBSKtv5k+c1/KyOcL52GLeOcfFbz6RrB9oy6d9R/3ey953ywo7QAAAABJRU5ErkJggg==);\n width: 100%;\n background-repeat: no-repeat;\n height: 100%;\n background-size: 2.5em;\n background-position-x: 50%;\n background-position-y: 43%;\n display: flex;\n justify-content: center;\n align-content: center;\n flex-wrap: wrap;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container {\n width: 23%;\n height: 35%;\n text-align: center;\n display: flex;\n flex-direction: column;\n border: 1px solid #80808030;\n border-radius: 5px;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container:hover {\n transform: scale(1.03);\n box-shadow: 0 22px 35px -16px #00000033;\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .image {\n width: 100%;\n height: 85%;\n object-fit: contain;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .name span {\n width: 75%;\n display: inline-block;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n #overview .common-info .section .page-tiles .tiles-list .tile-container .name {\n width: 100%;\n display: block;\n height: 15%;\n align-content: center;\n background-color: rgb(0 0 0 / 7%);\n border-top: 1px solid #80808030;\n color: #2cb2c1e6;\n -webkit-font-smoothing: antialiased;\n font-variant: all-small-caps;\n font-weight: 600;\n font-size: 0.87em;\n }\n\n #insights .cards {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n }\n\n #insights .cards .holder {\n display: flex;\n flex-direction: row;\n gap: 1.5rem;\n flex-wrap: wrap;\n }\n\n #insights .cards .card {\n display: flex;\n flex-direction: column;\n width: 20rem;\n min-height: 10rem;\n border: 1px solid #ddd;\n box-shadow: 0 22px 35px -16px rgba(0, 0, 0, 0.3);\n border-radius: 5px;\n background: white;\n }\n\n #insights .cards .card:hover {\n transform: scale(1.05);\n box-shadow: 0 22px 35px -16px #07ff7d33;\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n }\n\n #insights .cards .card .title {\n height: 2.5rem;\n font-size: 1rem;\n padding-top: 1rem;\n font-weight: 500;\n background: #0c5ad9d6;\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n padding-left: 1rem;\n color: white;\n }\n\n #insights .cards .card .title::after {\n content: '';\n }\n\n #insights .cards .card .card-content {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n padding: 1rem;\n }\n\n #insights .cards .card .status-bar {\n display: flex;\n flex-direction: column;\n gap: 0.8rem;\n }\n\n #insights .cards .card .status-bar .bar-container .name {\n font-size: 0.78rem;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n #insights .cards .card .status-bar .bar-container .name::after {\n content: ':';\n }\n\n #insights .cards .card .status-bar .bar-container .bar-holder {\n width: 100%;\n height: 10px;\n border: 1px solid #00000038;\n border-radius: 2px;\n }\n\n #insights .cards .card .status-bar .bar-container .bar-holder .bar {\n height: 100%;\n font-size: 0.63rem;\n text-align: right;\n color: rgb(0, 0, 0);\n }\n\n .bar.--green {\n background-color: #1dedaf;\n }\n\n .bar.--red {\n background-color: #fe3e3e;\n }\n\n .bar.--yellow {\n background-color: #faef1f;\n }\n\n .bar.--blue {\n background-color: #77c2ff;\n }\n\n #insights .cards .card .stat {\n display: flex;\n flex-direction: column;\n gap: 0.3rem;\n border: 0.5px solid #00000012;\n border-radius: 6px;\n }\n\n #insights .cards .card .stat table td {\n font-size: 0.78rem;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n #insights .cards .card .stat table td.name {\n width: 70%;\n }\n\n #insights .cards .card .stat table td.value::before {\n content: ': ';\n }\n\n #locale {\n display: flex;\n gap: 0.2em;\n background: white;\n padding: 1em;\n border-radius: 5px;\n flex-wrap: nowrap;\n }\n\n #locale .title {\n content: ':';\n }\n\n #locale .coverage-info {\n width: 100%;\n }\n\n #locale table thead th select.filter {\n margin-left: 1rem;\n max-width: 8rem;\n text-overflow: ellipsis;\n }\n\n #locale .image-viewer {\n display: none;\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.8);\n text-align: center;\n }\n\n #locale .image-container {\n position: relative;\n top: 50%;\n transform: translateY(-50%);\n display: inline-block;\n width: 98vw;\n height: 94vh;\n overflow: hidden;\n overflow-y: auto;\n }\n\n #locale .image-viewer img {\n width: 100%;\n }\n\n #locale .close-button {\n position: absolute;\n top: -0.6rem;\n right: 0.1rem;\n color: #ffffffbf;\n font-size: 2.4rem;\n cursor: pointer;\n z-index: 9999;\n }\n\n #locale .close-button:hover {\n color: white;\n }\n\n #locale table tbody td {\n font-size: 0.9em;\n }\n\n #locale table td .view-screenshot-btn {\n color: blue;\n }\n\n @media (max-width: 768px) {\n .sidebar {\n width: 16rem;\n padding: 10px;\n }\n }\n\n .--hidden {\n display: none !important;\n visibility: hidden !important;\n }\n\n #log {\n display: flex;\n }\n\n #log .title {\n display: block;\n flex: 1;\n }\n\n #log .log-content {\n flex: 99;\n background: white;\n border-radius: 3px;\n overflow: hidden;\n padding: 1em;\n }\n\n #log .log-content pre {\n height: 100%;\n white-space: pre-line;\n word-break: break-all;\n padding: 0;\n margin: 0;\n width: 100%;\n overflow: hidden;\n overflow-y: auto;\n user-select: all;\n }\n\n body .context-image-viewer {\n z-index: 9999;\n width: 100vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n position: fixed;\n top: 0;\n left: 0;\n background: #0000009c;\n }\n\n body .context-image-viewer .close-btn {\n position: absolute;\n right: 1.2%;\n top: 0.65%;\n color: #a7a5a5;\n font-size: 2.8em;\n cursor: pointer;\n z-index: 1;\n }\n\n body .context-image-viewer .close-btn:hover {\n color: #000000a6;\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .loader {\n width: 100vw;\n height: 100vh;\n z-index: 9999;\n top: 0;\n left: 0;\n position: absolute;\n background: #0000009c;\n color: white;\n text-align: center;\n padding-top: 50vh;\n }\n\n body .context-image-viewer .viewer {\n width: 98%;\n height: 96%;\n background-color: white;\n margin: auto;\n position: relative;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn,\n body .context-image-viewer .viewer .zoom-out-btn {\n position: absolute;\n bottom: 10%;\n width: 35px;\n height: 35px;\n border-radius: 50%;\n text-align: center;\n background: #0000001a;\n cursor: pointer;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn {\n left: 51%;\n }\n\n body .context-image-viewer .viewer .zoom-out-btn {\n left: 47%;\n }\n\n body .context-image-viewer .viewer .next-btn,\n body .context-image-viewer .viewer .prev-btn {\n width: 35px;\n height: 35px;\n border-radius: 50%;\n text-align: center;\n background: #0000001a;\n cursor: pointer;\n position: absolute;\n top: 50%;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn:hover,\n body .context-image-viewer .viewer .zoom-out-btn:hover {\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .viewer .zoom-in-btn span,\n body .context-image-viewer .viewer .zoom-out-btn span {\n display: inline-block;\n margin-top: 12%;\n font-size: 1.5em;\n }\n\n body .context-image-viewer .viewer .next-btn:hover,\n body .context-image-viewer .viewer .prev-btn:hover {\n transform: scale(1.04);\n transition: all 0.15s ease-in-out;\n }\n\n body .context-image-viewer .viewer .next-btn span,\n body .context-image-viewer .viewer .prev-btn span {\n display: inline-block;\n margin-top: 15%;\n font-size: 1.5em;\n }\n\n body .context-image-viewer .viewer .prev-btn {\n left: 1%;\n }\n\n body .context-image-viewer .viewer .next-btn {\n right: 1%;\n }\n\n body .context-image-viewer .viewer .wrapper {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n padding: 1.8%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper .page-title {\n font-weight: 600;\n max-width: 75%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper .page-count {\n font-size: 0.7em;\n }\n\n body #overview .section .page-tiles .search-field-wrapper #search-input {\n width: 55%;\n height: 1.5em;\n margin-left: 0.5%;\n white-space: nowrap;\n text-overflow: ellipsis;\n border-radius: 3px;\n border: 0.1px solid;\n }\n\n body #overview .section .page-tiles .search-field-wrapper #operator-filter {\n width: fit-content;\n height: 1.5em;\n border-radius: 3px;\n }\n\n body #overview .section .page-tiles .search-field-wrapper {\n flex: 33;\n display: flex;\n align-items: center;\n gap: 1%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper .switch-wrapper .switch-knob {\n position: absolute;\n width: 40%;\n height: 86%;\n border-radius: 50%;\n background-color: white;\n top: 50%;\n left: 3%;\n transform: translateY(-50%);\n transition: left 0.3s;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper .switch-wrapper {\n position: relative;\n width: 2.5em;\n height: 1.3em;\n border-radius: 2em;\n background-color: gray;\n cursor: pointer;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .smart-compare-wrapper {\n display: flex;\n align-items: center;\n gap: 1%;\n flex: 33;\n justify-content: end;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar .page-title-wrapper {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n flex: 33;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper .locale-name-bar span,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper .locale-name-bar span {\n opacity: 0.6;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper .locale-name-bar,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper .locale-name-bar {\n position: absolute;\n background-color: #8080801f;\n width: 9%;\n height: 3.5%;\n text-align: center;\n margin: auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 0.5em;\n bottom: 2.1rem;\n left: 2rem;\n border-radius: 3em;\n color: #00000099;\n font: status-bar;\n font-weight: 500;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper {\n width: 100%;\n height: 100%;\n overflow: hidden;\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n border: 0.1px solid #00000021;\n border-radius: 5px;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view .source-image-wrapper img,\n body .context-image-viewer .viewer .wrapper .image-section .split-view .target-image-wrapper img {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n transform: translate(0, 0) scale(1);\n transition: transform 0.1s ease-out;\n pointer-events: none;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section .split-view {\n width: 100%;\n height: 100%;\n display: flex;\n gap: 0.5%;\n }\n\n body .context-image-viewer .viewer .wrapper .image-section {\n display: flex;\n flex: 95;\n overflow: hidden;\n width: 100%;\n height: 100%;\n }\n\n body .context-image-viewer .viewer .wrapper .toolbar {\n border: 0.1px solid #00000021;\n border-bottom: 0;\n height: 7%;\n border-radius: 5px;\n padding: 0.5%;\n padding-left: 1%;\n padding-right: 1%;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: stretch;\n flex: 4;\n }\n\n body .dt-layout-row:first-of-type .dt-layout-cell {\n display: table-cell;\n width: 33.33%;\n vertical-align: middle;\n text-align: center;\n padding: 8px;\n }\n \n body .dt-layout-row:first-of-type .dt-layout-cell.dt-middle {\n text-align: right;\n }\n \n body .dt-layout-row:first-of-type .reset-filters-btn {\n background-color: #555;\n color: white;\n border: none;\n padding: 8px 16px;\n font-size: 14px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.3s ease;\n }\n\n .--reduced-zoom {\n zoom: 0.9123;\n }\n\n .--hide-overflow {\n overflow: hidden;\n }\n\n .--disabled {\n color: #00000014 !important;\n background: #00000014 !important;\n cursor: not-allowed !important;\n transform: none !important;\n transition: none !important;\n }\n\n .--disabled:hover {\n color: #00000014 !important;\n background: #00000014 !important;\n transform: none !important;\n transition: none !important;\n }",dataFilePaths:n,logData:l})}write(e,t,n){$e(Oe(e,`${t}.html`),n,"utf-8")}}class je extends _e{#o;#l=!1;static#c="STATUS";static#d="COUNTS";constructor(){super(ye),this.#u()}#u(){this.#o=new m({style:{headerTop:{left:"[94m╔[0m",mid:"[94m╦[0m",right:"[94m╗[0m",other:"[94m═"},headerBottom:{left:"[94m╟[0m",mid:"[94m╬[0m",right:"[94m╢[0m",other:"[94m═"},tableBottom:{left:"[94m╚[0m",mid:"[94m╩[0m",right:"[94m╝[0m",other:"[94m═[0m"},vertical:"[94m║[0m"},columns:[{name:je.#c,alignment:"left"},{name:je.#d,alignment:"center"}]})}buildContent(){if(ce[le]>0){this.#l=!0;for(const e in ce)e!==le?ce.hasOwnProperty(e)&&this.#o.addRows([{STATUS:e,COUNTS:`${ce[e]}`}]):(this.#o.addRows([{STATUS:"[94m"+"═".repeat(28)+"[0m",COUNTS:"[94m"+"═".repeat(8)+"[0m"}]),this.#o.addRows([{STATUS:"OVERALL_REVIEWED",COUNTS:`${ce[e]}`}]))}}export(){this.#l?this.#o.printTable():Ze.info("There are no visbile elements with UI strings")}}class Fe{static#p(e){switch(e){case we:return new Be;case ye:return new je;default:throw new Error("unsupported report type requested")}}static export(e,t,n){const a=Fe.#p(e),r=new ke({appName:Ct.getAppName(),reportTime:(new Date).toLocaleString(),isContext:Ct.isContextRun(),isSanity:Ct.isSanityRun(),defaultLocale:Ct.getDefaultLocale(),reportFolderTimestamp:Qe.getReportTimeStamp()});a.generate(r),a.export(t,n)}}class Ue{static#m;static#g;static init(){Ue.#g=Oe(Qe.getReportPath(),Re(Ct.getLocale())),Ue.#m=Oe(Ue.#g,be),this.#h()}static exists(){return ze(Ue.#m)}static save(e){this.#h();const t=`!window.rawData && (window.rawData={'${Ct.getLocale()}':{}});window.rawData['${Ct.getLocale()}']=${JSON.stringify(e)};`;$e(Ue.#m,t)}static getData(){if(!this.exists())return{};try{return JSON.parse(Pe(Ue.#m).replace(new RegExp(`(\\!window.rawData && \\(window\\.rawData=\\{'${Ct.getLocale()}':\\{\\}\\}\\);window\\.rawData\\['${Ct.getLocale()}'\\]=)|(;$)`,"g"),""))}catch(e){return{}}}static#h(){Me(Ue.#g)}static getCacheDirPath(){return Ue.#g}}class Ve{#f;#v;#w;#y;#b;#E;#L;#S;#x;#D;#I;#C;#T;#z;#N;#M;#P;#A;#O;#$;#R;#k;#_;#B;#j;constructor(e={}){this.#f=e?.localizedStrings||0,this.#v=e?.notLocalizedStrings||0,this.#w=e?.untranslatedStrings||0,this.#y=e?.partialStrings||0,this.#b=e?.localizedDateTime||0,this.#E=e?.localizedDate||0,this.#L=e?.localizedTime||0,this.#S=e?.notLocalizedDateTime||0,this.#x=e?.notLocalizedDate||0,this.#D=e?.notLocalizedTime||0,this.#I=e?.localizedNumber||0,this.#C=e?.notLocalizedNumber||0,this.#T=e?.localizedCurrency||0,this.#z=e?.notLocalizedCurrency||0,this.#N=e?.localizedPercentage||0,this.#M=e?.notLocalizedPercentage||0,this.#P=e?.ignored||0,this.#A=e?.dnt||0,this.#O=e?.autoIgnored||0,this.#$=e?.userInput||0,this.#R=e?.encoding||0,this.#k=e?.truncation||0,this.#_=e?.overlapping||0,this.#B=e?.reviewed||0,this.#j=e?.context||0}getLocalizedStrings(){return this.#f}setLocalizedStrings(e){this.#f=e}getUntranslatedStrings(){return this.#w}setUntranslatedStrings(e){this.#w=e}getNotLocalizedStrings(){return this.#v}setNotLocalizedStrings(e){this.#v=e}getPartialStrings(){return this.#y}setPartialStrings(e){this.#y=e}getLocalizedDateTime(){return this.#b}setLocalizedDateTime(e){this.#b=e}getLocalizedDate(){return this.#E}setLocalizedDate(e){this.#E=e}getLocalizedTime(){return this.#L}setLocalizedTime(e){this.#L=e}getNotLocalizedDateTime(){return this.#S}setNotLocalizedDateTime(e){this.#S=e}getNotLocalizedDate(){return this.#x}setNotLocalizedDate(e){this.#x=e}getNotLocalizedTime(){return this.#D}setNotLocalizedTime(e){this.#D=e}getLocalizedNumber(){return this.#I}setLocalizedNumber(e){this.#I=e}getNotLocalizeNumber(){return this.#C}setNotLocalizeNumber(e){this.#C=e}getCurrency(){return this.#T}setCurrency(e){this.#T=e}getNotLocalizeCurrency(){return this.#z}setNotLocalizeCurrency(e){this.#z=e}getPercentage(){return this.#N}setPercentage(e){this.#N=e}getNotLocalizePercentage(){return this.#M}setNotLocalizePercentage(e){this.#M=e}getIgnored(){return this.#P}setIgnored(e){this.#P=e}getUserInput(){return this.#$}setUserInput(e){this.#$=e}getDnt(){return this.#A}setDnt(e){this.#A=e}getAutoIgnored(){return this.#O}setAutoIgnored(e){this.#O=e}getEncoding(){return this.#R}setEncoding(e){this.#R=e}getTruncation(){return this.#k}setTruncation(e){this.#k=e}getOverlapping(){return this.#_}setOverlapping(e){this.#_=e}getReviewed(){return this.#B}setReviewed(e){this.#B=e}getContext(){return this.#j}setContext(e){this.#j=e}incrementByType(e){switch(e){case B:++this.#f;break;case H:++this.#v;break;case ie:++this.#w;break;case j:++this.#y;break;case se:++this.#O;break;case re:++this.#P;break;case ae:++this.#A;break;case ne:++this.#R;break;case W:++this.#E;break;case G:++this.#L;break;case q:++this.#b;break;case X:++this.#x;break;case J:++this.#D;break;case K:++this.#S;break;case F:++this.#I;break;case Q:++this.#C;break;case U:++this.#T;break;case Z:++this.#z;break;case V:++this.#N;break;case Y:++this.#M;break;case ee:++this.#k;break;case te:++this.#_;break;case le:++this.#B;break;case oe:++this.#$;break;case"context":++this.#j}}toJSON(){return{localizedStrings:this.#f,notLocalizedStrings:this.#v,untranslatedStrings:this.#w,partialStrings:this.#y,localizedDateTime:this.#b,localizedDate:this.#E,localizedTime:this.#L,notLocalizedDateTime:this.#S,notLocalizedDate:this.#x,notLocalizedTime:this.#D,localizedNumber:this.#I,notLocalizedNumber:this.#C,localizedCurrency:this.#T,notLocalizedCurrency:this.#z,localizedPercentage:this.#N,notLocalizedPercentage:this.#M,ignored:this.#P,dnt:this.#A,autoIgnored:this.#O,userInput:this.#$,encoding:this.#R,truncation:this.#k,overlapping:this.#_,reviewed:this.#B,context:this.#j}}}class We{#F;constructor(e={}){this.#F=new Ve(e.stats)}getStats(){return this.#F}setStats(e){this.#F=e}toJSON(){return{stats:this.#F}}}class Ge{#U;#V;#W;constructor(e){this.#U=e?.type||"",this.#V=e?.imagePath||"",this.#W=e?.description||""}getType(){return this.#U}setType(e){this.#U=e}getImagePath(){return this.#V}setImagePath(e){this.#V=e}getDescription(){return this.#W}setDescription(e){this.#W=e}toJSON(){return{type:this.#U,description:this.#W,imagePath:this.#V}}}class qe{#G={};constructor(e){if(e)for(const t in e)e[t].forEach((e=>{this.#G[t]||(this.#G[t]=[]),this.#G[t].push(new Ge(e))}))}getStringData(e){return this.#G[e]||[]}setStringData(e,t){return!this.#G[e]&&(this.#G[e]=[]),this.#G[e].push(new Ge(t)),!0}checkAndAdd(e,t){!this.#G[e]&&(this.#G[e]=[]);const n=this.getStringData(e).some((e=>e.getDescription()!==t.description||e.getImagePath()!==t.imagePath||e.getType()!==e.type));return n&&this.setStringData(e,t),n}hasStringData(e){return e in this.#G}static createNew(e,t){return t=Array.isArray(t)?t:[t],new qe({data:{[e]:t}})}toJSON(){return this.#G}}class He{#q;#H;constructor(e={}){this.#q=new We(e.metaData),this.#H=new qe(e.stringsData)}getMetaData(){return this.#q}setMetaData(e){this.#q=e}getStringEntry(e){return this.#H.getStringData(e)}static addString(e,t,n,a){const r=He.getInstance(),i=r.#Q(e,t,n,a.replace(/\\/g,"/"));return i&&r.save(),i}#Q(e,t,n,a){let r=!1;const i={type:t,description:n,imagePath:a};return r=this.#H.hasStringData(e)?this.#H.checkAndAdd(e,i):this.#H.setStringData(e,i),r&&this.#q.getStats().incrementByType(t),r}static incrementCountByType(e){const t=He.getInstance();t.#q.getStats().incrementByType(e),t.save()}toJSON(){return{metaData:this.#q,stringsData:this.#H}}save(){Ue.save(this)}remove(){Ue.delete()}static getInstance(){return new He(Ue.getData())}}class Qe{static#Z;static#Y;static setup(){Qe.#Y||(Qe.#Y=Qe.#X());const e=c.resolve();Qe.#Z=Oe(e,Ct.getL10nReportsPath(),Ct.getMode(),Qe.#Y)}static getReportTimeStamp(){return Qe.#Y}static getReportPath(){return Qe.#Z}static reviewedReports(e=null,t=null){return Qe.#J(le,e,t)}static getContextPath(){return this.#K()}static[B](e,t){return Qe.#J(B,e,t)}static[H](e,t){return Qe.#J(H,e,t)}static[j](e,t){return Qe.#J(j,e,t)}static[ae](e,t){return Qe.#J(ae,e,t)}static[re](e,t){return Qe.#J(re,e,t)}static[se](e,t){return Qe.#J(se,e,t)}static[ie](e,t){return Qe.#J(ie,e,t)}static[ne](e,t){return Qe.#J(ne,e,t)}static[q](e,t){return Qe.#J(q,e,t)}static[K](e,t){return Qe.#J(K,e,t)}static[W](e,t){return Qe.#J(W,e,t)}static[X](e,t){return Qe.#J(X,e,t)}static[G](e,t){return Qe.#J(G,e,t)}static[J](e,t){return Qe.#J(J,e,t)}static[U](e,t){return Qe.#J(U,e,t)}static[Z](e,t){return Qe.#J(Z,e,t)}static[V](e,t){return Qe.#J(V,e,t)}static[Y](e,t){return Qe.#J(Y,e,t)}static[F](e,t){return Qe.#J(F,e,t)}static[Q](e,t){return Qe.#J(Q,e,t)}static incrementCount(e,t,n,a){ce.hasOwnProperty(e)&&(ce[e]++,He.addString(t,e,a,n.replace(Qe.#Z,"")))}static#X(){const e=new Date;return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}-${String(e.getHours()).padStart(2,"0")}-${String(e.getMinutes()).padStart(2,"0")}`}static#J=(e,t,n)=>{const a=Oe(Qe.#Z,Re(Ct.getLocale()),"screenshots",String(e).toLowerCase());if(Me(a,he),null!=t){const a=Oe(Qe.#Z,Re(Ct.getLocale()),"strings");Me(a,he);let r="";const i=Oe(a,e+".txt");if(Ne(i)){Pe(i).includes(n)||(r+=`\n|<<<<<<<<<<<<<------${n}------\x3e>>>>>>>>>>>>|\n`),r+=t}else r+=`\n|<<<<<<<<<<<<<------${n}------\x3e>>>>>>>>>>>>|\n`,r+=t;((e,t,n="utf-8")=>{if(e&&t)try{o(e,t+"\n",{encoding:n,flag:"a"})}catch(e){Ze.error(e)}})(i,r)}return a};static#K=()=>{const e=Oe(Qe.#Z,Re(Ct.getLocale()),"contexts");return Me(e,he),e};static#ee(e){Fe.export(e,Qe.#Z)}static generateHtml(){Qe.#ee(we)}static generateAsciiTable(){Qe.#ee(ye)}}class Ze{static#te=!1;static#ne;static#ae=[];static info(e){Ze.#re("info",e)}static warn(e){Ze.#re("warn",e)}static error(e,t){Ze.#re("errr",e,t)}static debug(e){Ze.#re("dbug",e)}static#re(e,t,n=""){const a={INFO:"[37m",WARN:"[33m",ERRR:"[31m",DBUG:"[34m"};let r=a[e=e.toUpperCase()]||a.DEFAULT;n=n?` reason: ${n.toString()}`:"";let i=`[${(new Date).toLocaleString()}] [${r}${e}[0m] [${Ct.getLocale()}] - ${t}${n}`;switch(e){case"INFO":console.info(i);break;case"WARN":console.warn(i);break;case"ERRR":console.error(i);break;case"DBUG":console.debug(i);break;default:console.log(i)}i=i.replace(/\x1b\[\d+m/g,""),Ze.#ie(`${i}\n`)}static#ie=t=>{if(Ze.#te)Ze.#ae.push(t),Ze.#ne=setInterval((()=>{Ze.#te||(clearInterval(Ze.#ne),t=Ze.#ae.join("\n"),Ze.#ae=[],t&&Ze.#ie(t))}),10);else{Ze.#te=!0;const n=Oe(Qe.getReportPath(),"logs"),a=Oe(n,"log");Me(n,he),e.appendFileSync(a,t),Ze.#te=!1}}}class Ye extends Ce{constructor(e){super(e)}async getElement(e,t=!1){return await this.$driver.element.find({selector:e,locateStrategy:e.startsWith("/")?"xpath":"css selector",suppressNotFoundErrors:t})}async getElements(e,t=!1){return await this.$driver.element.findAll({selector:e,locateStrategy:e.startsWith("/")?"xpath":"css selector",suppressNotFoundErrors:t})}async getText(e){return await e.getText()}async getTitle(e){return await e.getAttribute("title")}async getPlaceholderText(e){return await e.getAttribute("placeholder")}async getAltText(e){return await e.getAttribute("alt")}async getTagName(e){return await e.getTagName()}async getValue(e){return await e.getAttribute("value")}async getElementID(e){return await e.getAttribute("id")}async getElementDimension(e){const t=await e.getRect();if(![null,"",void 0].includes(t))return[t.x,t.y,t.height,t.width]}async isElementDisplayed(e){return await this.$driver.isVisible(e)}async isElementPresent(e){return await this.$driver.isPresent(e)}async getElementStrings(e){const t=[];if(this.isElementDisplayed(e)){[{type:"eleTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"titleTxt",value:await this.getTitle(e)},{type:"placeTxt",value:await this.getPlaceholderText(e)},{type:"valueTxt",value:await this.getValue(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)}))}return t}async getElementString(e){let t=null;const n=[this.getText,this.getAltText,this.getTitle,this.getPlaceholderText,this.getValue];for(const a of n)if(t=await a(e),null!==t)return t;return t}async captureScreenshot(e){await Xe(this.$driver,e)}async getElementsCount(e){return e.length}async switchToIFrame(e,t){return new Promise((t=>{this.$driver.frame(e,(function(){t(!0)}))}))}async switchToParentFrame(){return new Promise((e=>this.$driver.frame(null,(function(){e(!0)}))))}pause(e){this.$driver.pause(1e3*e)}async onScreenStringHighlight(e,t){await this.$driver.execute(((e,t)=>{const n=window.getComputedStyle(e).border;e.style.border=`3px solid ${t}`,setTimeout((()=>{e.style.border=n}),40)}),[e,t])}async checkIfIframeIsPresent(){return new Promise((e=>{this.$driver.isPresent({selector:"//iframe",locateStrategy:"xpath",suppressNotFoundErrors:!0},(t=>{e(t.value)}))}))}async checkIfIframeBodyIsEmpty(){return""===(await this.$driver.executeScript((function(){return document.body.innerHTML}))).trim()}}const Xe=async(e,t)=>{const n=await g({host:"localhost",port:Je(e)}),{Page:a}=n;await a.enable();const{data:r}=await a.captureScreenshot({format:"png",captureBeyondViewport:!0}),i=Buffer.from(r,"base64");s(t,i),Ze.debug(`Screenshot saved as: ${t}`),await n.close()},Je=e=>{const t=e?.capabilities?.["goog:chromeOptions"]?.debuggerAddress;return t&&t.split(":")?.[1]||9229};class Ke extends Ce{constructor(e){super(e)}async getElement(e){return await this.$driver.$(e)}async getElements(e){return await this.$driver.$$(e)}async getText(e){return await $(e).getText()}async getTitle(e){return await $(e).getAttribute("title")}async getPlaceholderText(e){return await $(e).getAttribute("placeholder")}async getAltText(e){return await $(e).getAttribute("alt")}async getTagName(e){return await $(e).getTagName()}async getValue(e){return await $(e).getValue()}async getElementID(e){return await $(e).getAttribute("id")}async getElementDimension(e){if(!e)return null;try{const t=await e.getLocation(),n=await e.getSize();return t&&n?[t.x,t.y,n.height,n.width]:null}catch(e){return console.error("Error getting element dimensions:",e),null}}async isElementDisplayed(e){return await $(e).isDisplayed()}async isElementPresent(e){return await $(e).isExisting()}async getElementStrings(e){const t=[];return[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"titleTxt",value:await this.getTitle(e)},{type:"placeTxt",value:await this.getPlaceholderText(e)},{type:"valueTxt",value:await this.getValue(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)})),t}async getElementString(e){let t=null;const n=[this.getText,this.getAltText,this.getTitle,this.getPlaceholderText,this.getValue];for(const a of n)if(t=await a(e),null!==t)return t;return t}async captureScreenshot(e){await et(this.$driver,e)}async getElementsCount(e){return e.length}async switchToIFrame(e,t){await this.$driver.switchFrame(t)}async switchToParentFrame(){await this.$driver.switchToParentFrame()}pause(e){this.$driver.pause(1e3*e)}async onScreenStringHighlight(e,t){await this.$driver.execute(((e,t)=>{const n=window.getComputedStyle(e).border;e.style.border=`3px solid ${t}`,setTimeout((()=>{e.style.border=n}),40)}),e,t)}async checkIfIframeIsPresent(){return new Promise((e=>{this.$driver.$("//iframe").isExisting().then((()=>{e(!0)}))}))}async checkIfIframeBodyIsEmpty(){return""===(await this.$driver.executeScript("return document.body.innerHTML;",[])).trim()}}const et=async(t,n)=>{const a=tt(t),r=await g({host:"localhost",port:a}),{Page:i}=r;await i.enable();const{data:s}=await i.captureScreenshot({format:"png",captureBeyondViewport:!0});e.writeFileSync(n,Buffer.from(s,"base64")),await r.close()},tt=e=>{const t=e.capabilities?.["goog:chromeOptions"]?.debuggerAddress;return t&&t.split(":")[1]||9229},nt=({subject:e,selector:t,options:n={},driver:a})=>{if(!Cypress)return void a.printOnConsole("Cypress object is inaccessible");const r={name:"xpath",message:t};if(Cypress.dom.isElement(e)&&e.length>1)throw new Error("xpath() can only be called on a single element. Your subject contained "+e.length+" elements.");const i=()=>{let n,a=[],i=cy.state("withinSubject");n=Cypress.dom.isElement(e)?e[0]:Cypress.dom.isDocument(e)?e:i?i[0]:cy.state("window").document;let s=(n.ownerDocument||n).evaluate(t,n);if(s.resultType===XPathResult.NUMBER_TYPE){const e=(e=>e.numberValue)(s);return r.consoleProps=()=>({XPath:t,type:"number",result:e}),e}if((e=>e.resultType===XPathResult.STRING_TYPE)(s)){const e=(e=>e.stringValue)(s);return r.consoleProps=()=>({XPath:t,type:"string",result:e}),e}if((e=>e.resultType===XPathResult.BOOLEAN_TYPE)(s)){const e=(e=>e.booleanValue)(s);return r.consoleProps=()=>({XPath:t,type:"boolean",result:e}),e}try{let e=s.iterateNext();for(;e;)a.push(e),e=s.iterateNext();return r.consoleProps=()=>({XPath:t,result:1===a.length?a[0]:a}),a}catch(e){return console.error("Document tree modified during iteration",e),null}},s=()=>Cypress.Promise.try(i).then((e=>{var a;return a=e,Cypress._.isNumber(a)||Cypress._.isString(a)||Cypress._.isBoolean(a)||((e=Cypress.$(e)).selector=t),cy.verifyUpcomingAssertions(e,n,{onRetry:s})}));return s().then((e=>(!1!==n.log&&Cypress.log(r),e))).catch((e=>console.log(e)))};class at extends Ce{constructor(e){super(e)}async getElement(e){return"string"!=typeof e?this.$driver.wrap(e):e.startsWith("//")?await this.xpath(e).first():await this.$driver.get(e).first()}async getElements(e){return"string"!=typeof e?this.$driver.wrap(e):e.startsWith("//")?await this.xpath(e):await this.$driver.get(e)}async getText(e){return"string"==typeof e?await this.$driver.get(e).invoke("text"):await this.$driver.wrap(e).invoke("text")}async getTitle(e){return"string"==typeof e?await this.$driver.get(e).title():this.$driver.wrap().title()}async getPlaceholderText(e){return"string"==typeof e?this.$driver.get(e).filter("input").invoke("attr","placeholder").then((e=>e)):this.$driver.$$(e).attr("placeholder")}async getAltText(e){return this.$driver.get(e).invoke("attr","alt")}async getWidth(e){return"string"==typeof e?this.$driver.get(e).invoke("width"):this.$driver.$$(e).width()}async getHeight(e){return"string"==typeof e?this.$driver.get(e).invoke("height"):this.$driver.$$(e).height()}async getTagName(e){return"string"==typeof e?this.$driver.get(e).invoke("prop","tagName").then((e=>e.toLowerCase())):this.$driver.$$(e).tagName}async getValue(e){return"string"==typeof e?this.$driver.get(e).invoke("val"):this.$driver.wrap(e).invoke("val")}async getLocation(e){if("string"==typeof e)return this.$driver.get(e).invoke("offset").then((e=>({x:e.left,y:e.top})));{const t=this.$driver.$$(e).offset();return{x:t.left,y:t.top}}}async isElementDisplayed(e){return"string"==typeof e?this.$driver.get(e).then((e=>e.is(":visible"))):this.$driver.$$(e).is(":visible")}async captureScreenshot(e){this.$driver.screenshot(e)}async getElementsCount(e){return new Promise((t=>"string"==typeof e?this.$driver.get(e).its("length").then((e=>t(e))):this.$driver.wrap(e,{timeout:8e3}).its("length").then((e=>t(e)))))}async getElementStrings(e){const t=[];try{[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAltText(e)},{type:"placetxt",value:await this.getPlaceholderText(e)}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)}))}catch(e){Ze.error("Error getting text of element:",e)}return t}printOnConsole(e){this.$driver.log(e)}xpath(e,t,n={}){return nt({subject:e,selector:t,options:n,driver:this.$driver})}}class rt extends Ce{constructor(e){super(e)}async getElement(e){return await this.driver.findElement(e)}async getElements(e){return await this.driver.findElements(e)}async getText(e){return await e.getText()}async getTitle(){return await this.driver.getTitle()}async getAttribute(e,t){return await e.getAttribute(t)}async getTagName(e){return await e.getTagName()}async getElementDimension(e){const t=await e.getRect();if(![null,"",void 0].includes(t))return[t.x,t.y,t.height,t.width]}async isElementDisplayed(e){return await e.isDisplayed()}async getElementStrings(e){const t=[];return[{type:"elTxt",value:await this.getText(e)},{type:"altTxt",value:await this.getAttribute(e,"alt")},{type:"titleTxt",value:await this.getAttribute(e,"title")},{type:"placeTxt",value:await this.getAttribute(e,"placeholder")},{type:"valueTxt",value:await this.getAttribute(e,"value")}].forEach((({value:e})=>{this.checkIfNullOrEmpty(e)&&t.push(e)})),t}async getElementsCount(e){return e.length}}const it=(e,t=", ")=>e.join(t),st=(e=!0)=>e?"(?:\\s|\\t|(?: ))":/(?:\s|\t|(?: ))/g;class ot{#se={};#U;constructor(e){this.#U=e,this.#oe(),this.buildConfig(),this.#le()}#oe(){this.addToConfig("supportedLocales",Ct.getOptions()?.supportedLocales,[]),this.addToConfig("defaultLocale",Ct.getDefaultLocale()),this.addToConfig("defaultLanguage",Ct.getDefaultLocale()?.split(/[-_ ]/g)[0]),this.addToConfig("language",Ct.getLocale()?.split(/[-_ ]/g)[0]),this.addToConfig("locale",Ct.getLocale()),this.addToConfig("qaLocale",Ct.getQALocale())}buildConfig(){throw new Error(`{{${this.#U}}} buildConfig should be implemented by child class`)}#le(){h.load(f.entireSupplemental()),h.load(f.entireMainFor(this.#ce(this.#se.defaultLocale),this.#ce(this.#se.locale)))}#ce(e){if(this.isQaLocale())return"en";e=e.replace(/[-_ ]/g,"-");const t=f.availableLocales;let n;if(t.find((t=>t===e)))n=e;else{const a=e.split("-")[0];n=t.find((e=>e===a))?a:e}return n}validate(e){throw new Error(`{{${this.#U}}} validate should be implemented by child class`)}isInternationalized(e){throw new Error(`{{${this.#U}}} isInternationalized should be implemented by child class`)}setConfig(e){this.#se=e}addToConfig(e,t,n){this.#se[e]=t??n}getConfig(){return this.#se}isSupportedLocale(){return this.#se.supportedLocales.includes(this.currentLocale())}isQaLocale(){return this.currentLocale()===this.#se?.qaLocale}isDefaultLocale(){return this.currentLocale()===this.defaultLocale()}defaultLocale(){return this.#se?.defaultLocale}defaultLanguage(){return this.#se?.defaultLanguage}currentLocale(){return this.#se?.locale}currentLanguage(){return this.#se.language}getGlobalize(e){return h(this.#ce(e))}}class lt extends ot{constructor(){super("currency")}validate(e){return this.#de(e)||this.isInternationalized(e)}isInternationalized(e){return this.#ue(this.currentLocale(),e)}buildConfig(){}#ue(e,t){const n=this.#pe(e),a=this.#me(e,n);if(!this.#ge(n,a,t))return!1;const r=this.#he(n,a,t);return!isNaN(this.getGlobalize(e).numberParser()(r))}#ge(e,t,n){return new RegExp(`^(?:-)?(?:${e}|\\${t})(?:${st()})?(?:-)?[0-9,. ]+$`,"gi").test(n)||new RegExp(`^(?:[- ]?)[0-9,. ]+(?:${st()})?(?:${e}|\\${t})$`,"gi").test(n)}#he(e,t,n){return n.replace(new RegExp(`^(?:-)(?:${st()})?`,"gi"),"").replace(new RegExp(`^(?:${st()})?(?:${e}|\\${t})`,"gi"),"").replace(new RegExp(`^(?:-)(?:${st()})?`,"gi"),"").replace(new RegExp(`(?:${st()})?(?:${e}|\\${t})$`,"gi"),"").trim()}#fe(e){return(this.getGlobalize(e).cldr.supplemental(["likelySubtags",e])||e).split(/[-_ ]/).pop()}#pe(e){const t=this.#fe(e),n=this.getGlobalize(e).cldr.supplemental("currencyData").region[t];if(n){const e=n.find((e=>{const t=Object.values(e)[0];return!t._tender&&(!t._to||new Date(t._to)>=new Date)}));return e?Object.keys(e)[0]:"USD"}return"USD"}#me(e,t){const n=this.getGlobalize(e).cldr.main(`numbers/currencies/${t}`);return n?.symbol||"$"}#de(e){return this.#ue(this.defaultLocale(),e)}}const ct=(e,t)=>{t=String(t);const n=(e=>{let[t="en",n="us"]=e.split(/[-_ ]/g);switch(n=String(n).toLowerCase(),t){case"en":return"gb"===n?v.GB:v;case"de":return I;case"es":return D;case"fr":return x;case"pt":return S;case"ja":return L;case"zh":return["hans","cn","sg","hans-cn"].includes(n)?E.hans:["hant","tw","hk","mo","hant-tw"].includes(n)?E.hant:E;case"ru":return b;case"nl":return y;case"uk":return w;default:return v}})(e).parse(String(t).trim());return n.forEach((e=>{t=t.replace(e.text,"")})),0===t.replace(/[^\p{L}\p{N}]+/gu,"").trim().length},dt=e=>{try{return e=e.replace("_","-"),Intl.DateTimeFormat.supportedLocalesOf(e)[0]}catch(t){return Logger.warn(`${e} is unsupported by Intl`),!1}},ut=(e,t,n)=>{if(!1!==(e=dt(e))){if(C.fromFormat(n,t,{locale:dt(e)}).isValid)return!0}return!1};class pt extends ot{constructor(){super("date")}validate(e){return this.#ve(e)||this.isInternationalized(e)}isInternationalized(e){return this.#we(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#we(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Ee(e,t)}#ve(e){return this.#we(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({date:a})(t))return!0}return!1}#be(e,t){const n=[C.DATE_SHORT,C.DATE_MED,C.DATE_FULL,C.DATE_HUGE,C.DATE_MED_WITH_WEEKDAY,"D","DD","DDD","DDDD"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Ee(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class mt extends ot{constructor(){super("dateTime")}validate(e){return this.#Le(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Se(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#Se(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Ee(e,t)}#Le(e){return this.#Se(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({datetime:a})(t))return!0}return!1}#be(e,t){const n=[C.DATETIME_SHORT,C.DATETIME_SHORT_WITH_SECONDS,C.DATETIME_MED,C.DATETIME_MED_WITH_SECONDS,C.DATETIME_MED_WITH_WEEKDAY,C.DATETIME_FULL,C.DATETIME_FULL_WITH_SECONDS,C.DATETIME_HUGE,C.DATETIME_HUGE_WITH_SECONDS,"f","F","ff","FF"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Ee(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class gt extends ot{constructor(){super("number")}validate(e){return this.#xe(e)||this.isInternationalized(e)}isInternationalized(e){return this.#De(this.currentLocale(),e)}buildConfig(){}#De(e,t){const n=this.getGlobalize(e).numberParser();return!isNaN(n(t))}#xe(e){return this.#De(this.defaultLocale(),e)}}class ht extends ot{constructor(){super("percentage")}validate(e){return this.#Ie(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Ce(this.currentLocale(),e)}buildConfig(){}#Ce(e,t){if(!this.#Te(e,t))return!1;const n=this.#ze(t);return!isNaN(this.getGlobalize(e).numberParser()(n))}#ze(e=""){return e.replace(new RegExp(`(^(?:${st()})*|(?:${st()})*$)`,"g"),"").replace(/^-/,"").replace(/٪|%/g,"").trim()}#Te(e,t){const n="[0-9,. ]+";let a;return a=["fa-IR","fa-ir"].includes(e)||e.startsWith("ar")?new RegExp(`(?:-)?(?:${st()})?(?:٪|%)(?:${st()})?${n}`,"g"):new RegExp(`(?:-)?(?:${st()})?${n}(?:${st()})?(?:٪|%)`,"g"),a.test(t)}#Ie(e){return this.#Ce(this.defaultLocale(),e)}}class ft extends ot{constructor(){super("string")}buildConfig(){const e=Ct.getOptions();this.addToConfig("mockChars",e?.qaMock),this.addToConfig("resource",Ct.getResourceBundle(),{}),this.addToConfig("garbledChars",Ct.getGarbledChars()?.filter((e=>String(e).trim())).filter(Boolean),[])}isInternationalized(e){const t=e.trim().replace(/\s*:\s*$/,""),n=this.getConfig()?.resource?.[this.defaultLocale()];return n?.includes(e)||n?.includes(t)}validate(e){return"string"==typeof e}isDntText(e){return this.getConfig()?.resource?.dnt?.includes(e)}isAutoIgnoredText(e){return this.isDefaultLocale()||this.isUserInputText(e)||this.isEmailText(e)||this.isUrlText(e)}isIgnoredText(e){return this.getConfig()?.resource?.ignore?.includes(e)}isUserInputText(e){return e.startsWith("zqa_")}isEmailText(e){return pe.EMAIL.test(e)}isUrlText(e){return pe.URL.test(e)}isFullQaLanguageText(e){const t=this.getConfig();return(e.slice(0,2)===t?.mockChars?.prefix||e.slice(0,2)===t?.mockChars?.prefix.toUpperCase())&&e.slice(-2)===t?.mockChars.suffix}isPartialQaLanguageText(e){const t=this.getConfig();return e.slice(0,2)===t?.mockChars.prefix||e.slice(0,2)===t?.mockChars.prefix.toUpperCase()||e.slice(-2)===t?.mockChars.suffix}hasEncodingIssue(e){return this.getConfig()?.garbledChars?.length>0&&this.getConfig().garbledChars.some((t=>e.includes(t)))}isPartiallyLocalizedText(e){return this.#Ne(e)===this.currentLanguage()}#Ne(e){const t=Object.keys(T(e)?.scores||{}).filter(Boolean)||[],n=this.currentLanguage();return t.length?t.includes(n)?n:t[0]:this.defaultLanguage()}isLocalized(e){const t=e.trim().replace(/\s*:\s*$/,""),n=this.getConfig()?.resource?.[this.currentLocale()];return n?.includes(e)||n?.includes(t)}}class vt extends ot{constructor(){super("time")}validate(e){return this.#Me(e)||this.isInternationalized(e)}isInternationalized(e){return this.#Pe(this.isQaLocale()?this.defaultLocale():this.currentLocale(),e)}buildConfig(){}#Pe(e,t){return this.#ye(e,t)||this.#be(e,t)||this.#Ee(e,t)}#Me(e){return this.#Pe(this.defaultLocale(),e)}#ye(e,t){const n=["short","medium","long","full"];for(let a of n){if(this.getGlobalize(e).dateParser({time:a})(t))return!0}return!1}#be(e,t){const n=[C.TIME_SIMPLE,C.TIME_WITH_SECONDS,C.TIME_WITH_SHORT_OFFSET,C.TIME_WITH_LONG_OFFSET,C.TIME_24_SIMPLE,C.TIME_24_WITH_LONG_OFFSET,C.TIME_24_WITH_SECONDS,C.TIME_24_WITH_SHORT_OFFSET,"t","T","tt","TT"];for(let a of n){if(ut(e,a,t))return!0}return!1}#Ee(e,t){return ct(this.isQaLocale()?this.defaultLocale():e,t)}}class wt{static#Ae={};static#Oe;static#$e;static#Re;static#ke;static#X;static#_e;static#Be;static load(){wt.#Oe=new ft,wt.#$e=new gt,wt.#Re=new pt,wt.#ke=new vt,wt.#X=new mt,wt.#_e=new lt,wt.#Be=new ht}static getIssueType(e){const t=Ct.getLocale();if(t){let n=ie;return wt.#Ae[t]||(wt.#Ae[t]={}),wt.#Ae[t][e]?wt.#Ae[t][e]:(this.#Oe.isAutoIgnoredText(e)?n=se:this.#Oe.isDntText(e)?n=ae:this.#Oe.isIgnoredText(e)?n=re:this.#Oe.hasEncodingIssue(e)?n=ne:this.#_e.validate(e)?n=this.#_e.isInternationalized(e)?U:Z:this.#Be.validate(e)?n=this.#Be.isInternationalized(e)?V:Y:this.#$e.validate(e)?n=this.#$e.isInternationalized(e)?F:Q:this.#Re.validate(e)?n=this.#Re.isInternationalized(e)?W:X:this.#ke.validate(e)?n=this.#ke.isInternationalized(e)?G:J:this.#X.validate(e)?n=this.#X.isInternationalized(e)?q:K:this.#Oe.validate(e)?this.#Oe.isQaLocale()?n=this.#Oe.isFullQaLanguageText(e)?B:this.#Oe.isPartialQaLanguageText(e)?j:H:!this.#Oe.isDefaultLocale()&&this.#Oe.isSupportedLocale()&&(n=this.#Oe.isLocalized(e)?B:this.#Oe.isPartiallyLocalizedText(e)?j:this.#Oe.isInternationalized(e)?ie:H):this.#Oe.isDefaultLocale()&&(n=se),wt.#Ae[t][e]=n,n)}return ie}}class yt{static load(){const e={};if(Ct.getOptions()?.l10nResourcePath){const t=Oe(process.cwd(),Ct.getOptions()?.l10nResourcePath);if(ze(t))yt.#je(t,e);else{if(!Te(t))return void console.error(`${t} invalid resource path`);for(const n of Ae(t)){const a=Oe(t,n);yt.#je(a,e)}}Ct.setResourceBundle(e)}}static#je(e,t){const n=Ct.getLocale(),a=Ct.getDefaultLocale(),r=Pe(e),i=(e=>ze(e)?p(e):"")(e);if(me.includes(i)){if(!r)return void console.error(`${e} is empty or invalid`);if(e.toLowerCase().includes("dnt"))t.dnt=r.split(/\r?\n/);else if(e.toLowerCase().includes("ignore"))t.ignore=r.split(/\r?\n/);else if(e.includes(n)){const e=r.split(/\r?\n/);t[n]=e.map(bt)}else e.includes(a)&&(t[a]=r.split(/\r?\n/))}}}function bt(e){return e.replace(/<\/?[^>]+>/gi,"")}const Et=()=>{Ct.getDriver()&&!Ct.isLoaded()&&Ct.getLocale()&&(Ct.build(),yt.load(),wt.load(),Qe.setup(),Ue.init(),Ct.setIsLoaded(!0))},Lt=()=>{let e=process.cwd(),t=0;for(;t<10;){const n=Oe(e,"package.json");if(ze(n)){const e=Pe(n),t=JSON.parse(e),a={...t?.dependencies||{},...t?.devDependencies||{},...t?.peerDependencies||{}};if("@zeesoft/i18n-verify"!==t.name&&JSON.stringify(a).includes('"@zeesoft/i18n-verify"'))break}e=u(e),t++}return e},St=(e=Lt())=>Pe(Oe(e,"package.json")),xt=(e="")=>`{\n "appName": "${St()?.match(/"name": "([^"]+)",/)?.[1]||""}",\n "type": "${e}", // set automation framework type ${it(Object.values(N))}\n "qaLocale": "", // set the qa locale used in your project\n "l10nResourcePath": "./l10nResources", // set the dir path of l10n resources\n "l10nReportsPath": "./l10nReports/", // set the dir path of l10n reports\n "supportedLocales": ["en_US", "fr_FR", "ja_JP"], // set the list of supported locales\n "defaultLocale": "en_US", // set default locale of the application\n "qaMock": { // set the prefix and suffix characters of qa locale\n "prefix": "à_",\n "suffix": "_海"\n },\n "mode": "${Ee.I18N}", // should be one of the following: ${Object.values(Ee).join(", ")}\n "garbledChars": [""]\n}`,Dt=()=>{const e=Oe(process.cwd(),ve);if(!ze(e))throw new Error(`${ve} file is missing. Please add the file in your project root dir`);let t=Pe(e);if(!t)throw new Error(`invalid ${ve} file. Please add valid file in your project root dir`);t=t.replace(/\/\/.+$/gm,"");try{t=JSON.parse(t)}catch(e){throw new Error(`invalid ${ve} file. Please add valid file in your project root dir`)}return t},It=(e,t="")=>{e&&!ze(e)&&$e(e,xt(t))};class Ct{static#Fe;static#Ue;static#U;static#Ve;static#We;static#Ge;static#qe;static#He;static#Qe;static#e;static#Ze;static init(){try{const e=Dt();(e=>{if("object"!=typeof e||Object.keys(e).length<1)throw new Error(`empty ${ve} file`);const t=[];if(!((e,t,n=[])=>["l10nResourcePath","l10nReportsPath","supportedLocales","defaultLocale","type","mode"].every((e=>(((e,t)=>e in t)(e,t)||n.push(e),!0))))(0,e,t))throw new Error(`following properties are missing in ${ve} file: ${it(t)}`);if(!Object.values(N).includes(e?.type||""))throw new Error(`${ve} has invalid system type`);if(!Object.values(Ee).includes(e?.mode||""))throw new Error(`${ve} has invalid mode option`)})(e),this.#U=e?.type,this.#Ve=e?.mode,this.#Ge=e?.qaLocale,this.#e=e?.appName,this.#qe=e,this.#Qe=0,this.getGarbledChars()}catch(e){throw Ze.error(e.message),e}}static build(){this.#Fe=((e,t)=>{switch(e){case N.NIGHT_WATCH:return new Ye(t);case N.WEB_DRIVER_IO:return new Ke(t);case N.CYPRESS:return new at(t);case N.SELENIUMJS:return new rt(t);default:throw new Error("Unsupported framework")}})(this.#U,this.#Fe),this.resetCounter()}static getGarbledChars(){return[...this.#qe.garbledChars||[],...fe]}static getL10nReportsPath(){return this.#qe.l10nReportsPath}static setDriver(e){this.#Fe=e}static setType(e){this.#U=e}static setLocale(e){this.#We=e}static setOptions(e){this.#qe=e}static getExecutor(){if(Ct.#Ue)return Ct.#Ue;if(!Ct.#Fe)throw new Error("Unable to initialize process executor, driver is missing");return this.#Ue=new At(this.#Fe),this.#Ue}static getDriver(){return this.#Fe}static getLocale(){return this.#We}static getDefaultLocale(){return this.#qe.defaultLocale}static getType(){return this.#U}static getOptions(){return this.#qe}static setQALocale(e){this.#Ge=e}static getQALocale(){return this.#Ge}static setResourceBundle(e){this.#He=e}static getResourceBundle(e){return this.#He?.[e]||this.#He||[]}static incrementCounter(){this.#Qe++}static resetCounter(){this.#Qe=0}static getSSCount(e=!1){return e?++this.#Qe:this.#Qe}static getMode(){return this.#Ve}static isSanityRun(){return this.#Ve===Ee.SANITY}static isI18nVerificationRun(){return this.#Ve===Ee.I18N}static isContextRun(){return this.#Ve===Ee.CONTEXT}static getAppName(){return this.#e}static isLoaded(){return this.#Ze}static setIsLoaded(e){this.#Ze=e}}class Tt{#Fe;#Ye;#Xe;#W;#Je=[];#Ke;#et;#tt;#nt;#at;constructor(e,t,n,a,r){this.#Fe=e,this.#Ye=t,this.#Xe=n,this.#at=a,this.#W=r}async run(){try{await this.#rt(),this.shouldCollectStrings()?await this.#it():await this.#st()}catch(e){Ze.error("unable to run the process",e)}finally{await this.#ot()}}async process(e,t,n,a){throw new Error("should be implemeneted by the child class")}shouldCollectStrings(){return!0}async preProcess(){}async postProcess(){}async captureScreen(e){try{return await this.#Fe.captureScreenshot(e),Ze.debug("reviewed image has been saved"),!0}catch(e){return Ze.error("unable to capture screenshot"),!1}}getBaseImageName(){return this.#et}getDescription(){return this.#W}getVerificationArea(){return this.#Ye}getVerificationAreaEl(){return this.#tt}getVerificationAreaOffsets(){return this.#nt}getDriver(){return this.#Fe}async getElementOffsets(e){const t=await this.#Fe.getElementDimension(e);return{x:t[0],y:t[1],height:t[2],width:t[3]}}async#it(){this.#Je.length?(await this.#lt(),await this.#ct()):Ze.debug("No locators provided")}async#st(){await this.process(this.#W)}async#rt(){this.#dt(),this.#ut(),await this.#pt(),await this.#mt(),await this.preProcess()}async#ot(){await this.postProcess()}#dt(){const e=this.#Ye||"";if(this.#Xe?.length>0)for(const t of this.#Xe)this.#Je.push(e+t);this.#at&&(this.#Ke=""!==e?e+ge:ge,this.#Je.push(this.#Ke.toString()))}#ut(){this.#et=`${Ct.getSSCount(!0).toString().padStart(3,"0")}_${this.#W.replace(/[^\w\s]/gi,"").replace(/\s+/g,"")}.png`}async#mt(){const e=Oe(Qe.reviewedReports(),this.#et);await this.captureScreen(e)}async#pt(){if(this.#tt=await this.#Fe.getElement(this.#Ye),!this.#tt)throw new Error("invalid verification area selector is supplied");this.#nt=await this.getElementOffsets(this.#tt)}async#lt(){let e=!1;for(const t of this.#Je){const n=await this.#Fe.getElements(t,!0);if(n?.length){!e&&(e=!0);for(const e of n)await this.#gt(e,!0)}}e||Ze.debug("No UI elements found from the provided locators")}async#ct(){if(await this.#Fe.checkIfIframeIsPresent()){const e=await this.#Fe.getElements("//iframe");Ze.debug(`Total ${e.length} iframes are located on the ${this.#W}`);for(let t=0;t<e.length;t++){const n=e[t],a=await this.getElementOffsets(n);Ze.debug(`Switching to iFrame: ${t}`),await this.#Fe.switchToIFrame(t,n),this.#Fe.pause(1);const r=await this.#Fe.getElement("//body");await this.#Fe.isElementPresent(r);let i=!1;if(!await this.#Fe.checkIfIframeBodyIsEmpty()){const e=await this.#Fe.getElements(ge,!0);if(e?.length)for(const n of e){const e=await this.#gt(n,!0,a,t);!i&&e&&(i=!0)}}!i&&Ze.debug("No valid UI elements found in the iFrame"),await this.#Fe.switchToParentFrame()}}}async#gt(e,t=!1,n,a){if(await this.#ht(e)){const r=t?await this.#Fe.getElementStrings(e):[await this.#Fe.getElementString(e)];if(r?.length){for(const t of r)try{await this.process(t,e,n,a)}catch(e){Ze.error(`unable to process the ${a?`iframe-[${a}] `:""}string "${t}"`,e)}return!0}return!1}return!1}async#ht(e){return e&&await this.#Fe.isElementDisplayed(e)}}class zt extends Tt{#G={screenshot:"",category:"",fileName:"",bookmark:!0,pageTitle:"",strings:{}};#ft=0;#vt="";constructor(...e){super(...e)}async preProcess(){this.#wt(),this.#yt(),this.#bt(),this.#Et(),this.#Lt()}async process(e,t,n){Qe.incrementCount(le,e,this.getBaseImageName(),this.getDescription()),Ze.debug(`context creation for "${e}"`),await this.#St(e,t,n)}async postProcess(){this.#xt();const e=this.#Dt();$e(Oe(Qe.getContextPath(),`${this.#G.fileName}.json`),JSON.stringify(e)),He.incrementCountByType("context")}#wt(){this.#ft=Ae(Qe.getContextPath()).length+1}#yt(){this.#G.category=this.getDescription()}#bt(){this.#G.pageTitle=`TC-${Ct.getAppName()}-${this.getDescription().trim().slice(0,2).toUpperCase()}-${`${this.#ft}`.padStart(3,"0")}`}#Et(){this.#G.fileName=`${Re(Ct.getAppName())}-${`${this.#ft}`.padStart(3,"0")}`}#Lt(){this.#vt=`${Re(Ct.getAppName())}_${`${this.#ft}`.padStart(3,"0")}`}#xt(){var e;this.#G.screenshot=`data:image/png;base64,${e=Oe(Qe.reviewedReports(),this.getBaseImageName()),r(e).toString("base64")}`}#Dt(){return{[Le]:this.#G.bookmark,[Se]:this.#G.category,[xe]:this.#G.fileName,[De]:this.#G.screenshot,[Ie]:JSON.stringify(this.#G.strings)}}async#St(e,t,n){const a=await this.getElementOffsets(t),r=`${this.#vt}_${`${Object.keys(this.#G.strings).length+1}`.padStart(3,"0")}`;if(n&&Object.keys(n||{}).length){const e=n,t=["x","y"];for(const n of t)n in a&&n in e&&(a[n]+=e[n])}this.#G.strings[r]={text:e,coordinates:{...a}}}}class Nt{static async drawRectangle(t,n,a){const r=t?.x-2,i=t?.y-2,s=t?.width+2,o=t?.height+2;try{const t=await z.read(a),l=z.cssColorToHex(n);this.drawRectangleOutline(t,r,i,s,o,l,2);const d=c.dirname(a),u=await t.getBufferAsync(z.MIME_PNG);e.existsSync(d)||(Ze.debug("folder not found"),e.mkdirSync(d,{recursive:!0})),e.writeFileSync(a,u)}catch(e){Ze.error(`Error: ${e}`)}}static async drawRectangleOutline(e,t,n,a,r,i,s){for(let o=t;o<t+a;o++)for(let t=0;t<s;t++)e.setPixelColor(i,o,n+t),e.setPixelColor(i,o,n+r-1-t);for(let o=n;o<n+r;o++)for(let n=0;n<s;n++)e.setPixelColor(i,t+n,o),e.setPixelColor(i,t+a-1-n,o)}}class Mt extends Tt{constructor(...e){super(...e)}async process(e,t,a){if("string"!=typeof e||!e||0===Number(e))return;const r=Oe(Qe.reviewedReports(e,this.getBaseImageName()),this.getBaseImageName()),i=wt.getIssueType(e),s=Oe(Qe[i](e,this.getBaseImageName()),this.getBaseImageName());Qe.incrementCount(le,e,this.getBaseImageName(),this.getDescription()),n(r)||l(Oe(Qe.reviewedReports(),this.getBaseImageName()),r),n(r)&&!n(s)&&l(r,s);const o=this.#It(i,e);Qe.incrementCount(i,e,s,this.getDescription()),await this.#Ct(t,o,s,a)}async#Ct(e,t,n,a){await this.#Tt(e,t),await this.#zt(e,t,n,a)}async#Tt(e,t){await this.getDriver().onScreenStringHighlight(e,t)}async#zt(e,t,n,a){let r;const i=await this.getElementOffsets(e),s=O;if(a){r=a;const e=["x","y"];for(const t of e)t in i&&t in r&&(i[t]=r[t]+i[t])}else r=this.getVerificationAreaOffsets();await Nt.drawRectangle(r,s,n),await Nt.drawRectangle(i,t,n)}#It(e,t){return Ze.debug(`"${t}" - ${de[e][1]}${ue[e]}[0m`),de[e][0]}}class Pt extends Tt{constructor(...e){super(...e)}shouldCollectStrings(){return!1}async process(e){Ze.info(`${e} loaded`);const t=Oe(Qe.reviewedReports(e,this.getBaseImageName()),this.getBaseImageName());Qe.incrementCount(le,e,t,this.getDescription()),n(t)||l(Oe(Qe.reviewedReports(),this.getBaseImageName()),t)}}class At{#Fe;constructor(e){this.#Fe=e}async start({verificationArea:e,staticLocators:t,useSmartSelector:n,description:a}){const r=((e,t,n,a,r)=>{switch(Ct.getMode()){case Ee.I18N:return new Mt(e,t,n,a,r);case Ee.CONTEXT:return new zt(e,t,n,a,r);case Ee.SANITY:return new Pt(e,t,n,a,r);default:throw new Error("unsupported runner type")}})(this.#Fe,e,t,n,a);await r.run()}}export{Ct as C,At as E,Ze as L,Qe as R,N as S,St as a,ve as b,It as c,Lt as g,Oe as j,Et as l};
|
package/package.json
CHANGED