jsf.js_next_gen
Version:
A next generation typescript reimplementation of jsf.js
306 lines • 13 kB
JavaScript
;
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