@aappddeevv/dynamics-client-ui
Version:
## What is it? A library to help you create great dynamics applications.
356 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const R = require("ramda");
tslib_1.__exportStar(require("./getXrmP"), exports);
const BuildSettings_1 = require("BuildSettings");
/**
* Get a URL parameter from `search` or `document.loction.search` by
* default. This is useful for obtaining the "data" parameter from the URL
* that is passed in from a form if you set the WebResource url properties.
* Sometimes the API does not seem to work, but this seems to always work.
*/
function getURLParameter(name, search = document.location.search) {
search = search || "";
const r = new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(search);
const r2 = ["", ""];
return decodeURIComponent((r || r2)[1].replace(/\+/g, "%20")) || null;
}
exports.getURLParameter = getURLParameter;
/** Generate a unique id with an optional prefix. */
function generateId(prefix = "") {
return `${prefix}-${uuidv4()}`;
}
exports.generateId = generateId;
const dec2hex = [];
for (let i = 0; i <= 15; i++) {
dec2hex[i] = i.toString(16);
}
const UUID = () => {
let uuid = "";
for (let i = 1; i <= 36; i++) {
if (i === 9 || i === 14 || i === 19 || i === 24) {
uuid += "-";
}
else if (i === 15) {
uuid += 4;
}
else if (i === 20) {
uuid += dec2hex[(Math.random() * 4 | 0 + 8)];
}
else {
uuid += dec2hex[(Math.random() * 15 | 0)];
}
}
return uuid;
};
/** Probably need something multi-browser friendly here. */
//export function uuidv4(): string {
// return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
// (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
// )
//}
function uuidv4() {
return UUID();
}
exports.uuidv4 = uuidv4;
/** Internal to this module. ... */
function cleanId(id) {
if (typeof id === "undefined" || id === null)
throw Error(`Unable to clean nil id ${id}`);
return id.toString().replace(/[{}]/g, "").toLowerCase();
}
/** Uses internal API. */
function isUci(xrm) {
if (xrm.Internal && xrm.Internal.isUci)
return xrm.Internal.isUci();
return false;
}
exports.isUci = isUci;
let _isElectron = false;
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf(" electron/") > -1) {
_isElectron = true;
}
/** Return true if we are running inside electron. */
function isElectron() {
return _isElectron;
}
exports.isElectron = isElectron;
/**
* Checks the form context to see if there is an entity id. If not,
* it's probably a new form.
*/
function hasEntityId(xrm) {
if (!xrm)
return false;
const id = entityIdOrNull(xrm);
if (id)
return true;
return false;
}
exports.hasEntityId = hasEntityId;
/** Return the entity id on the page context or null. Braces removed. */
function entityIdOrNull(xrm) {
if (!xrm)
return null;
const e = eaccess(xrm);
if (e)
return cleanId(e.getId());
return null;
}
exports.entityIdOrNull = entityIdOrNull;
const eaccess = R.pathOr(null, ["Page", "data", "entity"]);
/**
* After a save event, run actionToTake if ready returns ture. Uses polling.
* Once actionToTake is run, the polling is removed. An exception thrown in
* in ready is considered a false return.
* @param xrm Xrm to attach to Xrm.Page.data.entity.addOnSave/removeOnSave
* @param ready Return true if the condition to run actionToTake has been met.
* @param actionToTake The action to take.
* @param pollInterval The interval to poll that ready is true after the save has occurred.
* @return A cancellable thunk.
*/
function runAfterSave(xrm, ready, actionToTake, pollInterval = 500) {
let cancellable = null;
const onSaveHandler = (ctx) => {
const keepChecking = () => {
try {
const fire = ready(xrm);
if (fire) {
xrm.Page.data.entity.removeOnSave(onSaveHandler);
clearInterval(cancellable);
actionToTake(xrm);
}
}
catch (e) {
// do nothing
console.log("Ready check failed", e);
}
};
cancellable = setInterval(keepChecking, pollInterval);
};
xrm.Page.data.entity.addOnSave(onSaveHandler);
return () => xrm.Page.data.entity.removeOnSave(onSaveHandler);
}
exports.runAfterSave = runAfterSave;
/** Render a null, which in react means no rendering. */
exports.RenderNothing = () => null;
/** Do nothing. */
function noop() { }
exports.noop = noop;
/**
* If cb is a function, return it, otherwise noop.
* @param cb Callback
*/
function callbackOrNoop(cb) {
return typeof cb === "function" ? cb : noop;
}
exports.callbackOrNoop = callbackOrNoop;
/**
* If arg is an array, return the first element if it exists,
* otherwise, return other.
*/
function firstOrElse(arg, other) {
arg = Array.isArray(arg) ? arg[0] : arg;
if (R.isNil(arg) && other)
return other;
else
return arg;
}
exports.firstOrElse = firstOrElse;
/** Find the first undefined array element or return undefined. */
function firstUndefined(...args) {
if (!Array.isArray(args))
return undefined;
return args.find(a => typeof a !== "undefined");
}
exports.firstUndefined = firstUndefined;
/**
* Execute fns with the same args in order until
* `event.preventDefault()` is called. This is really
* just a takeWhile and map where "event" is mutable state.
*/
function composeEventHandlers(...fns) {
return (event, ...args) => {
fns.some(fn => {
fn && fn(event, ...args);
return event.defaultPrevented;
});
};
}
exports.composeEventHandlers = composeEventHandlers;
/** Per downshift, (p)react. */
function isDOMElement(el) {
if (el) {
if (el.nodeName)
return typeof el.nodeName === "string";
else
return (typeof el.type === "string" ||
typeof el.type === "function");
}
return false;
}
exports.isDOMElement = isDOMElement;
/** Return true if its a number. */
function isNumber(thing) {
// eslint-disable-next-line no-self-compare
return thing === thing && typeof thing === "number";
}
exports.isNumber = isNumber;
/**
* Get props for (p)react.
*/
function getElementProps(element) {
return element.props || element.attributes;
}
exports.getElementProps = getElementProps;
/**
* Return the parent's Xrm from window.parent.Xrm or window.Xrm.
* No check to see if Xrm.Page.data is present as that is deprecated.
* This is a strict value check, no async.
*
* @see getXrmForEntity
*/
function getXrm() {
return window.parent.Xrm || window.Xrm;
}
exports.getXrm = getXrm;
/**
* Return the global context from GetGlobalContext(), then
* Xrm.Utility.getGlobalContext() then Xrm.Page.context.
* Throws Error if not found.
*
* @see https://msdn.microsoft.com/pt-pt/library/af74d417-1359-4eaa-9f87-5b33a8852e83(v=crm.7)
*/
function getGlobalContext() {
var errorMessage = "Context is not available.";
if (typeof GetGlobalContext !== "undefined") {
return GetGlobalContext();
}
else {
if (typeof Xrm !== "undefined") {
if (typeof Xrm.Utility !== "undefined" &&
typeof Xrm.Utility.getGlobalContext !== "undefined") {
return Xrm.Utility.getGlobalContext();
}
// Try this...
return Xrm.Page.context;
}
else {
throw new Error(errorMessage);
}
}
}
exports.getGlobalContext = getGlobalContext;
/**
* Walk the window chain looking for Xrm with Xrm.Page.data attribute being
* non-null. Return null if not found. It will walk the window hierarchy
* as well as test some well known locations of Xrm.
*
* @see getXrm
*/
function getXrmForEntity() {
const window = walkParents({
select: (w) => R.pathOr(false, ["Xrm", "Page", "data"], w)
});
if (window)
return window.Xrm;
const maybeXrm = getXrm();
if (R.pathOr(false, ["Page", "data"], maybeXrm))
return maybeXrm;
return null;
}
exports.getXrmForEntity = getXrmForEntity;
/**
* Run a thunk up the window chain. Return the last window visited if it meets
* select criteria (if provided) or if select returns true for a particular
* window. Return null otherwise. Thunk is usually used for logging.
*/
function walkParents({ thunk, select, max }) {
max = max || 10;
let current = window; // this window
while (current && max > 0 && !select(current)) {
if (thunk)
thunk(current);
max = max - 1;
if (current.parent === current)
current = null;
else
current = current.parent;
}
if (select(current))
return current;
return null;
}
exports.walkParents = walkParents;
/**
* Try to get entityid, userid, entity name, entity type code (number) from a
* variety of places including the Xrm values and the URL parameters in the
* document that the function is called from. Varibles that are found are
* returned but if something is not found it is not returned. If its a new
* entity, obviously, the entityid will not be present. Return an object with
* {userId, entityId, entityName, entityTypeCode}. Note that entityTypeCode
* is specific to an organization so do not use it if you can avoid it.
*
* Should typecode be number or string?
*/
function getEntityInfo(xrm) {
const x = xrm || getXrmForEntity();
const context = R.pathOr(null, ["Page", "context"], x);
const entity = R.pathOr(null, ["Page", "data", "entity"], x);
const etn = getURLParameter("etn");
const typename = getURLParameter("typename");
const etc = context ? parseInt(context.getQueryStringParameters().etc) : null;
const eid = (entity && entity.getId()) || getURLParameter("id") || null;
const uid = (context && context.getUserId()) || null;
const ename = entity ? entity.getEntityName() :
(etn ? etn :
(typename ? typename : null));
const tcode = etc;
const rval = Object.assign({}, (uid ? { userId: cleanId(uid) } : {}), (eid ? { entityId: cleanId(eid) } : {}), (ename ? { entityName: ename } : {}), (tcode ? { entityTypeCode: tcode } : {}));
return rval;
}
exports.getEntityInfo = getEntityInfo;
/**
* Access page context to return form type.
* @deprecated Use XRM members directly.
*/
function getFormType(xrm) {
const v = xrm.Page.ui.getFormType();
// check range???
return v;
}
exports.getFormType = getFormType;
/** If create form, checks formtype and whether there is an id. */
function isCreateForm(xrm) {
return getFormType(xrm) === 1 /* Create */ || !eaccess(xrm).getId();
}
exports.isCreateForm = isCreateForm;
/**
* Load scripts programmatically. The script is evaluated once loaded by the browser/host.
*/
function loadScripts(scripts, callback, targetDoc = document) {
const loader = (src, handler) => {
if (BuildSettings_1.DEBUG)
console.log("Programmatically loading: " + src);
const script = targetDoc.createElement("script");
script.src = src;
script.onload = () => {
// remove onload handler??
handler();
};
const head = targetDoc.getElementsByTagName("head")[0];
(head || targetDoc.body).appendChild(script);
};
// Run on each script...
(function run() {
if (scripts.length > 0) {
loader(scripts.shift(), run);
}
else if (callback)
callback();
})();
}
exports.loadScripts = loadScripts;
//# sourceMappingURL=Utils.js.map