UNPKG

@easyquery/enterprise

Version:

EasyQuery.JS Enterprise

1,230 lines (1,217 loc) 45.7 kB
'use strict'; var core = require('@easyquery/core'); var core$1 = require('@easydata/core'); var ui = require('@easydata/ui'); const majVer = 7; const minVer = 4; const magics = [45, 128, 231]; Math.trunc = Math.trunc || function (x) { if (isNaN(x)) { return NaN; } if (x > 0) { return Math.floor(x); } return Math.ceil(x); }; const lcv = { __kt: 0, versionNum: '' + majVer + '.' + minVer, ck: function (key) { if (!key || !key.length) { this.__kt = -3; return; } try { var part1 = key.slice(0, key.length - 12); var infoBytes = _bs(part1); var part2 = key.slice(key.length - 12); let ism = false; for (const magic of magics) { var edc = t36(magic); if (part2[0] === edc[0] && part2[2] === edc[1]) { ism = true; break; } } if (!ism) { lcv.__kt = -1; } var idx = minVer % 4; var shift = majVer + minVer; if (part2[6 + idx] != encChar(shift, 19)) { lcv.__kt = -2; return; } if (part2[1] != encChar(infoBytes[3])) { return; } lcv.__kt = part2[3] != encChar(infoBytes[5]) ? 2 : 3; if (lcv.__kt === 2) { var eds = `${f36(part2.substring(4, 6) + part2.substring(10))}`; var ed = new Date(2000 + Number.parseInt(eds.substring(0, 2)), Number.parseInt(eds.substring(2, 4)) - 1, Number.parseInt(eds.substring(4)), 0, 0, 0, 0); if (Date.now() > ed.getTime()) { lcv.__kt = 1; } } } catch (error) { } } }; function encChar(x, b = 20) { return String.fromCharCode(65 + x % b); } function _bs(str) { var binary_string = urlatob(str); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes; } function urlatob(str) { var s = str; s = s.replace(/-/g, '+'); s = s.replace(/_/g, '/'); switch (s.length % 4) { case 0: break; case 2: s += "=="; break; case 3: s += "="; break; } return window.atob(s); } const tokens = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; function t36(val) { var result = ""; while (val > 0) { result = tokens[val % tokens.length] + result; val /= tokens.length; } return result; } function f36(val) { var d = 0; for (var i = 0; i < val.length; ++i) d += tokens.indexOf(val[i]) * Math.pow(tokens.length, val.length - i - 1); return d; } class ConditionEx extends core.Condition { constructor() { super(...arguments); this.readOnly = false; this.parameterized = false; this.inJoin = false; } isReadOnly() { return this.readOnly || (this.getParent() && this.getParent().isReadOnly()); } setReadOnly(value) { this.readOnly = value || (this.getParent() && this.getParent().isReadOnly()); this.conditions && this.conditions.forEach((cond) => { cond.setReadOnly(value); }); } isParameterized() { return this.parameterized; } setParameterized(value) { this.parameterized = value; } isInJoin() { return this.inJoin; } setInJoin(value) { this.inJoin = value; } loadFromData(model, data) { super.loadFromData(model, data); if (lcv.__kt <= 1) return; if (typeof data.readOnly !== 'undefined') { this.setReadOnly(data.readOnly); } if (typeof data.inJoin !== 'undefined') { this.setInJoin(data.inJoin); } if (typeof data.parameterized !== 'undefined') { this.setParameterized(data.parameterized); } //if condition group //this.linkType = data.linking; if (data.conds) { for (let i = 0; i < data.conds.length; i++) { let newCond = this.query.createCondition(); newCond.loadFromData(model, data.conds[i]); this.conditions.push(newCond); newCond.setParent(this); } } } saveToData() { let obj = super.saveToData(); if (lcv.__kt <= 1) return obj; if (this.isReadOnly()) { obj.readOnly = this.isReadOnly(); } if (this.isParameterized()) { obj.parameterized = this.isParameterized(); } if (this.isInJoin()) { obj.inJoin = this.isInJoin(); } if (this.tag == core.CondTag.Group) { //if condition group //obj.linking = this.linkType; obj.conds = []; for (let i = 0; i < this.conditions.length; i++) { obj.conds.push(this.conditions[i].saveToData()); } } return obj; } } class QueryColumnEx extends core.QueryColumn { constructor() { super(...arguments); this.readOnly = false; } isReadOnly() { return this.readOnly; } setReadOnly(value) { this.readOnly = value; } /** * Loads column from its JSON representation object. * @param model The Data Model. * @param colData The JSON representation object. */ loadFromData(model, colData) { super.loadFromData(model, colData); if (lcv.__kt <= 2) return; if (colData) { if (typeof colData.readOnly != 'undefined') { this.readOnly = colData.readOnly; } } } saveToData() { let colData = super.saveToData(); if (lcv.__kt <= 2) return colData; if (this.isReadOnly()) { colData.readOnly = this.isReadOnly(); } return colData; } } class QueryEx extends core.Query { constructor(model, options) { super(model, null, options); } isEx() { return lcv.__kt > 1; } createSubQuery() { return new QueryEx(this.model, { context: this.context }); } createCondition(tag = core.CondTag.Simple) { if (lcv.__kt <= 1) return super.createCondition(tag); return new ConditionEx(this, tag); } createColumn(justsorted = false) { if (lcv.__kt <= 1) return super.createColumn(justsorted); return new QueryColumnEx(this, justsorted); } /** * Adds a condition group - a group of simple conditions (predicates) linked by AND or OR * @param descriptor The descriptor of the new condition group. * @returns The new search condition. */ addConditionGroup(descriptor, addChildCondition = true) { if (lcv.__kt <= 1) return super.addConditionGroup(descriptor, addChildCondition); let parent = descriptor.parent || this.getRootCondition(); let lType = descriptor.linking || (parent.linkType === core.LinkType.All ? core.LinkType.Any : core.LinkType.All); let group = this.createCondition(core.CondTag.Group); group.linkType = lType; if (addChildCondition) { let model = this.getModel(); let attr = model.getFirstUICAttr(); if (attr) { let op = model.getDefaultOperatorForAttr(attr); let cond = this.createSimpleConditionObject(attr, op, ""); group.addCondition(cond); } } parent.addCondition(group); return group; } addExtraConditionGroup(descriptor, addChildCondition = true) { descriptor.parent = descriptor.parent || this.extraConditions; if (lcv.__kt <= 1) return super.addExtraConditionGroup(descriptor, addChildCondition); return this.addConditionGroup(descriptor); } buildQueryPath() { const model = this.getModel(); if (lcv.__kt <= 1) return this.buildQueryPath(); if (model.getMainEntity()) return this.buildQueryPath(); const entities = this.getEntitiesInQuery(); if (entities.length == 0) { throw Error("Cannon build path. Path is empty"); } let tree = new core.Tree(entities[0]); for (let i = 1; i < entities.length; i++) { const entity = entities[i]; if (tree.contains(entity)) { continue; } let path = this.findPath(tree.value, entity); if (path != null) { //Add path to tree tree = this.addPathToTree(tree, (path[0] === tree.value) ? path.slice(1) : path); } else { throw Error("Unable to build query. Cannot build path."); } } tree.setParents(); return tree; } addPathToTree(tree, path) { if (path.length == 0) { return; } let pathAdded = false; for (let child of tree.childs) { if (child.value === path[0]) { pathAdded = true; if (path.length > 1) child = this.addPathToTree(child, path.slice(1)); break; } } if (!pathAdded) { let link = this.getModel().findLink(tree.value, path[0]); if (link) { let newNode = new core.Tree(path[0]); tree.addChild(newNode); if (path.length > 1) newNode = this.addPathToTree(newNode, path.slice(1)); } else if (!tree.parent) { let node = new core.Tree(tree); tree = new core.Tree(path[0]); tree.addChild(node); if (path.length > 1) tree = this.addPathToTree(tree, path.slice(1)); } else { throw Error("Unable to build query. Cannot build path."); } } return tree; } findPath(entityFrom, entityTo) { let curPath = []; let curStep = []; let nextStep = []; let passedEntities = []; let pathFound = false; curPath.push(entityFrom); curStep.push(curPath); passedEntities.push(entityFrom); while (!pathFound) { nextStep = []; for (let path of curStep) { pathFound = this.checkPath(path, entityTo, nextStep, passedEntities); if (pathFound) { path.push(entityTo); curPath = path; break; } } curStep = []; if (!pathFound) { if (nextStep.length == 0) return null; //copy nextStep to curStep for (let path of nextStep) curStep.push(path); } } return curPath; } checkPath(path, entityTo, nextStep, passedEntities) { const endPoint = path[path.length - 1]; const entityLinks = this.getModel().getLinksByEntity(endPoint); for (let link of entityLinks) { let entity2 = null; if (link.entityFrom == endPoint) { entity2 = link.entityTo; } else { entity2 = link.entityFrom; } if (entity2 == entityTo) { //if reach the destination nextStep = []; return true; } //if destination is not reached yet we add current node (entity2) to the path and proceed if (path.indexOf(entity2) < 0 && passedEntities.indexOf(entity2) < 0) { //to prevent cycling passedEntities.push(entity2); let newPath = new Array(); newPath = newPath.concat(path); newPath.push(entity2); nextStep.push(newPath); } } return false; } } class EqServerQueryStorage { constructor(context) { this.context = context; } init() { } newQuery(options) { options = options || {}; const query = options.query || this.context.getQuery(); const model = this.context.getModel(); const modelId = options.modelId || (model ? model.getId() : ""); if (options.queryId) { query.setId(options.queryId); } if (options.name) { query.setName(options.name); } if (options.description) { query.setDescription(options.description); } let requestData = { 'query': query.toJSONData() }; if (options.data) { requestData['data'] = options.data; } const url = this.context.resolveEndpoint('NewQuery', { modelId: modelId }); const http = this.context.getServices().getHttpClient(); return http.post(url, requestData) .then((responseData) => { return responseData.query; }); } getQueryList(options) { options = options || {}; const modelId = options.modelId || this.context.getModel().getId(); const url = this.context.resolveEndpoint('GetQueryList', { modelId: modelId }); const http = this.context.getServices().getHttpClient(); return http.get(url) .then((responseData) => { if (!responseData.queries) { return Promise.reject({ message: 'Wrong response format' }); } return responseData.queries; }); } loadQuery(options) { options = options || {}; const model = this.context.getModel(); const query = this.context.getQuery(); const modelId = options.modelId || model.getId(); const queryId = options.queryId || query.getId(); const url = this.context.resolveEndpoint('GetQuery', { modelId: modelId, queryId: queryId }); const http = this.context.getServices().getHttpClient(); return http.get(url) .then((responseData) => { return responseData.query; }); } saveQuery(options) { options = options || {}; const query = options.query || this.context.getQuery(); const queryId = options.queryId || query.getId(); if (options.queryId) { query.setId(options.queryId); } const modelId = options.modelId || query.getModel().getId(); if (options.queryId) { query.setId(options.queryId); } let requestData = { 'query': query.toJSONData() }; if (options.data) { requestData['data'] = options.data; } const url = this.context.resolveEndpoint('SaveQuery', { modelId: modelId, queryId: queryId }); const http = this.context.getServices().getHttpClient(); return http.put(url, requestData) .then((responseData) => { return responseData.query; }); } removeQuery(options) { options = options || {}; const context = this.context; const query = context.getQuery(); const modelId = options.modelId || query.getModel().getId(); const queryId = options.queryId || query.getId(); let requestData = { 'modelId': modelId, 'queryId': queryId }; if (options.data) { requestData['data'] = options.data; } const url = this.context.resolveEndpoint('RemoveQuery', { modelId: modelId, queryId: queryId }); const http = this.context.getServices().getHttpClient(); return http.delete(url, requestData).getPromise(); } } class DataModelEx extends core.DataModel { } class DataTableAggregatesCalculator { constructor(context) { this.context = context; //stores current totals by level (group) number this.levelsTotals = []; this.prevRow = null; this.aggrContainer = new core.EqAggregatesContainer(context); } getAggrContainer() { return this.aggrContainer; } initObjects() { this.aggrSettings = this.context.getQuery().getAggregationSettings(); this.groups = this.aggrSettings.getGroups(); this.aggregates = this.aggrSettings.getAggregates(); this.aggrColIds = this.aggrSettings.getAggregates().map(a => a.colId); this.aggrCols = this.context.resultTable.columns.getItems() .filter(col => this.aggrColIds.indexOf(col.id) >= 0); this.levelsTotals = []; this.prevRow = null; } createColumnAggregatesObject() { return { sum: 0, min: Number.MAX_VALUE, max: Number.MIN_VALUE }; } calculate(options) { this.initObjects(); this.aggrContainer.clear(); options = options || {}; options.maxLevel = options.maxLevel >= 0 ? options.maxLevel : 0; var levelNumber = this.groups.length + 1; //number of groups plus grand totals for (let levelIndex = 0; levelIndex < levelNumber; levelIndex++) { let levelTotals = { count: 0, columns: {} }; this.resetLevelTotals(levelTotals); this.levelsTotals.push(levelTotals); } const allRows = this.context.resultTable.getCachedRows(); if (allRows.length > 0) { for (const row of allRows) { this.processDataRow(row); } //flusing the totals for all group levels for (let levelIndex = 1; levelIndex < levelNumber; levelIndex++) { this.flushLevelTotals(levelIndex); } //flusing the grand totals (if they are turned on) if (this.aggrSettings.hasGrandTotals()) { this.flushLevelTotals(0); } } return Promise.resolve(); } resetLevelTotals(rowTotals) { for (const aggrCol of this.aggrCols) { rowTotals.columns[aggrCol.id] = this.createColumnAggregatesObject(); } rowTotals.count = 0; } processDataRow(row) { const levelIndexChanged = this.checkLevelChange(row); if (levelIndexChanged >= 0) { for (let levelIndex = levelIndexChanged; levelIndex < this.levelsTotals.length; levelIndex++) { this.flushLevelTotals(levelIndex); } } if (this.aggrSettings.hasGrandTotals()) { this.updateLevelTotals(0, row); } let levelIndex = 1; for (const group of this.groups) { this.updateLevelTotals(levelIndex, row); levelIndex++; } this.prevRow = row; } /** Checks if the values in group columns were changed and return the level number they were */ checkLevelChange(row) { if (this.prevRow == null) return -1; for (let groupIndex = 0; groupIndex < this.groups.length; groupIndex++) { var group = this.groups[groupIndex]; for (const colId of group.columns) { const value = row.getValue(colId); const prevValue = this.prevRow.getValue(colId); if (!this.aggrSettings.compareValues(prevValue, value)) { return groupIndex + 1; } } } return -1; } /** Saves the current values in levelData for the specified level to AggregateContainer and then reset the data row for that level */ flushLevelTotals(levelIndex) { let levelTotals = this.levelsTotals[levelIndex]; const group = levelIndex > 0 ? this.groups[levelIndex - 1] : null; let groupKey = this.aggrSettings.buildGroupKey(group, this.prevRow); var groupTotals = {}; for (const aggr of this.aggregates) { const columnTotals = levelTotals.columns[aggr.colId]; groupTotals[aggr.colId] = this.applyAggrFunc(aggr.funcId, columnTotals, levelTotals); } if (this.aggrSettings.hasRecordCount()) { groupTotals[this.aggrSettings.COUNT_FIELD_NAME] = levelTotals.count; } this.aggrContainer.updateAggregateData(levelIndex, groupKey, groupTotals); this.resetLevelTotals(levelTotals); } applyAggrFunc(funcId, columnTotals, levelTotals) { switch (funcId) { case "SUM": return columnTotals.sum; case "AVG": return columnTotals.sum / levelTotals.count; case "COUNT": case "CNTDST": return levelTotals.count; case "MIN": return columnTotals.min; case "MAX": return columnTotals.max; } } /** Updates the totals by values in the current DataRow * and according to the specified aggregate */ updateLevelTotals(levelIndex, row) { const levelTotals = this.levelsTotals[levelIndex]; levelTotals.count += 1; for (const aggr of this.aggregates) { const value = row.getValue(aggr.colId); const columnTotals = levelTotals.columns[aggr.colId]; columnTotals.sum += value; if (value < columnTotals.min) { columnTotals.min = value; } if (value > columnTotals.max) { columnTotals.max = value; } } } needRecalculation() { return true; } reset() { } } function strIsEmpty(str) { if (str) { return str.trim() === ''; } else return true; } const baseLicApiUrl = 'https://account.korzh.com/api/license'; const eqVersion = lcv.versionNum; class LCDialog { constructor(context) { this.http = new core$1.HttpClient(); this.context = context; } show() { const dialogService = new ui.DefaultDialogService(); this.dialogSet = dialogService.createSet([ { closable: false, cancelable: false, submitOnEnter: true, title: 'No license key for EasyQuery', body: this.buildStartPage(), submitButtonText: 'Submit', onInput: this.validateForm.bind(this), onSubmit: this.startPageOnSubmit.bind(this) }, { closable: false, cancelable: false, submitOnEnter: true, title: 'Please check your inbox', body: this.buildCodePage(), submitButtonText: 'Get License Key', onInput: this.validateForm.bind(this), onSubmit: this.codePageOnSubmit.bind(this) }, { closable: false, cancelable: false, submitable: false, title: 'Success', body: this.buildSuccessPage() }, { closable: false, cancelable: false, submitable: false, title: 'Failure', body: this.buildFailurePage() }, ]); const dialog1 = this.dialogSet.open(0); setTimeout(() => { this.validateForm(dialog1); const emailInput = document.getElementById('eqAuthEmail'); if (emailInput) { emailInput.focus(); } }, 200); } isValidForm(form) { const emailInput = form.querySelector('#eqAuthEmail'); //form.querySelector('[name="email"]') as HTMLInputElement; if (emailInput) { return !strIsEmpty(emailInput.value) && emailInput.value .toLowerCase() .match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/); } const codeInput = form.querySelector('#eqAuthCode'); //form.querySelector('[name="code"]') as HTMLInputElement; if (codeInput) { return !strIsEmpty(codeInput.value) && !isNaN(+codeInput.value) && codeInput.value == codeInput.value.trim() && codeInput.value.length === 6; } return true; } validateForm(dialog) { const submitButton = dialog.getSubmitButtonElement(); submitButton.disabled = !this.isValidForm(dialog.getRootElement()); } addInputField(parentBuilder, inputOptions) { parentBuilder .addChild('label', b => b .attr('for', inputOptions.id) .addHtml(`${inputOptions.label} ${inputOptions.required ? '<sup style="color: red">*</sup>' : ''}: `)) .addChild('input', b => { b.id(inputOptions.id); b.name(inputOptions.name || inputOptions.id); b.type(inputOptions.type || 'text'); if (typeof (inputOptions.value) !== 'undefined') { b.value(inputOptions.value); } if (inputOptions.onInput) { b.on('input', inputOptions.onInput); } }); } buildStartPage() { let form; let controlsPanelBuilder; const formBuilder = ui.domel('div'); formBuilder.addChild('div', b => b .html('<div style="font-size:1.2em">The EasyQuery license key is either absent, invalid or was issued for a different version of the library.</div>' + '<div style="font-size:1.2em">Please fill out the form to obtain a valid trial license key.</div>')); formBuilder .addClass('kfrm-form') .addChild('div', b => { controlsPanelBuilder = b; b.addClass('kfrm-fields label-above'); b.addClass('kfrm-break-20'); }); this.addInputField(controlsPanelBuilder, { id: 'eqAuthEmail', name: 'email', label: 'Email', required: true }); this.addInputField(controlsPanelBuilder, { id: 'eqAuthName', name: 'contactName', label: 'Name (optional)', required: false }); formBuilder.addChild('div', b => b .html('<div style="margin-top:20px;font-size:0.9em">* By clicking Submit, you agree with Korzh.com <a href="https://korzh.com/terms-of-use">Terms of use</a>' + ' and <a href="https://korzh.com/privacy">Privacy policy</a>.</div>' + '<div style="font-size:0.9em">You also agree to receive email messages from Korzh.com. This consent may be revoked at any time.</div>')); form = formBuilder.toDOM(); return form; } buildCodePage() { let form; let controlsPanelBuilder; const formBuilder = ui.domel('div'); formBuilder.addChild('div', b => b .html('<div style="font-size:1.2em">We sent a confirmation code to <strong>{{email}}</strong>.</div>' + '<div style="font-size:1.2em">Please find it in the email and enter to the text box below</div>')); formBuilder .addClass('kfrm-form') .addChild('div', b => { controlsPanelBuilder = b; b.addClass('kfrm-fields label-above'); b.addClass('kfrm-break-20'); }); this.addInputField(controlsPanelBuilder, { id: 'eqAuthCode', name: 'code', label: 'Confirmation code', required: true }); formBuilder.addChild('div', b => b .html('<div style="margin-top:20px;font-size:0.9em">NB: Don\'t forget to check your SPAM folder ' + 'if the message does not arrive within the next 5 minutes, and mark it as "Not Spam" if it\'s found there.</div>')); form = formBuilder.toDOM(); return form.outerHTML; } buildSuccessPage() { let form; const formBuilder = ui.domel('div'); formBuilder.addChild('div', b => b .html('<div style="font-size:1.2em">You have successfully completed the registration.</div>' + '{{message}}')); form = formBuilder.toDOM(); return form.outerHTML; } buildFailurePage() { let form; const formBuilder = ui.domel('div'); formBuilder.addChild('div', b => b .html('{{message}}')); form = formBuilder.toDOM(); return form.outerHTML; } startPageOnSubmit(dlg) { const emailInput = document.getElementById('eqAuthEmail'); const nameInput = document.getElementById('eqAuthName'); this.authEmail = emailInput.value; this.authName = nameInput.value; dlg.disableButtons(); this.http.post(core.equtils.combinePath(baseLicApiUrl, 'get-code'), { email: this.authEmail, name: this.authName, data: { ptag: this.context.backendInfo.type, apptype: this.context.backendInfo.subType, } }, { dataType: 'json' }) .then(result => { if (result.status !== 0) { throw new Error(result.message); } const dlg = this.dialogSet.openNext({ email: this.authEmail }); setTimeout(() => { this.validateForm(dlg); const codeInput = document.getElementById('eqAuthCode'); if (codeInput) { codeInput.focus(); } }, 200); }) .catch(error => { dlg.showAlert('Error: ' + error.message, "error", true); dlg.enableButtons(); }); return false; } codePageOnSubmit(dlg) { const codeInput = document.getElementById('eqAuthCode'); const authCode = codeInput.value; dlg.disableButtons(); this.http.post(core.equtils.combinePath(baseLicApiUrl, 'get-key'), { token: authCode, email: this.authEmail, ptag: this.context.backendInfo.type, version: eqVersion }, { dataType: 'json' }) .then(result => { switch (result.status) { case 0: const url = core.equtils.combinePath(this.context.getBaseEndpoint(), 'lck'); this.http.post(url, { version: eqVersion, key: result.key }, { dataType: 'json' }) .then(_ => this.dialogSet.open(2, { message: '<div style="font-size:1.2em">Please restart your web application to get the key applied.</div>' }), _ => this.dialogSet.open(2, { message: '<div style="font-size:1.2em">' + 'Please visit <a href="https://account.korzh.com">your account page on Korzh.com</a> to get the key ' + 'and then add it to the <i>appsettings.json</i> file (in the <i>EasyQuery/LicenseKey</i> option).</div>' })); break; case 2: this.dialogSet.open(3, { message: '<div style="font-size:1.2em">Your trial license for EasyQuery has expired.</div>' + '<div style="font-size:1.2em">Please <a href="https://korzh.com/support">contact us</a> if you want to extend the trial period.</div>' }); break; case 3: this.dialogSet.open(3, { message: '<div style="font-size:1.2em">The maintenance subscription for your EasyQuery license has expired.</div>' + '<div style="font-size:1.2em">You need to <a href="https://account.korzh.com">renew the subscription</a> to get a valid license.</div>' }); break; default: throw new Error(result.message); } }) .catch(error => { //quick fix for 7.2.5 (must be removed after fixing of HttpResponseError in EasyData) if (!error.message) { error.message = 'Invalid or expired code. Please re-submit your request.'; } dlg.showAlert('Error: ' + error.message, "error", true); dlg.enableButtons(); }); return false; } } function showNoLicenseAlert(context) { if (lcv.__kt == -3) { const lcd = new LCDialog(context); lcd.show(); } else { let title, body, buttonTitle, buttonUrl; switch (lcv.__kt) { case 1: title = 'EasyQuery trial has expired'; body = `Your trial period for EasyQuery has expired. You can <a href="https://korzh.com/support">submit a request to extend your trial</a> or <a href="https://korzh.com/easyquery#licensing">buy a license</a> to get rid of all limitations.`; buttonTitle = 'Buy a license'; buttonUrl = '#licensing'; break; default: title = `Invalid key for EasyQuery!`; body = `The EasyQuery product key is either invalid or was issued for a different version of the library. The version you are using now is ${eqVersion}, so please <a href="https://account.korzh.com">login to your account</a> to get a proper key for version ${eqVersion}`; buttonTitle = 'Get key'; buttonUrl = 'https://account.korzh.com'; break; } const ds = new ui.DefaultDialogService(); const dlg = ds.open({ closable: false, cancelable: false, submitable: false, title: title, body: body }); const footer = dlg.getRootElement().querySelector('footer'); ui.domel(footer) .removeClass('align-right') .setStyle('justify-content', 'center') .addChild('button', b => b .addClass('kfrm-button', 'is-info') .addText(buttonTitle) .on('click', (e) => { window.location.href = buttonUrl; })); if (lcv.__kt == 1) { ui.domel(footer) .addChild('button', b => b .addClass('kfrm-button') .addText('Extend trial') .on('click', (e) => { window.location.href = 'https://account.korzh.com'; })); } } } class EnterpriseEqContext extends core.EqContext { useEnterprise(keyOrInitCallback) { const func = (key) => { lcv.ck(key); if (lcv.__kt > 1) { const services = this.getServices(); services.registerDataModelResolver(context => new DataModelEx()); services.registerQueryResolver(context => new QueryEx(context.getModel(), { context: context })); services.registerQueryStorageResolver(context => new EqServerQueryStorage(context)); services.registerAggregatesCalculator(context => new DataTableAggregatesCalculator(context)); if (lcv.__kt === 2) { console.warn('EasyQuery.JS is working in the TRIAL MODE!\n' + 'Please order a license (https://korzh.com/easyquery) to get a proper product key.'); this.createTrialWM(); setTimeout(() => { this.showTrialWM(); setTimeout(() => { this.hideTrialWM(); }, 6000); }, 3000); } } else { showNoLicenseAlert(this); } }; if (typeof (keyOrInitCallback) === "string") { func(keyOrInitCallback); } else if (typeof (keyOrInitCallback) === "function") { const http = this.getServices().getHttpClient(); let url = core.equtils.combinePath(this.getBaseEndpoint(), 'lck2'); http.get(url) .then(result => { func(result.key.split("").reverse().join("")); this.useBackend({ type: result.backendType, subType: result.appType }); keyOrInitCallback(); }) .catch(error => { if (error.status == 401) { //if the stutus is Not Autorized func(''); } else { //otherwise try the old way url = core.equtils.combinePath(this.getBaseEndpoint(), 'lck'); http.get(url) .then(result => { func(result.split("").reverse().join("")); keyOrInitCallback(); }) .catch(error => this.throwError({ action: "LCK", sourceError: error })); } }); } else { throw new Error("Wrong type of 'keyOrInitCallback' parameter. It must be a string with a license key or a callback function."); } } createTrialWM() { // we can rewrite it using shadow dom // to make it impossible to hide using JS :) let trialDiv = document.createElement("div"); trialDiv.style.position = "fixed"; trialDiv.style.bottom = "0"; trialDiv.style.right = "-400px"; trialDiv.style.width = "400px"; trialDiv.style.height = "100px"; trialDiv.style.color = "grey"; trialDiv.style.textAlign = "center"; trialDiv.style.opacity = "0.7"; trialDiv.style.font = "bold 1em Trebuchet MS, Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif"; trialDiv.style.transition = "right 3s ease-out"; const header = document.createElement("div"); const eq = document.createElement("span"); eq.innerText = "EasyQuery"; eq.style.color = "#3A94D4"; eq.style.lineHeight = "50px"; header.appendChild(eq); const korzh = document.createElement("span"); korzh.innerText = "by Korzh.com"; korzh.style.marginLeft = "10px"; header.appendChild(korzh); trialDiv.appendChild(header); const trialOnly = document.createElement("div"); trialOnly.innerText = "FOR TRIAL USE ONLY"; trialOnly.style.fontSize = "1.2em"; trialOnly.style.fontWeight = "1000"; trialDiv.appendChild(trialOnly); document.body.appendChild(trialDiv); this.trialDivElement = trialDiv; } showTrialWM() { if (this.trialDivElement) { this.trialDivElement.style.right = "0"; } } hideTrialWM() { if (this.trialDivElement) { this.trialDivElement.style.right = "-400px"; setTimeout(() => { this.trialDivElement.parentNode.removeChild(this.trialDivElement); }, 5000); } } } core.registerEqContextResolver(() => new EnterpriseEqContext()); class EqServerAggregatesCalculator { constructor(context) { this.context = context; this._needRecalculation = true; this.aggrContainer = new core.EqAggregatesContainer(context); } getAggrContainer() { return this.aggrContainer; } calculate(options) { this._needRecalculation = false; this.aggrContainer.clear(); options = options || {}; options.maxLevel = options.maxLevel >= 0 ? options.maxLevel : 0; const levelPromises = []; const query = this.context.createQuery(); query.loadFromData(this.context.getQuery().toJSONData()); const settings = this.context.getQuery().getAggregationSettings(); const aggregates = settings.getAggregates(); const aggrColIds = settings.getAggregates().map(a => a.colId); const aggrCols = query.getColumns() .filter(col => aggrColIds.indexOf(col.id) >= 0); for (const col of aggrCols) { query.changeColumnType(col, core.ExprTag.AggregateFunction, { funcId: aggregates.filter(a => a.colId == col.id)[0].funcId }); } //get all non-aggregate columns const queryCols = query.getColumns() .filter(c => c.expr.tag !== core.ExprTag.AggregateFunction); for (const col of queryCols) { col.setHidden(true); } if (settings.hasRecordCount()) { const col = query.createColumn(); col.caption = settings.COUNT_FIELD_NAME; col['_id'] = settings.COUNT_FIELD_NAME; const sqlText = 'COUNT(*)'; const customSqlExpr = new core.Expression(col); customSqlExpr.tag = core.ExprTag.CustomSql; customSqlExpr.sql = sqlText; customSqlExpr.baseAttrId = null; col.expr = customSqlExpr; query.getColumns().push(col); } if (settings.hasGrandTotals()) { levelPromises.push(this.processLevel({ query: query, level: 0, groupName: 'Grand Totals', resultObtainedCallback: options.resultObtained, errorOccurredCallback: options.errorOccurred })); } const groups = settings.getGroups(); for (let level = 1; level <= groups.length; level++) { const group = groups[level - 1]; for (const col of queryCols) { col.setHidden(group.columns.indexOf(col.id) < 0); } levelPromises.push(this.processLevel({ query: query, level: level, groupName: group.name, resultObtainedCallback: options.resultObtained, errorOccurredCallback: options.errorOccurred })); } return Promise.all(levelPromises) .then(() => { return; }) .catch((error) => console.error(error)); } processLevel(options) { const fetcher = this.context.getDataFetcher(); let fetchOptions = { aux: true, query: options.query }; if (this.context.debugMode) { fetchOptions.debug = `Fetching group data. Group: '${options.groupName}', level: ${options.level}`; } const resultPromise = fetcher.fetchData(fetchOptions) .then((result) => { const dataTable = new core$1.EasyDataTable({ elasticChunks: true }); const resultSet = result.resultSet; for (const col of resultSet.cols) { dataTable.columns.add(col); } for (const row of resultSet.rows) { dataTable.addRow(row); } const levelData = this.buildLevelData(options.level, dataTable); this.aggrContainer.setAggregateData(options.level, levelData); if (options.resultObtainedCallback) { options.resultObtainedCallback(result, options.level); } }) .catch((error) => { if (options.errorOccurredCallback) { options.errorOccurredCallback(Object.assign(Object.assign({}, error), { level: options.level })); } }); return resultPromise; } buildLevelData(level, dataTable) { const aggrSettings = this.context.getQuery().getAggregationSettings(); const keyColumns = level > 0 ? aggrSettings.getGroups()[level - 1].columns : []; const valueColumns = aggrSettings.getAggregates().map(a => a.colId); if (aggrSettings.hasRecordCount()) { valueColumns.push(aggrSettings.COUNT_FIELD_NAME); } const data = new Map(); for (const row of dataTable.getCachedRows()) { const key = {}; const value = {}; for (const keyCol of keyColumns) { const colIdx = dataTable.columns.getIndex(keyCol); if (colIdx >= 0) { let keyVal = row.getValue(keyCol); if (!aggrSettings.caseSensitiveGroups && typeof (keyVal) === 'string') { keyVal = keyVal.toLowerCase(); } key[keyCol] = keyVal; } } for (const valueCol of valueColumns) { value[valueCol] = row.getValue(valueCol); } data.set(JSON.stringify(key), value); } return data; } needRecalculation() { return this._needRecalculation; } reset() { this._needRecalculation = true; } } exports.ConditionEx = ConditionEx; exports.DataModelEx = DataModelEx; exports.EqServerAggregatesCalculator = EqServerAggregatesCalculator; exports.EqServerQueryStorage = EqServerQueryStorage; exports.QueryColumnEx = QueryColumnEx; exports.QueryEx = QueryEx;