UNPKG

jsf.js_next_gen

Version:

A next generation typescript reimplementation of jsf.js

306 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExtConfig = exports.ExtDQ = exports.ExtDomQuery = void 0; /*! Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const mona_dish_1 = require("mona-dish"); const Const_1 = require("../core/Const"); /** * detects whether a source is a faces.js request * * @param source the source string for the faces.js request * @return true if a faces.js loading pattern is detected * @constructor */ const IS_FACES_SOURCE = (source) => { //spec version smaller 4 we have to deal with the jsf namespace return source && !!((source === null || source === void 0 ? void 0 : source.search(/\/jakarta\.faces\.resource.*\/faces\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/faces-development\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/faces-uncompressed\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/faces[^.]*\.js.*ln=jakarta.faces.*/gi)) != -1 || //fallback without check for jsf, that way we allow both bookmarks (source === null || source === void 0 ? void 0 : source.search(/\/javax\.faces\.resource.*\/jsf\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/jsf-development\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/jsf-uncompressed\.js.*/)) != -1 || (source === null || source === void 0 ? void 0 : source.search(/\/jsf[^.]*\.js.*ln=javax.faces.*/gi)) != -1); }; /** * namespace myfaces\.testscripts can be used as extension point for internal * tests, those will be handled similarly to faces.js, in regard * to reload blocking on ajax requests * * Note: atm not used, used to be used in the old implementation * but still is reserved for now * * @param source the source to check * @constructor */ const IS_INTERNAL_SOURCE = (source) => { return source.search(/\/faces[^.]*\.js.*ln=myfaces.testscripts.*/gi) != -1 || source.search(/\/jsf[^.]*\.js.*ln=myfaces.testscripts.*/gi) != -1; }; const ATTR_SRC = 'src'; /** * Extension which adds implementation specific * meta-data to our dom query * * Usage * el = new ExtDQ(oldReference) * nonce = el.nonce * windowId = el.getWindowId */ class ExtDomQuery extends mona_dish_1.DQ { static get windowId() { return new ExtDomQuery(document.body).windowId; } static get nonce() { return new ExtDomQuery(document.body).nonce; } get windowId() { const fetchWindowIdFromURL = function () { let href = window.location.href; let windowId = "windowId"; let regex = new RegExp("[\\?&]" + windowId + "=([^&#\\;]*)"); let results = regex.exec(href); //initial trial over the url and a regexp if (results != null) return results[1]; return null; }; //byId ($) if (this.value.isPresent()) { let result = this.querySelectorAll("form input[name='" + Const_1.P_WINDOW_ID + "']"); if (result.length > 1) { throw Error("Multiple different windowIds found in document"); } return mona_dish_1.Optional.fromNullable((result.isPresent()) ? result.getAsElem(0).value.value : fetchWindowIdFromURL()); } else { return mona_dish_1.Optional.fromNullable(fetchWindowIdFromURL()); } } /* * determines the faces.js nonce and adds them to the namespace * this is done once and only lazily */ get nonce() { var _a; //already processed let myfacesConfig = new ExtConfig(window.myfaces); let globalNonce = myfacesConfig.getIf("config", "cspMeta", "nonce"); if (!!globalNonce.value) { return mona_dish_1.ValueEmbedder.fromNullable(globalNonce); } let curScript = new mona_dish_1.DQ(document.currentScript); //since our baseline atm is ie11 we cannot use document.currentScript globally let nonce = curScript.nonce; if (nonce.isPresent()) { // fast-path for modern browsers return mona_dish_1.ValueEmbedder.fromNullable(nonce); } // fallback if the currentScript method fails, we just search the jsf tags for nonce, this is // the last possibility let nonceScript = mona_dish_1.Optional.fromNullable((_a = mona_dish_1.DQ .querySelectorAll("script[src], link[src]").asArray .filter((item) => item.nonce.isPresent() && item.attr(ATTR_SRC) != null) .filter(item => IS_FACES_SOURCE(item.attr(ATTR_SRC).value))) === null || _a === void 0 ? void 0 : _a[0]); if (!(nonceScript === null || nonceScript === void 0 ? void 0 : nonceScript.value)) { return mona_dish_1.ValueEmbedder.absent; } return new mona_dish_1.DomQuery(nonceScript.value).nonce; } static searchJsfJsFor(item) { return new ExtDomQuery(document).searchJsfJsFor(item); } /** * searches the embedded faces.js for items like separator char etc. * expects a match as variable under position 1 in the result match * @param regExp */ searchJsfJsFor(regExp) { var _a; //perfect application for lazy stream return mona_dish_1.Optional.fromNullable((_a = mona_dish_1.DQ.querySelectorAll("script[src], link[src]").asArray .filter(item => IS_FACES_SOURCE(item.attr(ATTR_SRC).value)) .map(item => item.attr(ATTR_SRC).value.match(regExp)) .filter(item => item != null && item.length > 1) .map((result) => { return decodeURIComponent(result[1]); })) === null || _a === void 0 ? void 0 : _a[0]); } globalEval(code, nonce) { return new ExtDomQuery(super.globalEval(code, nonce !== null && nonce !== void 0 ? nonce : this.nonce.value)); } // called from base class runScripts, do not delete // noinspection JSUnusedGlobalSymbols globalEvalSticky(code, nonce) { return new ExtDomQuery(super.globalEvalSticky(code, nonce !== null && nonce !== void 0 ? nonce : this.nonce.value)); } /** * decorated run scripts which takes our jsf extensions into consideration * (standard DomQuery will let you pass anything) * @param sticky if set to true the internally generated element for the script is left in the dom * @param whiteListed */ runScripts(sticky = false, whiteListed) { const whitelistFunc = (src) => { var _a; return ((_a = whiteListed === null || whiteListed === void 0 ? void 0 : whiteListed(src)) !== null && _a !== void 0 ? _a : true) && !IS_FACES_SOURCE(src) && !IS_INTERNAL_SOURCE(src); }; return super.runScripts(sticky, whitelistFunc); } /** * adds the elements in this ExtDomQuery to the head * * @param suppressDoubleIncludes checks for existing elements in the head before running the insert */ runHeadInserts(suppressDoubleIncludes = true) { let head = ExtDomQuery.byId(document.head); //automated nonce handling let processedScripts = []; // the idea is only to run head inserts on resources // which do not exist already, that way // we can avoid double includes on subsequent resource // requests. function resourceIsNew(element) { if (!suppressDoubleIncludes) { return true; } const tagName = element.tagName.value; if (!tagName) { // text node they do not have tag names, so we can process them as they are without // any further ado return true; } let reference = element.attr("href") .orElseLazy(() => element.attr("src").value) .orElseLazy(() => element.attr("rel").value); if (!reference.isPresent()) { return true; } return !head.querySelectorAll(`${tagName}[href='${reference.value}']`).length && !head.querySelectorAll(`${tagName}[src='${reference.value}']`).length && !head.querySelectorAll(`${tagName}[rel='${reference.value}']`).length; } this .filter(resourceIsNew) .each(element => { if (element.tagName.value != "SCRIPT") { //we need to run runScripts properly to deal with the rest new ExtDomQuery(...processedScripts).runScripts(true); processedScripts = []; head.append(element); } else { processedScripts.push(element); } }); new ExtDomQuery(...processedScripts).runScripts(true); } /** * byId producer * * @param selector id * @param deep whether the search should go into embedded shadow dom elements * @return a DomQuery containing the found elements */ static byId(selector, deep = false) { var _a, _b, _c; const ret = mona_dish_1.DomQuery.byId(selector, deep); if ((0, Const_1.$faces)().getProjectStage().toLowerCase() == "development" && (window === null || window === void 0 ? void 0 : window.console) && ret.isAbsent() && selector) { let identifier = (_c = (_b = (_a = selector === null || selector === void 0 ? void 0 : selector.id) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : selector === null || selector === void 0 ? void 0 : selector.id) !== null && _c !== void 0 ? _c : selector.toString(); console.error("Element " + identifier + "not found"); } return new ExtDomQuery(ret); } filter(func) { return new ExtDomQuery(super.filter(func)); } } exports.ExtDomQuery = ExtDomQuery; exports.ExtDQ = ExtDomQuery; /** * in order to reduce the number of interception points for the fallbacks we add * the namespace remapping straight to our config accessors */ class ExtConfig extends mona_dish_1.Config { constructor(root) { super(root); this.$nspEnabled = true; } assignIf(condition, ...accessPath) { const accessPathMapped = this.remap(accessPath); return super.assignIf(condition, ...accessPathMapped); } assign(...accessPath) { const accessPathMapped = this.remap(accessPath); return super.assign(...accessPathMapped); } append(...accessPath) { return super.append(...accessPath); } appendIf(condition, ...accessPath) { const accessPathMapped = this.remap(accessPath); return super.appendIf(condition, ...accessPathMapped); } getIf(...accessPath) { const accessPathMapped = this.remap(accessPath); return super.getIf(...accessPathMapped); } get(defaultVal) { return super.get((0, Const_1.$nsp)(defaultVal)); } delete(key) { return super.delete((0, Const_1.$nsp)(key)); } /** * creates a config from an initial value or null * @param value */ static fromNullable(value) { return new ExtConfig(value); } getClass() { return ExtConfig; } /** * shallow copy getter, copies only the first level, references the deeper nodes * in a shared manner */ shallowCopy$() { const ret = super.shallowCopy$(); return new ExtConfig(ret); } /** * deep copy, copies all config nodes */ get deepCopy() { return new ExtConfig(super.deepCopy$()); } /** * helper to remap the namespaces of an array of access paths * @param accessPath the access paths to be remapped * @private returns an array of access paths with version remapped namespaces */ remap(accessPath) { if (!this.$nspEnabled) { return accessPath; } return new mona_dish_1.Es2019Array(...accessPath).map(key => (0, Const_1.$nsp)(key)); } } exports.ExtConfig = ExtConfig; //# sourceMappingURL=ExtDomQuery.js.map