"use strict"; // ------------------------------------------------------------- // WARNING: this file is used by both the client and the server. // Do not use any browser or node-specific API! // ------------------------------------------------------------- /* eslint hammerhead/proto-methods: 2 */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const regexp_escape_1 = __importDefault(require("../utils/regexp-escape")); const internal_attributes_1 = __importDefault(require("../processing/dom/internal-attributes")); const url_1 = require("../utils/url"); const attributes_1 = require("./dom/attributes"); const dom_1 = __importDefault(require("./dom")); const SOURCE_MAP_RE = /\/\*\s*[#@]\s*sourceMappingURL\s*=[\s\S]*?\*\/|\/\/[\t ]*[#@][\t ]*sourceMappingURL[\t ]*=.*/ig; const CSS_URL_PROPERTY_VALUE_RE = /(url\s*\(\s*(['"]?))([^\s]*?)(\2\s*\))|(@import\s+(['"]))([^\s]*?)(\6)/g; const ATTRIBUTE_SELECTOR_RE = /((?:(\W?)(\w+))?\[\s*)(\w+)(\s*\^?=)/g; const STYLESHEET_PROCESSING_START_COMMENT = '/*hammerhead|stylesheet|start*/'; const STYLESHEET_PROCESSING_END_COMMENT = '/*hammerhead|stylesheet|end*/'; const HOVER_PSEUDO_CLASS_RE = /:\s*hover(\W)/gi; const PSEUDO_CLASS_RE = new RegExp(`\\[${internal_attributes_1.default.hoverPseudoClass}\\](\\W)`, 'ig'); const IS_STYLE_SHEET_PROCESSED_RE = new RegExp(`\\s*${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_START_COMMENT)}`, 'gi'); const STYLESHEET_PROCESSING_COMMENTS_RE = new RegExp(`${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_START_COMMENT)}\n?|` + `\n?${(0, regexp_escape_1.default)(STYLESHEET_PROCESSING_END_COMMENT)}\\s*`, 'gi'); class StyleProcessor { constructor() { this.STYLESHEET_PROCESSING_START_COMMENT = STYLESHEET_PROCESSING_START_COMMENT; this.STYLESHEET_PROCESSING_END_COMMENT = STYLESHEET_PROCESSING_END_COMMENT; this.proxyless = false; } process(css, urlReplacer, shouldIncludeProcessingComments) { if (!css || typeof css !== 'string' || shouldIncludeProcessingComments && IS_STYLE_SHEET_PROCESSED_RE.test(css)) return css; // NOTE: Replace the :hover pseudo-class. css = css.replace(HOVER_PSEUDO_CLASS_RE, '[' + internal_attributes_1.default.hoverPseudoClass + ']$1'); // NOTE: Remove all 'source map' directives. css = css.replace(SOURCE_MAP_RE, ''); // NOTE: Replace URLs in CSS rules with proxy URLs. if (!this.proxyless) css = this._replaceStylesheetUrls(css, urlReplacer); // NOTE: Replace url attributes to stored attributes css = this._replaceUrlAttributes(css); if (shouldIncludeProcessingComments) css = `${STYLESHEET_PROCESSING_START_COMMENT}\n${css}\n${STYLESHEET_PROCESSING_END_COMMENT}`; return css; } cleanUp(css, parseProxyUrl) { if (typeof css !== 'string') return css; css = css .replace(PSEUDO_CLASS_RE, ':hover$1') .replace(internal_attributes_1.default.storedAttrPostfix, ''); css = this._removeStylesheetProcessingComments(css); if (!this.proxyless) { css = this._replaceStylesheetUrls(css, (url) => { const parsedProxyUrl = parseProxyUrl(url); return parsedProxyUrl ? parsedProxyUrl.destUrl : url; }); } return css; } _removeStylesheetProcessingComments(css) { const parts = css.split(STYLESHEET_PROCESSING_COMMENTS_RE); const stylesheetPartsFound = parts.length >= 3; if (!stylesheetPartsFound) return css; for (let i = 0; i < parts.length; i += 2) { let whiteSpaceCount = 0; // NOTE: search for whitespaces from the end of the string // we do not use /\s*$/ regex intentionally to improve performance for (let j = parts[i].length - 1; j >= 0; j--) { if (!(/\s/.test(parts[i][j]))) // eslint-disable-line @typescript-eslint/no-extra-parens break; whiteSpaceCount++; } parts[i] = parts[i].substring(0, parts[i].length - whiteSpaceCount); } return parts.join(''); } _replaceStylesheetUrls(css, processor) { return css.replace(CSS_URL_PROPERTY_VALUE_RE, (match, prefix1, _q1, url1, postfix1, prefix2, _q2, url2, postfix2) => { const prefix = prefix1 || prefix2; const url = url1 || url2; const processedUrl = (0, url_1.isSpecialPage)(url) ? url : processor(url); const postfix = postfix1 || postfix2; return url ? prefix + processedUrl + postfix : match; }); } _replaceUrlAttributes(css) { return css.replace(ATTRIBUTE_SELECTOR_RE, (match, prefix, prev, possibleTag, attribute, postfix) => { const tagName = prev === '.' || prev === '#' ? '' : possibleTag; if (!tagName || !attributes_1.URL_ATTR_TAGS[attribute] || attributes_1.URL_ATTR_TAGS[attribute].indexOf(tagName) === -1) return match; return prefix + dom_1.default.getStoredAttrName(attribute) + postfix; }); } } exports.default = new StyleProcessor();module.exports = exports.default;