UNPKG

fepper-ui

Version:

The client-side package that powers the Fepper UI

356 lines (290 loc) 13.8 kB
export default class RequerioInspector { constructor(fepperUi) { this.codeViewer = fepperUi.codeViewer; this.$orgs = fepperUi.requerio.$orgs; this.poll = true; this.requerio = fepperUi.requerio; this.requerioInspector = fepperUi.requerioInspector; this.stoked = false; } listen() { const $orgs = this.$orgs; $orgs['#sg-viewport'].on('load', () => { if (this.interval) { this.interval = clearInterval(this.interval); } const classesNew = []; const requerioInspector = this.requerioInspector; const requerioP = $orgs['#sg-viewport'][0].contentWindow.requerio; const selectorsOrig = []; const selectorsNew = []; if (!$orgs['#sg-code-tree-requerio-trunk']) { this.requerio.incept('#sg-code-tree-requerio-trunk'); } else if (!$orgs['#sg-code-tree-requerio-trunk'].length) { $orgs['#sg-code-tree-requerio-trunk'].populateMembers(); } if (!$orgs['#sg-code-tree-requerio-trunk>li']) { this.requerio.incept('#sg-code-tree-requerio-trunk>li'); } else if (!$orgs['#sg-code-tree-requerio-trunk>li'].length) { $orgs['#sg-code-tree-requerio-trunk>li'].populateMembers(); } if (!$orgs['li.sg-code-tree-requerio-branch']) { this.requerio.incept('li.sg-code-tree-requerio-branch'); } else if (!$orgs['li.sg-code-tree-requerio-branch'].length) { $orgs['li.sg-code-tree-requerio-branch'].populateMembers(); } if (!$orgs['li.sg-code-tree-requerio-branch>.clickable']) { this.requerio.incept('li.sg-code-tree-requerio-branch>.clickable'); } else if (!$orgs['li.sg-code-tree-requerio-branch>.clickable'].length) { $orgs['li.sg-code-tree-requerio-branch>.clickable'].populateMembers(); } const $listItemsTrunk = $orgs['#sg-code-tree-requerio-trunk>li']; if ( !requerioP || !requerioP.$orgs || !$orgs['#sg-code-tree-requerio-trunk>li'] || !$orgs['#sg-code-tree-requerio-trunk>li'].length ) { this.hideRequerioShowNa(); return; } // Be sure not to incept organisms with names beginning with .sg-code-tree-requerio- elsewhere. // They will get deleted by this loop. for (const key of Object.keys($orgs)) { if (key.startsWith('.sg-code-tree-requerio-')) { delete $orgs[key]; } } for (let i = 0, l = $listItemsTrunk.length; i < l; i++) { $orgs['#sg-code-tree-requerio-trunk>li'].dispatchAction('text', null, i); const liState = $orgs['#sg-code-tree-requerio-trunk>li'].getState(i); if (!liState.textContent) { continue; } const selector = liState.textContent.split(':')[0]; let classNew = 'sg-code-tree-requerio-' + selector.replace(/-/g, '_U_002D_').replace(/\W/g, '').replace(/_U_002D_/g, '-'); let selectorNew = '.' + classNew; while (Object.keys($orgs).includes(selectorNew)) { classNew += '_'; selectorNew += '_'; } classesNew.push(classNew); selectorsOrig.push(selector); selectorsNew.push(selectorNew); $orgs['#sg-code-tree-requerio-trunk>li'].dispatchAction('addClass', classNew, i); this.requerio.incept(selectorNew); } const selectorsOrigLength = selectorsOrig.length; // Using additional logic in case elements already have a contrast filter. for (let i = 0; i < selectorsOrigLength; i++) { const selectorOrig = selectorsOrig[i]; if (!requerioP || !requerioP.$orgs || !requerioP.$orgs[selectorOrig]) { continue; } const patternOrgState = requerioP.$orgs[selectorOrig].getState(); for (let j = 0; j < patternOrgState.members; j++) { const computedStyle = window.getComputedStyle(requerioP.$orgs[selectorOrig][j]); if (computedStyle.filter.includes('contrast')) { const filterOrig = computedStyle.filter; requerioP.$orgs[selectorOrig].dispatchAction('data', {filterOrig}, j); } } } let displayedItemsLength = 0; for (let i = 0; i < selectorsOrigLength; i++) { const selectorOrig = selectorsOrig[i]; const selectorNew = selectorsNew[i]; if (!requerioP || !requerioP.$orgs || !requerioP.$orgs[selectorOrig]) { continue; } $orgs[selectorNew].dispatchAction('css', {display: 'list-item'}); displayedItemsLength++; if (selectorOrig === 'window' || selectorOrig === 'document') { continue; } $orgs[selectorNew].on('mouseenter', () => { const patternOrgState = requerioP.$orgs[selectorOrig].getState(); // Using additional logic in case elements already have a contrast filter. for (let j = 0; j < patternOrgState.members; j++) { const filterOrig = patternOrgState.$members[j].data.filterOrig; const contrastOrig = filterOrig && filterOrig.includes('contrast(') && filterOrig .replace(/^.*contrast\(/, '') .replace(/\).*$/, ''); if (contrastOrig) { const contrastOrigFloat = parseFloat(contrastOrig); const filterSplit = filterOrig.split('contrast('); const filter0 = filterSplit[0]; const filterSplitSplit = filterSplit[1].split(')'); filterSplitSplit[0] = ''; const filter1 = filterSplitSplit.join(')'); let contrastNew; if (contrastOrigFloat < 0.25) { contrastNew = contrastOrigFloat + 0.25; } else if (contrastOrigFloat > 1) { contrastNew = 0.75; } else { contrastNew = contrastOrigFloat - 0.25; } requerioP.$orgs[selectorOrig].dispatchAction( 'css', {filter: filter0 + 'contrast(' + contrastNew + filter1}, j ); } else { requerioP.$orgs[selectorOrig].dispatchAction('css', {filter: 'contrast(0.75)'}, j); } } }); $orgs[selectorNew].on('mouseleave', () => { const patternOrgState = requerioP.$orgs[selectorOrig].getState(); // Using additional logic in case elements already have a contrast filter. for (let j = 0; j < patternOrgState.members; j++) { const filter = patternOrgState.$members[j].data.filterOrig; if (filter) { requerioP.$orgs[selectorOrig].dispatchAction('css', {filter}, j); } else { requerioP.$orgs[selectorOrig].dispatchAction('css', {filter: 'contrast(1)'}, j); } } }); requerioP.$orgs[selectorOrig].on('mouseenter', () => { $orgs[selectorNew].dispatchAction('addClass', 'highlighted'); }); requerioP.$orgs[selectorOrig].on('mouseleave', () => { $orgs[selectorNew].dispatchAction('removeClass', 'highlighted'); }); } if (!displayedItemsLength) { this.hideRequerioShowNa(); return; } // If you've made it this far, show Requerio and hide NA. this.$orgs['#sg-code-pane-requerio'].dispatchAction('css', {display: 'block'}); this.$orgs['#sg-code-pane-requerio-na'].dispatchAction('css', {display: 'none'}); $orgs['li.sg-code-tree-requerio-branch>.clickable'].on('click', function () { requerioInspector.toggleExpandableBranch(this.parentElement); }); let patternStoreStateBefore = requerioP.store.getState(); for (const patternOrgKey of Object.keys(requerioP.$orgs)) { const patternOrg = requerioP.$orgs[patternOrgKey]; // Set a temporary data value for window and document so in the setInterval() block, // a change gets picked up and their corresponding HTML get redrawn. if (patternOrgKey === 'window' || patternOrgKey === 'window') { patternOrg.dispatchAction('data', {stoked: false}); } // Get targeted states of $members of $organisms. This will write their measurements to the state. else if (patternOrg) { for (let i = 0, l = patternOrg.$members.length; i < l; i++) { requerioP.$orgs[patternOrgKey].getState(i); } } } this.interval = setInterval(() => { // Poll 4 times a second continuously for 10 minutes. Reduce or stop the polling when inactive. // Polling stops when switching to a different browser tab. // Polling restarts when switching back to this tab. // pollCount resets to 0 when: // 1. Switching to a different tab and then switching back. // 2. Mouse moves within the UI document. // 3. Mouse moves within the pattern iframe document. // If pollCount reaches 2400 (10 minutes), polling will only occur every 4 seconds unless pollCount resets to 0. // // Cannot reasonably test with automation. Instead: // 1. Check that the polling slows after exceeding pollCount threshold. // 2. Check that the polling stops when switching to a different browser tab. // 2. Check that the polling resets and restarts when switching back to this tab. // 3. Check that pollCount resets when mouse moves within the UI document. // 3. Check that pollCount resets when mouse moves within the pattern iframe document. if (this.poll) { if (this.requerioInspector.pollCount < 2400 || this.requerioInspector.pollCount % 16 === 0) { // eslint-disable-next-line max-len // if (this.requerioInspector.pollCount < 40 || this.requerioInspector.pollCount % 16 === 0) { // DEBUGGING ONLY if (this.codeViewer.codeActive && this.codeViewer.tabActive === 'requerio') { // console.log(Math.floor(Date.now() / 1000), this.requerioInspector.pollCount); // DEBUGGING ONLY const patternStoreStateBeforeString = JSON.stringify(patternStoreStateBefore); let patternStoreStateNow; let patternStoreStateNowString; if (this.stoked) { for (const organism of Object.keys(patternStoreStateBefore)) { const $organism = requerioP.$orgs[organism]; const memberIdxs = $organism.$members.map((val, idx) => idx); for (let memberIdx of memberIdxs) { $organism.getState(memberIdx); } $organism.updateMeasurements(patternStoreStateBefore[organism]); } patternStoreStateNow = requerioP.store.getState(); patternStoreStateNowString = JSON.stringify(patternStoreStateNow); } else { for (const patternOrgKey of Object.keys(requerioP.$orgs)) { const patternOrg = requerioP.$orgs[patternOrgKey]; // Unset temporary data. if (patternOrgKey === 'window' || patternOrgKey === 'window') { patternOrg.dispatchAction('removeData', 'stoked'); } } this.stoked = true; patternStoreStateNow = requerioP.store.getState(); patternStoreStateNowString = JSON.stringify(patternStoreStateNow); } if (patternStoreStateBeforeString !== patternStoreStateNowString) { this.requerioInspector.recurseStatesAndDom( patternStoreStateBefore, patternStoreStateNow, $orgs['#sg-code-tree-requerio-trunk'][0].children ); patternStoreStateBefore = patternStoreStateNow; } } } this.requerioInspector.pollCount++; } }, 250); // Add an event listener for mousemoves within the iframe. Necessary because mousemoves within the iframe will not // trigger events on the UI document. $orgs['#sg-viewport'][0].contentWindow.document.addEventListener('mousemove', () => { this.requerioInspector.pollCount = 0; }); }); // Reset pollCount if there is a mousemove within the document of the UI. document.addEventListener('mousemove', () => { this.requerioInspector.pollCount = 0; }); window.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { this.poll = true; this.requerioInspector.pollCount = 0; } // Stop polling if switching to a different browser tab. else { this.poll = false; } }); $orgs['#sg-viewport'].on('load', () => { const logo = $orgs['#sg-viewport'][0].contentWindow.document.querySelector('.img-alt-h1 > .logo'); if (logo && logo.title === 'Fepper Demo Site') { $orgs['#sg-code-requerio-demo-link-container'].dispatchAction('css', {display: 'block'}); $orgs['#sg-code-main-distro-link-container'].dispatchAction('css', {display: 'none'}); } else { $orgs['#sg-code-requerio-demo-link-container'].dispatchAction('css', {display: 'none'}); $orgs['#sg-code-main-distro-link-container'].dispatchAction('css', {display: 'block'}); } }); } hideRequerioShowNa() { this.$orgs['#sg-code-pane-requerio'].dispatchAction('html', this.requerioInspector.htmlOrig); this.$orgs['#sg-code-pane-requerio'].dispatchAction('css', {display: 'none'}); this.$orgs['#sg-code-pane-requerio-na'].dispatchAction('css', {display: 'block'}); } }