new(all): Auto import for logo, colors and website content.

This commit is contained in:
Rodrigo Rodriguez 2024-05-23 23:45:45 -03:00
parent 5d32eedba9
commit 533fefe0da

View file

@ -1,4 +1,3 @@
/*****************************************************************************\
| ® |
| |
@ -64,11 +63,7 @@ import exts from '../../../extensions.json' assert { type: 'json' };
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import retry from 'async-retry';
import {
BlobServiceClient,
BlockBlobClient,
StorageSharedKeyCredential
} from '@azure/storage-blob';
import { BlobServiceClient, BlockBlobClient, StorageSharedKeyCredential } from '@azure/storage-blob';
import { md5 } from 'js-md5';
import { GBUtil } from '../../../src/util.js';
@ -81,7 +76,6 @@ import { GBUtil } from '../../../src/util.js';
* BASIC system class for extra manipulation of bot behaviour.
*/
export class SystemKeywords {
/**
* @tags System
*/
@ -90,7 +84,7 @@ export class SystemKeywords {
const step = null;
const deployer = null;
return await GBVMService.callVM(text, min, step, pid,false, [text]);
return await GBVMService.callVM(text, min, step, pid, false, [text]);
}
public async append({ pid, args }) {
@ -172,22 +166,22 @@ export class SystemKeywords {
if (date) {
return array
? array.sort((a, b) => {
const c = new Date(a[memberName]);
const d = new Date(b[memberName]);
return c.getTime() - d.getTime();
})
const c = new Date(a[memberName]);
const d = new Date(b[memberName]);
return c.getTime() - d.getTime();
})
: null;
} else {
return array
? array.sort((a, b) => {
if (a[memberName] < b[memberName]) {
return -1;
}
if (a[memberName] > b[memberName]) {
return 1;
}
return 0;
})
if (a[memberName] < b[memberName]) {
return -1;
}
if (a[memberName] > b[memberName]) {
return 1;
}
return 0;
})
: array;
}
}
@ -662,7 +656,7 @@ export class SystemKeywords {
* Saves the content of variable into BLOB storage.
*
* MSFT uses MD5, see https://katelynsills.com/law/the-curious-case-of-md5.
*
*
* @exaple UPLOAD file.
*
*/
@ -675,17 +669,10 @@ export class SystemKeywords {
const accountName = min.core.getParam(min.instance, 'Blob Account');
const accountKey = min.core.getParam(min.instance, 'Blob Key');
const sharedKeyCredential = new StorageSharedKeyCredential(
accountName,
accountKey
);
const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
const baseUrl = `https://${accountName}.blob.core.windows.net`;
const blobServiceClient = new BlobServiceClient(
`${baseUrl}`,
sharedKeyCredential
);
const blobServiceClient = new BlobServiceClient(`${baseUrl}`, sharedKeyCredential);
// It is an SharePoint object that needs to be downloaded.
@ -704,31 +691,25 @@ export class SystemKeywords {
const container = blobServiceClient.getContainerClient(accountName);
const blockBlobClient: BlockBlobClient = container.getBlockBlobClient(file.path);
const res = await blockBlobClient.uploadFile(localName,
{
blobHTTPHeaders: {
blobContentMD5: hash
}
});
const res = await blockBlobClient.uploadFile(localName, {
blobHTTPHeaders: {
blobContentMD5: hash
}
});
// If upload is OK including hash check, removes the temporary file.
if (res._response.status === 201 &&
(new Uint8Array(res.contentMD5)).toString() === hash.toString()) {
if (res._response.status === 201 && new Uint8Array(res.contentMD5).toString() === hash.toString()) {
Fs.rmSync(localName);
file['md5'] = hash.toString();
return file;
}
else {
} else {
GBLog.error(`BASIC: BLOB HTTP ${res.errorCode} ${res._response.status} .`);
}
}
/**
* Takes note inside a notes.xlsx of .gbdata.
*
@ -736,19 +717,18 @@ export class SystemKeywords {
*
*/
public async note({ pid, text }): Promise<any> {
await this.save({ pid, file: "Notes.xlsx", args: [text] });
await this.save({ pid, file: 'Notes.xlsx', args: [text] });
}
/**
* Saves variables to storage, not a worksheet.
*
*/
* Saves variables to storage, not a worksheet.
*
*/
public async saveToStorageBatch({ pid, table, rows }): Promise<void> {
const { min } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `BASIC: Saving batch to storage '${table}' (SAVE).`);
if (rows.length === 0) {
return;
}
@ -756,7 +736,6 @@ export class SystemKeywords {
const rowsDest = [];
rows.forEach(row => {
const dst = {};
let i = 0;
Object.keys(row).forEach(column => {
@ -768,27 +747,26 @@ export class SystemKeywords {
});
await retry(
async (bail) => {
async bail => {
await definition.bulkCreate(GBUtil.caseInsensitive(rowsDest));
},
{
retries: 5,
onRetry: (err) => { GBLog.error(`Retrying SaveToStorageBatch due to: ${err.message}.`); }
onRetry: err => {
GBLog.error(`Retrying SaveToStorageBatch due to: ${err.message}.`);
}
}
);
}
/**
* Saves variables to storage, not a worksheet.
*
* @example SAVE "Billing", columnName1, columnName2
*
*/
* Saves variables to storage, not a worksheet.
*
* @example SAVE "Billing", columnName1, columnName2
*
*/
public async saveToStorage({ pid, table, fieldsValues, fieldsNames }): Promise<any> {
if (!fieldsValues || fieldsValues.length===0 || !fieldsValues[0]){
if (!fieldsValues || fieldsValues.length === 0 || !fieldsValues[0]) {
return;
}
@ -810,17 +788,17 @@ export class SystemKeywords {
let item;
await retry(
async (bail) => {
async bail => {
item = await definition.create(dst);
},
{
retries: 5,
onRetry: (err) => { GBLog.error(`Retrying SaveToStorage due to: ${err.message}.`); }
onRetry: err => {
GBLog.error(`Retrying SaveToStorage due to: ${err.message}.`);
}
}
);
return item;
}
public async saveToStorageWithJSON({ pid, table, fieldsValues, fieldsNames }): Promise<any> {
@ -830,7 +808,8 @@ export class SystemKeywords {
const definition = minBoot.core.sequelize.models[table];
let out = [];
let data = {}, data2 = {};
let data = {},
data2 = {};
// Flattern JSON to a table.
@ -853,8 +832,7 @@ export class SystemKeywords {
*
*/
public async save({ pid, file, args }): Promise<any> {
if (!args){
if (!args) {
return;
}
@ -1040,9 +1018,7 @@ export class SystemKeywords {
});
return filter;
};
}
/**
* Finds a value or multi-value results in a tabular file.
@ -1134,13 +1110,18 @@ export class SystemKeywords {
for (let i = 0; i < worksheet.rowCount; i++) {
const r = worksheet.getRow(i + 1);
let outRow = [];
let hasValue = false;
for (let j = 0; j < r.cellCount; j++) {
outRow.push(r.getCell(j + 1).text);
const value = r.getCell(j + 1).text;
if (value) {
hasValue = true;
}
outRow.push(value);
}
if (i == 0) {
header = outRow;
} else {
} else if (hasValue) {
rows.push(outRow);
}
}
@ -1177,7 +1158,6 @@ export class SystemKeywords {
return res.length > 1 ? res : res[0];
}
const contentLocale = min.core.getParam(
min.instance,
'Default Content Language',
@ -1349,20 +1329,27 @@ export class SystemKeywords {
rowCount++;
let row = {};
const xlRow = rows[foundIndex];
let hasValue = false;
for (let colIndex = 0; colIndex < xlRow.length; colIndex++) {
const propertyName = header[colIndex];
let value = xlRow[colIndex];
if (value && value.charAt(0) === "'") {
if (await this.isValidDate({ pid, dt: value.substr(1) })) {
value = value.substr(1);
if (value) {
hasValue = true;
if (value.charAt(0) === "'") {
if (await this.isValidDate({ pid, dt: value.substr(1) })) {
value = value.substr(1);
}
}
}
row[propertyName] = value;
}
row['ordinal'] = rowCount;
row['line'] = foundIndex + 1;
table.push(row);
if (hasValue) {
table.push(row);
}
}
}
@ -1445,19 +1432,17 @@ export class SystemKeywords {
}
public async setSystemPrompt({ pid, text }) {
let { min, user } = await DialogKeywords.getProcessInfo(pid);
if (user) {
ChatServices.userSystemPrompt[user.userSystemId] = text;
const path = DialogKeywords.getGBAIPath(min.botId);
const systemPromptFile = urlJoin(process.cwd(), 'work', path, 'users',user.userSystemId, 'systemPrompt.txt');
const systemPromptFile = urlJoin(process.cwd(), 'work', path, 'users', user.userSystemId, 'systemPrompt.txt');
Fs.writeFileSync(systemPromptFile, text);
}
}
/**
* Creates a folder in the bot instance drive.
*
@ -1632,7 +1617,6 @@ export class SystemKeywords {
*
*/
public async convert({ pid, src, dest }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `BASIC: CONVERT '${src}' to '${dest}'`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
@ -1704,43 +1688,39 @@ export class SystemKeywords {
continue;
}
if (typeof obj[key] !== 'object' || obj[key] instanceof Date) {
// If not defined already add the flattened field.
const newKey = `${parent ? (parent + separator) : ''}${key}`;
const newKey = `${parent ? parent + separator : ''}${key}`;
if (!res[newKey]) {
res[newKey] = obj[key];
}
else {
} else {
GBLog.verbose(`Ignoring duplicated field in flatten operation to storage: ${key}.`);
}
} else {
obj[key] = this.flattenJSON(obj[key], res, separator, `${parent ? parent + separator : ''}${key}`);
};
};
}
}
return res;
}
public async getCustomToken({ pid, tokenName }) {
const { min } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `BASIC internal getCustomToken: ${tokenName}`);
const token = await (min.adminService as any)['acquireElevatedToken']
(min.instance.instanceId, false,
tokenName,
min.core.getParam(min.instance, `${tokenName} Client ID`, null),
min.core.getParam(min.instance, `${tokenName} Client Secret`, null),
min.core.getParam(min.instance, `${tokenName} Host`, null),
min.core.getParam(min.instance, `${tokenName} Tenant`, null)
);
const token = await (min.adminService as any)['acquireElevatedToken'](
min.instance.instanceId,
false,
tokenName,
min.core.getParam(min.instance, `${tokenName} Client ID`, null),
min.core.getParam(min.instance, `${tokenName} Client Secret`, null),
min.core.getParam(min.instance, `${tokenName} Host`, null),
min.core.getParam(min.instance, `${tokenName} Tenant`, null)
);
const expiresOn = await min.adminService.getValue(min.instance.instanceId, `${tokenName}expiresOn`);
return { token, expiresOn };
}
/**
* Calls any REST API by using GET HTTP method.
*
@ -1754,10 +1734,9 @@ export class SystemKeywords {
GBLogEx.info(min, `GET: ${url}`);
let pageMode = await DialogKeywords.getOption({ pid, name: 'pageMode' });
let continuationToken = await
DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` });
let continuationToken = await DialogKeywords.getOption({ pid, name: `${proc.executable}-continuationToken` });
if (pageMode === "auto" && continuationToken) {
if (pageMode === 'auto' && continuationToken) {
headers = headers ? headers : {};
headers['MS-ContinuationToken'] = continuationToken;
@ -1778,11 +1757,9 @@ export class SystemKeywords {
}
let result;
await retry(
async (bail) => {
async bail => {
result = await fetch(url, options);
if (result.status === 401) {
GBLogEx.info(min, `Waiting 5 secs. before retrynig HTTP 401 GET: ${url}`);
await GBUtil.sleep(5 * 1000);
@ -1800,38 +1777,34 @@ export class SystemKeywords {
}
if (result.status === 2000) {
// Token expired.
await DialogKeywords.setOption({ pid, name: `${proc.executable}-continuationToken`, value: null });
bail(new Error(`Expired Token for ${url}.`));
}
if (result.status != 200) {
throw new Error(`BASIC: GET ${result.status}: ${result.statusText}.`);
}
},
{
retries: 5,
onRetry: (err) => { GBLog.error(`Retrying HTTP GET due to: ${err.message}.`); }
onRetry: err => {
GBLog.error(`Retrying HTTP GET due to: ${err.message}.`);
}
}
);
let res = JSON.parse(await result.text());
function process(key, value, o) {
if (value === '0000-00-00') {
o[key] = null;
}
}
function traverse(o, func) {
for (var i in o) {
func.apply(this, [i, o[i], o]);
if (o[i] !== null && typeof (o[i]) == "object") {
if (o[i] !== null && typeof o[i] == 'object') {
traverse(o[i], func);
}
}
@ -1839,24 +1812,22 @@ export class SystemKeywords {
traverse(res, process);
if (pageMode === "auto") {
if (pageMode === 'auto') {
continuationToken = res.next?.headers['MS-ContinuationToken'];
if (continuationToken) {
GBLogEx.info(min, `Updating continuationToken for ${url}.`);
await DialogKeywords.setOption({ pid, name: 'continuationToken', value: continuationToken });
}
}
else {
pageMode = "none";
} else {
pageMode = 'none';
}
if (res) { res['pageMode'] = pageMode; }
if (res) {
res['pageMode'] = pageMode;
}
return res;
}
/**
@ -1876,11 +1847,10 @@ export class SystemKeywords {
method: 'PUT'
};
if (typeof (data) === 'object') {
if (typeof data === 'object') {
options['body'] = JSON.stringify(data);
options.headers['Content-Type'] = 'application/json';
}
else {
} else {
options['body'] = data;
}
@ -1889,7 +1859,7 @@ export class SystemKeywords {
GBLogEx.info(min, `BASIC: PUT ${url} (${data}): ${text}`);
if (result.status != 200 && result.status != 201) {
throw new Error(`BASIC: PUT ${result.status}: ${result.statusText}.`)
throw new Error(`BASIC: PUT ${result.status}: ${result.statusText}.`);
}
let res = JSON.parse(text);
@ -1912,11 +1882,10 @@ export class SystemKeywords {
method: 'POST'
};
if (typeof (data) === 'object') {
if (typeof data === 'object') {
options['body'] = JSON.stringify(data);
options.headers['Content-Type'] = 'application/json';
}
else {
} else {
options['body'] = data;
}
@ -1925,7 +1894,7 @@ export class SystemKeywords {
GBLogEx.info(min, `BASIC: POST ${url} (${data}): ${text}`);
if (result.status != 200 && result.status != 201) {
throw new Error(`BASIC: POST ${result.status}: ${result.statusText}.`)
throw new Error(`BASIC: POST ${result.status}: ${result.statusText}.`);
}
let res = JSON.parse(text);
@ -2056,8 +2025,11 @@ export class SystemKeywords {
};
const metadata = await sharp(buf).metadata();
const size = getNormalSize({width:metadata['width'],
height:metadata['height'], orientation: metadata['orientation'] });
const size = getNormalSize({
width: metadata['width'],
height: metadata['height'],
orientation: metadata['orientation']
});
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', Path.basename(imageName));
images[index++] = { url: url, size: size, buf: buf };
}
@ -2152,7 +2124,6 @@ export class SystemKeywords {
} else {
return minBoot.core.sequelize.models[file];
}
}
private cachedMerge: any = {};
@ -2173,22 +2144,20 @@ export class SystemKeywords {
return data;
}
GBLogEx.info(min, `BASIC: MERGE running on ${file} and key1: ${key1}, key2: ${key2}...`);
if (!this.cachedMerge[pid]) {
this.cachedMerge[pid] = { file: {} }
this.cachedMerge[pid] = { file: {} };
}
// Check if is a tree or flat object.
const hasSubObject = (t) => {
const hasSubObject = t => {
for (var key in t) {
if (!t.hasOwnProperty(key)) continue;
if (typeof t[key] === "object") return true;
if (typeof t[key] === 'object') return true;
}
return false;
}
};
// MAX LINES property.
@ -2203,14 +2172,14 @@ export class SystemKeywords {
let storage = file.indexOf('.xlsx') === -1;
let results;
let header = [], rows = [];
let header = [],
rows = [];
let t;
let fieldsNames = [];
let fieldsSizes = [];
let fieldsValuesList = [];
if (storage) {
t = this.getTableFromName(file, min);
if (!t) {
@ -2219,11 +2188,11 @@ export class SystemKeywords {
Object.keys(t.fieldRawAttributesMap).forEach(e => {
fieldsNames.push(e);
})
});
Object.keys(t.fieldRawAttributesMap).forEach(e => {
fieldsSizes.push(t.fieldRawAttributesMap[e].size);
})
});
header = Object.keys(t.fieldRawAttributesMap);
@ -2232,37 +2201,31 @@ export class SystemKeywords {
if (!this.cachedMerge[pid][file]) {
await retry(
async (bail) => {
let page = 0, pageSize = 1000;
async bail => {
let page = 0,
pageSize = 1000;
let count = 0;
while (page === 0 || count === pageSize) {
const paged = await t.findAll(
{ offset: page * pageSize, limit: pageSize, subquery: false, where: {} }
);
const paged = await t.findAll({ offset: page * pageSize, limit: pageSize, subquery: false, where: {} });
rows = [...paged, ...rows];
page++;
count = paged.length;
GBLogEx.info(min, `BASIC: MERGE cached: ${rows.length} from page: ${page}.`);
}
},
{
retries: 5,
onRetry: (err) => { GBLog.error(`MERGE: Retrying SELECT ALL on table: ${err.message}.`); }
onRetry: err => {
GBLog.error(`MERGE: Retrying SELECT ALL on table: ${err.message}.`);
}
}
);
}
else {
} else {
rows = this.cachedMerge[pid][file];
}
} else {
const botId = min.instance.botId;
const path = DialogKeywords.getGBAIPath(botId, 'gbdata');
@ -2297,7 +2260,6 @@ export class SystemKeywords {
row = tmpRow.dataValues ? tmpRow.dataValues : tmpRow;
for (let colIndex = 0; colIndex < tmpRow.length; colIndex++) {
const propertyName = header[colIndex];
let value = tmpRow[colIndex];
@ -2316,8 +2278,7 @@ export class SystemKeywords {
if (storage) {
this.cachedMerge[pid][file] = table;
}
}
else {
} else {
table = this.cachedMerge[pid][file];
}
@ -2332,12 +2293,12 @@ export class SystemKeywords {
}
let updates = 0,
adds = 0, skipped = 0;
adds = 0,
skipped = 0;
// Scans all items in incoming data.
for (let i = 0; i < data.length; i++) {
// Scans all sheet lines and compare keys.
let row = data[i];
@ -2350,7 +2311,6 @@ export class SystemKeywords {
let key1Value;
let key1Original = key1;
if (key1Index) {
key1 = key1.charAt(0).toLowerCase() + key1.slice(1);
Object.keys(row).forEach(e => {
@ -2368,16 +2328,14 @@ export class SystemKeywords {
if (found) {
let merge = false;
for (let j = 0; j < header.length; j++) {
const columnName = header[j];
let columnNameFound = false;
let value;
Object.keys(row).forEach(e => {
if (columnName.toLowerCase() === e.toLowerCase()) {
value = row[e];
if (typeof (value) === 'string') {
if (typeof value === 'string') {
value = value.substring(0, fieldsSizes[j]);
}
@ -2385,7 +2343,9 @@ export class SystemKeywords {
}
});
if (value === undefined) { value = null; }
if (value === undefined) {
value = null;
}
let valueFound;
Object.keys(found).forEach(e => {
@ -2395,27 +2355,24 @@ export class SystemKeywords {
});
const equals =
typeof (value) === 'string' && typeof (valueFound) === 'string' ?
value?.toLowerCase() != valueFound?.toLowerCase() :
value != valueFound;
typeof value === 'string' && typeof valueFound === 'string'
? value?.toLowerCase() != valueFound?.toLowerCase()
: value != valueFound;
if (equals && columnNameFound) {
if (storage) {
let obj = {};
obj[columnName] = value;
let criteria = {};
criteria[key1Original] = key1Value;
await retry(
async (bail) => {
async bail => {
await t.update(obj, { where: criteria });
}, { retries: 5 }
},
{ retries: 5 }
);
} else {
const cell = `${this.numberToLetters(j)}${i + 1}`;
const address = `${cell}:${cell}`;
@ -2423,22 +2380,18 @@ export class SystemKeywords {
}
merge = true;
}
}
merge ? updates++ : skipped++;
} else {
let fieldsValues = [];
for (let j = 0; j < fieldsNames.length; j++) {
let add = false;
Object.keys(row).forEach(p => {
if (fieldsNames[j].toLowerCase() === p.toLowerCase()) {
let value = row[p];
if (typeof (value) === 'string') {
if (typeof value === 'string') {
value = value.substring(0, fieldsSizes[j]);
}
@ -2452,8 +2405,6 @@ export class SystemKeywords {
}
if (storage) {
// Uppercases fields.
const dst = {};
@ -2467,10 +2418,8 @@ export class SystemKeywords {
fieldsValuesList.push(dst);
this.cachedMerge[pid][file].push(dst);
}
else {
} else {
await this.save({ pid, file, args: fieldsValues });
}
adds++;
}
@ -2483,7 +2432,7 @@ export class SystemKeywords {
}
GBLogEx.info(min, `BASIC: MERGE results: adds:${adds}, updates:${updates} , skipped: ${skipped}.`);
return { title:file, adds, updates, skipped };
return { title: file, adds, updates, skipped };
}
/**
@ -2640,20 +2589,20 @@ export class SystemKeywords {
const filter = await SystemKeywords.getFilter(criteria);
await retry(
async (bail) => {
async bail => {
const options = { where: {} };
options.where = {};
options.where[filter['columnName']] = filter['value'];
await definition.destroy(options);
},
{
retries: 5,
onRetry: (err) => { GBLog.error(`Retrying SaveToStorageBatch due to: ${err.message}.`); }
onRetry: err => {
GBLog.error(`Retrying SaveToStorageBatch due to: ${err.message}.`);
}
}
);
}
public async deleteFile({ pid, file }) {
@ -2667,14 +2616,11 @@ export class SystemKeywords {
const ext = Path.extname(fileName).substring(1);
const kind = await this.getExtensionInfo(ext);
await client
.api(`${baseUrl}/drive/root:/${gbaiPath}/${file.path}`)
.delete();
await client.api(`${baseUrl}/drive/root:/${gbaiPath}/${file.path}`).delete();
return { contentType, ext, kind, category: kind['category'] };
}
public async getExtensionInfo(ext: any): Promise<any> {
let array = exts.filter((v, i, a) => a[i]['extension'] === ext);
if (array[0]) {
@ -2684,10 +2630,9 @@ export class SystemKeywords {
}
/**
* Loads all para from tabular file Config.xlsx.
*/
* Loads all para from tabular file Config.xlsx.
*/
public async dirFolder({ pid, remotePath, baseUrl = null, client = null, array = null }) {
const { min } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `dirFolder: remotePath=${remotePath}, baseUrl=${baseUrl}`);
@ -2721,13 +2666,10 @@ export class SystemKeywords {
// Navigate files / directory to recurse.
await CollectionUtil.asyncForEach(documents, async item => {
if (item.folder) {
remotePath = urlJoin(remotePath, item.name);
array = [...array, ... await this.dirFolder({ pid, remotePath, baseUrl, client, array })];
array = [...array, ...(await this.dirFolder({ pid, remotePath, baseUrl, client, array }))];
} else {
// TODO: https://raw.githubusercontent.com/ishanarora04/quickxorhash/master/quickxorhash.js
let obj = {};
@ -2746,36 +2688,32 @@ export class SystemKeywords {
}
public async log({ pid, text: obj }) {
const { min } = await DialogKeywords.getProcessInfo(pid);
let level = 0;
const mydump = (text, level) => {
var dumped_text = '';
var dumped_text = "";
var level_padding = '';
for (var j = 0; j < level + 1; j++) level_padding += ' ';
var level_padding = "";
for (var j = 0; j < level + 1; j++) level_padding += " ";
if (typeof (text) == 'object') {
if (typeof text == 'object') {
for (var item in text) {
var value = text[item];
if (typeof (value) == 'object') {
if (typeof value == 'object') {
dumped_text += level_padding + "'" + item + "' ...\n";
dumped_text += mydump(value, level + 1);
} else {
dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
dumped_text += level_padding + "'" + item + '\' => "' + value + '"\n';
}
}
} else {
dumped_text = text + "(" + typeof (text) + ")";
dumped_text = text + '(' + typeof text + ')';
}
return dumped_text;
};
GBLogEx.info(min, mydump(obj, level));
}
}