Skip to content

Commit

Permalink
[Chore] rewrite stack overflow functions (#2258)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorDykhta committed Jun 15, 2023
1 parent 9d57f57 commit c79e9f9
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 56 deletions.
6 changes: 4 additions & 2 deletions src/reducers/src/map-state-updaters.ts
Expand Up @@ -478,10 +478,12 @@ function getViewportFromMapState(state) {
]);
}

// From https://stackoverflow.com/a/56650790
/** Select items from object whose value is not undefined */
const definedProps = obj =>
Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== undefined));
Object.entries(obj).reduce(
(accu, [k, v]) => ({...accu, ...(v !== undefined ? {[k]: v} : {})}),
{}
);

function updateViewport(originalViewport: Viewport, viewportUpdates: Viewport): Viewport {
let newViewport = {
Expand Down
60 changes: 8 additions & 52 deletions src/utils/src/dom-to-image.ts
Expand Up @@ -28,6 +28,7 @@ import document from 'global/document';
import Console from 'global/console';
import svgToMiniDataURI from 'mini-svg-data-uri';
import {IMAGE_EXPORT_ERRORS} from '@kepler.gl/constants';

import {
canvasToBlob,
escape,
Expand All @@ -43,7 +44,9 @@ import {
resolveUrl,
getWidth,
getHeight,
getAndEncode
getAndEncode,
setStyleSheetBaseHref,
toStyleSheet
} from './dom-utils';

const inliner = newInliner();
Expand Down Expand Up @@ -355,8 +358,10 @@ function newFontFaces() {
return window
.fetch(sheet.href, {credentials: 'omit', cache})
.then(response => response.text())
.then(setBaseHref(sheet.href))
.then(toStyleSheet)
.then(text => {
const result = setStyleSheetBaseHref(text, sheet.href);
return toStyleSheet(result);
})
.catch(err => {
// Handle any error that occurred in any of the previous
// promises in the chain. stylesheet failed to load should not stop
Expand All @@ -369,55 +374,6 @@ function newFontFaces() {
return Promise.resolve(sheet);
})
);

function setBaseHref(base) {
base = base.split('/');
base.pop();
base = base.join('/');

function addBaseHrefToUrl(match, p1) {
const url = /^http/i.test(p1) ? p1 : concatAndResolveUrl(base, p1);
return `url('${url}')`;
}

// Source: http://stackoverflow.com/a/2676231/3786856
function concatAndResolveUrl(url, concat) {
const url1: string[] = url.split('/');
const url2: string[] = concat.split('/');
const url3: string[] = [];
for (let i = 0, l = url1.length; i < l; i++) {
if (url1[i] === '..') {
url3.pop();
} else if (url1[i] !== '.') {
url3.push(url1[i]);
}
}
for (let i = 0, l = url2.length; i < l; i++) {
if (url2[i] === '..') {
url3.pop();
} else if (url2[i] !== '.') {
url3.push(url2[i]);
}
}
return url3.join('/');
}

return text => {
return isSrcAsDataUrl(text)
? text
: text.replace(/url\(['"]?([^'"]+?)['"]?\)/g, addBaseHrefToUrl);
};
}

function toStyleSheet(text) {
const doc = document.implementation.createHTMLDocument('');
const styleElement = document.createElement('style');

styleElement.textContent = text;
doc.body.appendChild(styleElement);

return styleElement.sheet;
}
}

function getCssRules(styleSheets) {
Expand Down
27 changes: 25 additions & 2 deletions src/utils/src/dom-utils.ts
Expand Up @@ -148,7 +148,6 @@ export function asArray(arrayLike) {
}

export function fourRandomChars() {
/* see http://stackoverflow.com/a/6248722/2519373 */
return `0000${((Math.random() * Math.pow(36, 4)) << 0).toString(36)}`.slice(-4);
}

Expand Down Expand Up @@ -234,7 +233,6 @@ export function delay(ms) {

export function isSrcAsDataUrl(text) {
const DATA_URL_REGEX = /url\(['"]?(data:)([^'"]+?)['"]?\)/;

return text.search(DATA_URL_REGEX) !== -1;
}

Expand Down Expand Up @@ -352,3 +350,28 @@ export function getAndEncode(url, options) {
}
});
}

export function concatAndResolveUrl(base, url) {
return new URL(url, base).href;
}

// Set relative URL in stylesheet to absolute url
export function setStyleSheetBaseHref(text, base) {
function addBaseHrefToUrl(match, p1) {
const url = /^http/i.test(p1) ? p1 : concatAndResolveUrl(base, p1);
return `url('${url}')`;
}
return isSrcAsDataUrl(text)
? text
: text.replace(/url\(['"]?([^'"]+?)['"]?\)/g, addBaseHrefToUrl);
}

export function toStyleSheet(text) {
const doc = document.implementation.createHTMLDocument('');
const styleElement = document.createElement('style');

styleElement.textContent = text;
doc.body.appendChild(styleElement);

return styleElement.sheet;
}
101 changes: 101 additions & 0 deletions test/node/utils/dom-to-image.js
@@ -0,0 +1,101 @@
// Copyright (c) 2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import test from 'tape';
import {setStyleSheetBaseHref} from '../../../src/utils/src/dom-utils';

const MOCK_CSS = `div#header {
background-image: url('images/header-background.jpg');
}`;
const EXPECTED_CSS = `div#header {
background-image: url('http://mock.kepler.com/js/v1.1.1/images/header-background.jpg');
}`;
const MOCK_CSS_1 = `div#header {
background-image: url('./images/header-background.jpg');
}`;
const EXPECTED_CSS_1 = `div#header {
background-image: url('http://mock.kepler.com/js/v1.1.1/images/header-background.jpg');
}`;
const MOCK_CSS_2 = `div#header {
background-image: url('../images/header-background.jpg');
}`;
const EXPECTED_CSS_2 = `div#header {
background-image: url('http://mock.kepler.com/js/images/header-background.jpg');
}`;

const MOCK_CSS_3 = `@font-face {
src: url("data:application/font-woff;base64,d09GRg");
}`;
const MOCK_CSS_4 = `div#header {
background-image: url('http://mock.kepler.com/js/images/header-background.jpg');
}`;
const BASE_HREF = 'http://mock.kepler.com/js/v1.1.1/main.css';

test('dom-to-image: setStyleSheetBaseHref', t => {
const TEST_CASES = [
{
args: {
text: MOCK_CSS,
base: BASE_HREF
},
expected: EXPECTED_CSS,
msg: 'should replace relative path'
},
{
args: {
text: MOCK_CSS_1,
base: BASE_HREF
},
expected: EXPECTED_CSS_1,
msg: 'should replace relative path'
},
{
args: {
text: MOCK_CSS_2,
base: BASE_HREF
},
expected: EXPECTED_CSS_2,
msg: 'should replace relative path'
},
{
args: {
text: MOCK_CSS_3,
base: BASE_HREF
},
expected: MOCK_CSS_3,
msg: 'should not change data url'
},
{
args: {
text: MOCK_CSS_4,
base: BASE_HREF
},
expected: MOCK_CSS_4,
msg: 'should not change absolute url'
}
];

TEST_CASES.forEach(tc => {
const result = setStyleSheetBaseHref(tc.args.text, tc.args.base);
t.equal(result, tc.expected, tc.msg);
});

t.end();
});
1 change: 1 addition & 0 deletions test/node/utils/index.js
Expand Up @@ -39,3 +39,4 @@ import './editor-utils-test';
import './kepler-gl-utils-test';
import './timeline-test';
import './composer-helpers-test';
import './dom-to-image';

0 comments on commit c79e9f9

Please sign in to comment.