diff --git a/config/app-config.cfg b/config/app-config.cfg index 81442266..24e41c41 100755 --- a/config/app-config.cfg +++ b/config/app-config.cfg @@ -9,6 +9,7 @@ language=en [limit] sandbox=1 cqlsh=10 +insertBlobSize=1GB [sshtunnel] readyTimeout=60000 @@ -18,6 +19,7 @@ forwardTimeout=60000 sandboxProjects=true containersManagementTool=none basicCQLSH=true +previewBlob=true [updates] checkForUpdates=true diff --git a/custom_node_modules/main/reports.js b/custom_node_modules/main/reports.js index 2654d7f6..cd15bda1 100755 --- a/custom_node_modules/main/reports.js +++ b/custom_node_modules/main/reports.js @@ -111,7 +111,7 @@ let getMemory = async () => { let value = result[attribute] // Convert the value from bytes to a human-readable string - value = ByteSize(value) + value = Bytes(value) // Format the string to be the value and its unit - KB, MB, etc... - value = `${value['value']}${value['unit']}` diff --git a/localization/ar.json b/localization/ar.json index ea5ea420..e50ee63b 100755 --- a/localization/ar.json +++ b/localization/ar.json @@ -234,7 +234,7 @@ "logging system": "نظام التسجيل", "logging": "تسجيل", "logout": "تسجيل خروج", - "mandatory field": "حقل إجباري", + "mandatory": "إجباري", "maximum number": "الرقم الأقصى", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "تم نسخ البيانات الوصفية للعنقود المُتصَل به من خلال [b]$data[/b]، حجم البيانات هو $data", "missing variables": "المتغيرات المفقودة", diff --git a/localization/en.json b/localization/en.json index c525bb33..084261c0 100755 --- a/localization/en.json +++ b/localization/en.json @@ -234,7 +234,7 @@ "logging system": "logging system", "logging": "logging", "logout": "logout", - "mandatory field": "mandatory field", + "mandatory": "mandatory", "maximum number": "maximum number", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data", "missing variables": "missing variables", diff --git a/localization/es.json b/localization/es.json index d4db14ab..06639537 100644 --- a/localization/es.json +++ b/localization/es.json @@ -234,7 +234,7 @@ "logging system": "Sistema de registro", "logging": "Registro", "logout": "Cerrar sesión", - "mandatory field": "Campo obligatorio", + "mandatory": "obligatorio", "maximum number": "Número máximo", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "Los metadatos del clúster conectado por [b]$data[/b] se han copiado en el portapapeles, el tamaño es $data", "missing variables": "Variables faltantes", diff --git a/localization/fr.json b/localization/fr.json index 38d8e5d6..91d89ab6 100755 --- a/localization/fr.json +++ b/localization/fr.json @@ -234,7 +234,7 @@ "logging system": "système d’enregistrement", "logging": "exploitation forestière", "logout": "déconnexion", - "mandatory field": "Champ obligatoire", + "mandatory": "obligatoire", "maximum number": "nombre maximum", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "Les métadonnées du cluster connecté par [b]$data[/b] ont été copiées dans le presse-papiers, leur taille est de $data", "missing variables": "Variables manquantes", diff --git a/localization/gl.json b/localization/gl.json index d70188bc..44a0118e 100755 --- a/localization/gl.json +++ b/localization/gl.json @@ -234,7 +234,7 @@ "logging system": "Sistema de rexistro", "logging": "Rexistro", "logout": "Logout", - "mandatory field": "Campo obrigatorio", + "mandatory": "obrigatorio", "maximum number": "Número máximo", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "Os metadatos para o cluster conectado a [b]$data[/b] foron copiados no portapapeis, o tamaño é $data", "missing variables": "Faltan variables", diff --git a/localization/iw.json b/localization/iw.json index 7e34e6ea..d05ea7b0 100755 --- a/localization/iw.json +++ b/localization/iw.json @@ -234,7 +234,7 @@ "logging system": "מערכת רישום", "logging": "רישום", "logout": "התנתקות", - "mandatory field": "שדה חובה", + "mandatory": "הֶכְרֵחִי", "maximum number": "מספר מרבי", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "מטה-נתונים עבור האשכול המחובר באמצעות [b]$data[/b] הועתקו ללוח, הגודל הוא $data", "missing variables": "משתנים חסרים", diff --git a/localization/zh.json b/localization/zh.json index 10f29a15..7331de3c 100755 --- a/localization/zh.json +++ b/localization/zh.json @@ -234,7 +234,7 @@ "logging system": "测井系统", "logging": "伐木", "logout": "注销", - "mandatory field": "必填字段", + "mandatory": "强制的", "maximum number": "最大数量", "metadata for the cluster connected to by [b]$data[/b] has been copied to the clipboard, the size is $data": "通过 [b]$data[/b] 连接的集群的元数据已复制到剪贴板,大小为 $data", "missing variables": "缺少变量", diff --git a/main/main.js b/main/main.js index c001b9ae..9bcc9a1e 100755 --- a/main/main.js +++ b/main/main.js @@ -59,7 +59,8 @@ const Electron = require('electron'), * https://www.electronjs.org/docs/latest/api/menu-item * */ - MenuItem = Electron.MenuItem + MenuItem = Electron.MenuItem, + NativeImage = Electron.nativeImage /** * Import modules globally @@ -776,6 +777,28 @@ App.on('second-instance', () => { }) }) } + + { + IPCMain.on('blob:read-convert', (_, data) => { + // Send the request to the background processes' renderer thread + views.backgroundProcesses.webContents.send('blob:read-convert', data) + + IPCMain.on(`blob:read-convert:result:${data.requestID}`, (_, data) => { + // Send the response to the renderer thread + views.main.webContents.send(`blob:read-convert:result:${data.requestID}`, data) + }) + }) + + IPCMain.on('blob:convert-write', (_, data) => { + // Send the request to the background processes' renderer thread + views.backgroundProcesses.webContents.send('blob:convert-write', data) + + IPCMain.on(`blob:convert-write:result:${data.requestID}`, (_, data) => { + // Send the response to the renderer thread + views.main.webContents.send(`blob:convert-write:result:${data.requestID}`, data) + }) + }) + } } /** @@ -924,6 +947,12 @@ App.on('second-instance', () => { try { item.click = eval(item.click) } catch (e) {} + + try { + item.icon = NativeImage.createFromPath(item.icon) + } catch (e) { + item.icon = '' + } } catch (e) {} } } diff --git a/package.json b/package.json index 59f0dfcd..c80751c6 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,9 @@ "main/bin", "config" ], - "publish": [ - { - "provider": "github" - } - ], + "publish": [{ + "provider": "github" + }], "artifactName": "AxonOps.Workbench-${version}-${os}-${arch}.${ext}", "win": { "icon": "./renderer/assets/images/axonops-icon-256x256.ico", @@ -347,11 +345,14 @@ "autosize": "^6.0.1", "axios": "^1.7.9", "better-sqlite3": "^11.8.1", - "byte-size": "^8.1.1", + "bytes": "^3.1.2", "chart.js": "^4.4.7", + "cheerio": "^1.0.0", "co": "^4.6.0", "configparser": "^0.3.10", + "convert-hex": "^0.1.0", "debug": "^4.4.0", + "detect-file-type": "^0.2.8", "diff": "^5.2.0", "dotenv": "^16.4.7", "electron-context-menu": "^3.6.1", @@ -363,6 +364,7 @@ "highlight.js": "^11.11.1", "i18next": "^22.4.9", "id-16": "^1.0.5", + "is-timestamp": "^1.0.0", "jquery": "^3.6.3", "jquery-sortablejs": "^1.6.1", "jquery.json-viewer": "^1.5.0", @@ -398,7 +400,9 @@ "tinykeys": "^2.1.0", "tree-kill": "^1.2.2", "url-join": "^5.0.0", + "uuid": "^11.0.5", "v8-compile-cache": "^2.3.0", + "validate-date": "^2.1.0", "value-size": "^1.0.1", "xterm-theme": "^1.1.0", "xterm-webfont": "^2.0.0", diff --git a/renderer/assets/images/icons/calendar.svg b/renderer/assets/images/icons/calendar.svg new file mode 100644 index 00000000..ad434fd9 --- /dev/null +++ b/renderer/assets/images/icons/calendar.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/renderer/assets/images/icons/function.svg b/renderer/assets/images/icons/function.svg new file mode 100644 index 00000000..2266748e --- /dev/null +++ b/renderer/assets/images/icons/function.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/renderer/assets/images/icons/time-outline.svg b/renderer/assets/images/icons/time-outline.svg new file mode 100644 index 00000000..53efba28 --- /dev/null +++ b/renderer/assets/images/icons/time-outline.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/renderer/js/background_processes.js b/renderer/js/background_processes.js index a1502827..797003b0 100755 --- a/renderer/js/background_processes.js +++ b/renderer/js/background_processes.js @@ -29,6 +29,11 @@ const FS = require('fs-extra'), * Working with file and directory paths, and providing useful utilities */ Path = require('path'), + /** + * Node.js OS module + * Used for operating system-related utilities and properties + */ + OS = require('os'), // JQuery library $ = require('jquery'), jQuery = $, @@ -53,7 +58,11 @@ const FS = require('fs-extra'), */ SSH2Utils = require('ssh2').utils, // JS module for text differencing implementation - Diff = require('diff') + Diff = require('diff'), + // Convert to/from HEX strings and byte arrays + ConvertHEX = require('convert-hex'), + // Sanitize a string to be safe for use as a file name; by removing directory paths and invalid characters + Sanitize = require('sanitize-filename') /** * Get the set extra resources path @@ -472,5 +481,49 @@ $(document).ready(() => IPCRenderer.on('extra-resources-path', (_, path) => { }) }) } + + // Handle `blob` content bi-directional conversion + { + IPCRenderer.on('blob:read-convert', (_, data) => { + let itemHEXString = '' + + try { + let itemBuffer = FS.readFileSync(data.itemPath) + + itemHEXString = ConvertHEX.bytesToHex(Array.from(itemBuffer)) + + itemHEXString = `0x${itemHEXString}` + } catch (e) {} + + IPCRenderer.send(`blob:read-convert:result:${data.requestID}`, { + itemHEXString, + requestID: data.requestID + }) + }) + + IPCRenderer.on('blob:convert-write', (_, data) => { + let itemTempFile = ``, + error = false + + try { + let blobBytes = ConvertHEX.hexToBytes(data.blobHEXString), + itemBuffer = Buffer.from(blobBytes) + + itemTempFile = Path.join(OS.tmpdir(), Sanitize(`preview_item_${data.randomID}`)) + + itemTempFile = `${itemTempFile}.${data.itemType}` + + FS.writeFileSync(itemTempFile, itemBuffer) + } catch (e) { + error = true + } finally { + IPCRenderer.send(`blob:convert-write:result:${data.requestID}`, { + itemTempFile, + error, + requestID: data.requestID + }) + } + }) + } } })) diff --git a/renderer/js/events/clusters.js b/renderer/js/events/clusters.js index b6bf8d24..bfa1e747 100755 --- a/renderer/js/events/clusters.js +++ b/renderer/js/events/clusters.js @@ -1560,7 +1560,7 @@ // Get the beautified version of the result let resultBeautified = applyJSONBeautify(result), // Get the result size - resultSize = ByteSize(ValueSize(resultBeautified)) + resultSize = Bytes(ValueSize(resultBeautified)) // Copy the result to the clipboard try { @@ -2095,6 +2095,37 @@ })`, visible: nodeType == 'udt' }, + { + label: I18next.capitalize(I18next.t('insert row as JSON')), + action: 'insertRow', + click: `() => views.main.webContents.send('insert-row', { + tableName: '${clickedNode.attr('name')}', + tables: '${JSON.stringify(keyspaceJSONObj.tables) || []}', + udts: '${JSON.stringify(keyspaceUDTs) || []}', + tabID: '_${cqlshSessionContentID}', + keyspaceName: '${keyspaceName}', + isCounterTable: '${clickedNode.attr('is-counter-table')}', + textareaID: '_${cqlshSessionStatementInputID}', + btnID: '_${executeStatementBtnID}', + asJSON: 'true' + })`, + visible: nodeType == 'table' && clickedNode.attr('is-counter-table') == 'false' + }, + { + label: I18next.capitalize(I18next.t('insert row')), + action: 'insertRow', + click: `() => views.main.webContents.send('insert-row', { + tableName: '${clickedNode.attr('name')}', + tables: '${JSON.stringify(keyspaceJSONObj.tables) || []}', + udts: '${JSON.stringify(keyspaceUDTs) || []}', + tabID: '_${cqlshSessionContentID}', + keyspaceName: '${keyspaceName}', + isCounterTable: '${clickedNode.attr('is-counter-table')}', + textareaID: '_${cqlshSessionStatementInputID}', + btnID: '_${executeStatementBtnID}' + })`, + visible: nodeType == 'table' && clickedNode.attr('is-counter-table') == 'false' + }, { label: I18next.capitalize(I18next.t('alter table')), action: 'alterTable', @@ -2166,6 +2197,7 @@ action: 'createKeyspace', click: `() => views.main.webContents.send('create-keyspace', { datacenters: '${getAttributes(clusterElement, 'data-datacenters')}', + keyspaces: '${JSON.stringify(metadata.keyspaces.map((keyspace) => keyspace.name))}', tabID: '_${cqlshSessionContentID}', textareaID: '_${cqlshSessionStatementInputID}', btnID: '_${executeStatementBtnID}' @@ -2540,7 +2572,7 @@ output: outputGroup }), // Get the content's size - contentSize = ByteSize(ValueSize(contentBeautified)) + contentSize = Bytes(ValueSize(contentBeautified)) // Copy content to the clipboard try { @@ -2885,7 +2917,7 @@ output: outputGroup }), // Get the content's size - contentSize = ByteSize(ValueSize(contentBeautified)) + contentSize = Bytes(ValueSize(contentBeautified)) // Copy content to the clipboard try { @@ -4206,7 +4238,7 @@ // Get the beautified version of the metadata let metadataBeautified = applyJSONBeautify(latestMetadata, true), // Get the metadata size - metadataSize = ByteSize(ValueSize(metadataBeautified)) + metadataSize = Bytes(ValueSize(metadataBeautified)) // Copy metadata to the clipboard try { @@ -4380,7 +4412,7 @@ // Reset the suffix value suffixInput.val('') suffixInputObject.update() - suffixInputObject._deactivate() + setTimeout(() => suffixInputObject._deactivate()) // Get the current date and time, format it, and show it to the user let time = parseInt($(`span[data-id="${newMetadataTimeID}"]`).attr('data-time')) || new Date().getTime() @@ -4501,7 +4533,7 @@
${snapshot.name}
${formatTimestamp(snapshot.time)} - ${ByteSize(snapshot.size)} + ${Bytes(snapshot.size)}
@@ -6074,7 +6106,7 @@ // Update the object object.update() - object._deactivate() + setTimeout(() => object._deactivate()) // If the current input is not a file selector then skip this try-catch block if ($(object._element).attr('file-name') == undefined) @@ -6190,7 +6222,7 @@ // Update the object object.update() - object._deactivate() + setTimeout(() => object._deactivate()) }) }) } catch (e) { @@ -6234,7 +6266,7 @@ // Update the object object.update() - object._deactivate() + setTimeout(() => object._deactivate()) // If the current input is not a file selector then skip this try-catch block if ($(object._element).attr('file-name') == undefined) @@ -8533,7 +8565,7 @@ } finally { // Update the object object_.update() - object_._deactivate() + setTimeout(() => object_._deactivate()) } }) }, 1000) @@ -8625,7 +8657,6 @@ // Point at the status element - the flashing circle at the top right - statusElement = clusterElement.children('div.status') - try { if (!secrets[0]) throw 0 @@ -9178,7 +9209,7 @@ setTimeout(() => { try { inputObject.update() - inputObject._deactivate() + setTimeout(() => inputObject._deactivate()) } catch (e) {} // Remove the initial indicator's attribute @@ -9298,6 +9329,8 @@ }) } + let updateActionStatusForInsertRow + // Handle the request of getting a CQL description of the cluster, keyspace in it, or a table { IPCRenderer.on('cql-desc:get', (_, data) => { @@ -9367,7 +9400,7 @@ throw 0 } - let descriptionSize = ByteSize(ValueSize(description)) + let descriptionSize = Bytes(ValueSize(description)) FS.writeFile(path, description, (err) => { if (err) @@ -9701,7 +9734,7 @@ }) IPCRenderer.on('drop-keyspace', (_, data) => { - let keyspaceName = `${data.keyspaceName}`, + let keyspaceName = addDoubleQuotes(`${data.keyspaceName}`), dropKeyspaceEditor = monaco.editor.getEditors().find((editor) => $(`div.modal#actionDataDrop .editor`).find('div.monaco-editor').is(editor.getDomNode())) if (minifyText(keyspaceName).length <= 0) @@ -9821,7 +9854,7 @@ // Get related data to the current UDT try { - let udt = JSON.parse(data.udts).find((udt) => udt.name == data.udtName), + let udt = JSON.parse(JSONRepair(data.udts)).find((udt) => udt.name == data.udtName), fields = []; for (let i = 0; i < udt.field_names.length; ++i) { @@ -9840,8 +9873,8 @@ }) IPCRenderer.on('drop-udt', (_, data) => { - let udtName = `${data.udtName}`, - keyspaceName = `${data.keyspaceName}`, + let udtName = addDoubleQuotes(`${data.udtName}`), + keyspaceName = addDoubleQuotes(`${data.keyspaceName}`), dropUDTEditor = monaco.editor.getEditors().find((editor) => $(`div.modal#actionDataDrop .editor`).find('div.monaco-editor').is(editor.getDomNode())) if ([udtName, keyspaceName].some((name) => minifyText(name).length <= 0)) @@ -9929,7 +9962,7 @@ // For UDT columns try { - let keyspaceUDTs = JSON.parse(data.udts) + let keyspaceUDTs = JSON.parse(JSONRepair(data.udts)) if (keyspaceUDTs.length > 0) throw 0 @@ -10040,7 +10073,7 @@ tableOptions = [] try { - tableObj = JSON.parse(data.tables).find((table) => table.name == tableName) + tableObj = JSON.parse(JSONRepair(data.tables)).find((table) => table.name == tableName) } catch (e) {} try { @@ -10126,7 +10159,7 @@ let keyspaceUDTs try { - tableObj = JSON.parse(data.tables).find((table) => table.name == tableName) + tableObj = JSON.parse(JSONRepair(data.tables)).find((table) => table.name == tableName) } catch (e) {} try { @@ -10150,7 +10183,7 @@ } catch (e) {} try { - keyspaceUDTs = JSON.parse(data.udts), + keyspaceUDTs = JSON.parse(JSONRepair(data.udts)), filteredColumns = tableObj.columns.filter((column) => tableObj.primary_key.find((key) => key.name == column.name) == undefined) for (let column of filteredColumns) { @@ -10229,8 +10262,8 @@ }) IPCRenderer.on('drop-table', (_, data) => { - let tableName = `${data.tableName}`, - keyspaceName = `${data.keyspaceName}`, + let tableName = addDoubleQuotes(`${data.tableName}`), + keyspaceName = addDoubleQuotes(`${data.keyspaceName}`), dropUDTEditor = monaco.editor.getEditors().find((editor) => $(`div.modal#actionDataDrop .editor`).find('div.monaco-editor').is(editor.getDomNode())) if ([tableName, keyspaceName].some((name) => minifyText(name).length <= 0)) @@ -10278,2022 +10311,4018 @@ } catch (e) {} }) }) - } - } - - // Handle the request of closing a cluster's work area - { - IPCRenderer.on('workarea:close', (_, data) => $(`div.btn[data-id="${data.btnID}"]`).trigger('click', false)) - } - { - setTimeout(() => { - // Define the scroll value, by default, the current scroll value is `0` - at the top of the dialog - - let scrollValue = 0, - dialogElement = $(`div.modal#rightClickActionsMetadata`), - actionEditor = monaco.editor.getEditors().find((editor) => dialogElement.find('div.action-editor div.editor div.monaco-editor').is(editor.getDomNode())), - updateActionStatusForKeyspaces = () => { - let replicationStrategy = $('input#keyspaceReplicationStrategy').val(), - keyspaceName = $('input#keyspaceName').val(), - durableWrites = $('input#keyspaceDurableWrites').prop('checked'), - replication = {}, - isRFAcceptable = false + let tableFieldsTreeContainers = { + primaryKey: $('div#tableFieldsPrimaryKeysTree'), + columnsRegular: $('div#tableFieldsRegularColumnsTree'), + columnsCollection: $('div#tableFieldsCollectionColumnsTree'), + columnsUDT: $('div#tableFieldsUDTColumnsTree') + } - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + let effectedNodes = {} - isAlterState = isAlterState != null && isAlterState == 'alter' + IPCRenderer.on('insert-row', (_, data) => { + Modules.Config.getConfig((config) => { + let enableBlobPreview = false try { - if (dialogElement.find('div[action="keyspaces"]').find('.is-invalid:not(.ignore-invalid)').length <= 0) - throw 0 + enableBlobPreview = config.get('features', 'previewBlob') == 'true' + } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') + effectedNodes = {} - return - } catch (e) {} + let rightClickActionsMetadataModal = getElementMDBObject($('#rightClickActionsMetadata'), 'Modal'), + isInsertionAsJSON = data.asJSON === 'true' - try { - if (minifyText(replicationStrategy) != 'simplestrategy') - throw 0 + $('button#executeActionStatement').attr({ + 'data-tab-id': `${data.tabID}`, + 'data-textarea-id': `${data.textareaID}`, + 'data-btn-id': `${data.btnID}` + }) - let replicationFactor = $('input#keyspaceReplicationFactorSimpleStrategy').val() + $('#rightClickActionsMetadata').attr({ + 'data-state': null, + 'data-keyspace-name': `${data.keyspaceName}`, + 'data-table-name': `${data.tableName}`, + 'data-as-json': `${!isInsertionAsJSON ? null : 'true'}` + }) - replication.class = 'SimpleStrategy' - replication.replication_factor = replicationFactor - } catch (e) {} + $('div.row.default-omitted-columns-value-row').toggle(isInsertionAsJSON) - try { - if (minifyText(replicationStrategy) != 'networktopologystrategy') - throw 0 + $('div.row.default-omitted-columns-value-row').find('a[value="UNSET"]').click() - let replicationFactor = $('input#keyspaceReplicationFactorSimpleStrategy').val() + $('input#ttl').val('') + $('input#ttlMS').prop('checked', true) + $('input#insertionTimestamp').val('') - replication.class = 'NetworkTopologyStrategy' + $('div.dropdown[for-select="writeConsistencyLevel"]').find(`a[value="NOT SET"]`).click() - let dataCenters = dialogElement.find('div[action="keyspaces"]').find('div[for-strategy="NetworkTopologyStrategy"]').children('div.data-centers').find('div.data-center') - for (let dataCenter of dataCenters) { - dataCenter = $(dataCenter) - let rf = dataCenter.find('input[type="number"]').val() + $('#rightClickActionsMetadata').find('h5.modal-title').children('span').attr('mulang', 'insert row').html(`${I18next.capitalize(I18next.t('insert row'))} ${isInsertionAsJSON ? '(JSON)' : ''} ${data.keyspaceName}.${data.tableName}`) - if (rf <= 0) - continue + $('#rightClickActionsMetadata').attr('data-keyspace-tables', `${data.tables}`) - isRFAcceptable = true + $('#rightClickActionsMetadata').attr('data-keyspace-udts', `${data.udts}`) - replication[dataCenter.attr('data-datacenter')] = rf + try { + for (let container of Object.keys(tableFieldsTreeContainers)) { + try { + tableFieldsTreeContainers[container].jstree('destroy') + } catch (e) {} } + } catch (e) {} - if (isAlterState) - isRFAcceptable = true + let keys = [], + columns = [], + udts = [], + keyspaceUDTs = [] - dialogElement.find('div.row.invalid-text-container').toggleClass('show', !isRFAcceptable) + try { + let tableObj = JSON.parse(JSONRepair(data.tables)).find((table) => table.name == data.tableName) - let invalidState = !isAlterState && (!isRFAcceptable || ($('input#keyspaceName').hasClass('is-invalid') || `${$('input#keyspaceName').val()}`.length <= 0)) + try { + keys = tableObj.primary_key.map((key) => { + let isPartition = tableObj.partition_key.find((partitionKey) => partitionKey.name == key.name) != undefined - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', invalidState ? '' : null) + return { + name: key.name, + type: key.cql_type, + isPartition + } + }) + } catch (e) {} - if (invalidState) - return - } catch (e) {} + try { + columns = tableObj.columns.filter((column) => tableObj.primary_key.find((key) => key.name == column.name) == undefined) + .map((column) => { + return { + name: column.name, + type: column.cql_type + } + }) + } catch (e) {} - let durableWritesSetValue = $('input#keyspaceDurableWrites').attr('set-value'), - replicationStrategySetValue = '' + try { + try { + keyspaceUDTs = JSON.parse(JSONRepair(data.udts)) + } catch (e) {} - try { - replicationStrategySetValue = repairJSON(JSON.parse(repairJSON($('#rightClickActionsMetadata').attr('data-keyspace-info'))).replication_strategy) - } catch (e) {} + for (let column of columns) { + let udtStructure = {}, + manipulatedColumnType = column.type - let durableWritesFinal = '', - replicationStrategyFinal = '' + manipulatedColumnType = removeFrozenKeyword(`${manipulatedColumnType}`) - try { - if (isAlterState && minifyText(replicationStrategySetValue) == minifyText(JSON.stringify(replication))) - throw 0 + try { + manipulatedColumnType = `${manipulatedColumnType}`.match(/<(.*?)>$/)[1]; + } catch (e) {} - replicationStrategyFinal = OS.EOL + `WITH replication = ${JSON.stringify(replication).replace(/"/gm, "'")}` - } catch (e) {} + let udtObject = keyspaceUDTs.find((udt) => udt.name == manipulatedColumnType) - try { - if (isAlterState && `${durableWritesSetValue}` == `${durableWrites}`) - throw 0 + if (udtObject == undefined || ['map', 'set', 'list'].some((type) => `${column.type}`.includes(`${type}<`))) + continue - durableWritesFinal = OS.EOL + (replicationStrategyFinal.length <= 0 ? 'WITH' : 'AND') + ` durable_writes = ${durableWrites}` - } catch (e) {} + udtStructure = { + ...udtObject, + ...column + } - try { - if (!isAlterState) - throw 0 + udts.push(udtStructure) + } - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', [replicationStrategyFinal, durableWritesFinal].every((str) => `${str}`.length <= 0) ? '' : null) + columns = columns.filter((column) => udts.find((udt) => udt.name == column.name) == undefined) + } catch (e) {} } catch (e) {} - let statement = `${isAlterState ? 'ALTER' : 'CREATE'} KEYSPACE ${keyspaceName}${replicationStrategyFinal}${durableWritesFinal};` + let groupStructure = buildTableFieldsTreeview(keys, columns, udts, keyspaceUDTs, enableBlobPreview), + handleHiddenNodes = (treeData) => { + let index = 0 - try { - actionEditor.setValue(statement) - } catch (e) {} - } + while (index < treeData.length) { + const node = treeData[index] - $('input#keyspaceName').on('input', function() { - let keyspaces = [], - keyspaceName = $(this).val(), - isNameDuplicated = false, - isNameInvalid = false, - invalidFeedback = $(this).parent().children('div.invalid-feedback'), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + try { + if (Array.isArray(node)) { + processArray(node) - isAlterState = isAlterState != null && isAlterState == 'alter' + index++ + continue + } - $(this).attr('disabled', isAlterState ? '' : null) - $(this).parent().toggleClass('invalid-warning', isAlterState) - $(this).toggleClass('is-invalid ignore-invalid', isAlterState) + if (node.a_attr['add-hidden-node'] !== undefined) { + treeData.splice(index + 1, 0, { + id: node.a_attr['add-hidden-node'], + parent: node.parent, + state: { + opened: true, + selected: false + }, + text: `` + }) - try { - if (!isAlterState) - throw 0 + index++ + } + } catch (e) {} - invalidFeedback.find('span').attr('mulang', 'the keyspace name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the keyspace name can\'t be altered'))) + index++ - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + } + return treeData; + }, + handleNodeCreationDeletion = () => { + let allInsertionRelatedInputs = $('#rightClickActionsMetadata').find('div[action="insert-row"]').find('input').get() + + for (let inputDOM of allInsertionRelatedInputs) { + let input = $(inputDOM), + [ + inputCassandraType, + inputHTMLType, + inputID + ] = getAttributes(input, ['data-field-type', 'type', 'id']), + inputSavedValue = effectedNodes[inputID] || '' + + if (inputSavedValue == undefined || inputSavedValue.length <= 0) + continue - return - } catch (e) {} + try { + if (inputHTMLType != 'checkbox') + throw 0 + input.prop('checked', !(inputSavedValue != 'true')).trigger('change') - try { - keyspaces = JSON.parse($('#rightClickActionsMetadata').attr('data-keyspaces')) - } catch (e) {} + continue + } catch (e) {} - try { - if (keyspaces.length <= 0) - throw 0 + try { + if (inputCassandraType != 'blob') + throw 0 - isNameDuplicated = keyspaces.some((keyspace) => `${keyspace}` == `${keyspaceName}`) + input.val(inputSavedValue.inputValue).trigger('input') - if (isAlterState && isNameDuplicated && (keyspaceName == $('div.modal#rightClickActionsMetadata').attr('data-keyspacename'))) - isNameDuplicated = false + input.data('value', inputSavedValue.dataValue) - if (isNameDuplicated) - invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) - } catch (e) {} + continue + } catch (e) {} - try { - if (`${keyspaceName}`.length <= 0) - throw 0 + // Not `blob` or `boolean` + try { + input.val(inputSavedValue).trigger('input') + } catch (e) {} + } - isNameInvalid = `${keyspaceName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + } - if (isNameInvalid) - invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) - } catch (e) {} + try { + for (let treeViewType of Object.keys(groupStructure)) + groupStructure[treeViewType].core.data = handleHiddenNodes(groupStructure[treeViewType].core.data) + } catch (e) {} - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) + { + let primaryKeyTreeElements = tableFieldsTreeContainers.primaryKey.add('div#insertPrimaryKeyBadge') - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 ? '' : null) + try { + if (keys.length <= 0) + throw 0 - try { - updateActionStatusForKeyspaces() - } catch (e) {} - }) + primaryKeyTreeElements.show() - $('input#keyspaceReplicationStrategy').on('input', function() { - let replicationStrategy = $(this).val(), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + tableFieldsTreeContainers.primaryKey.jstree(groupStructure.primaryKey) + } catch (e) { + primaryKeyTreeElements.hide() + } + } - isAlterState = isAlterState != null && isAlterState == 'alter' + { + let columnsRegularTreeElements = tableFieldsTreeContainers.columnsRegular.add('div#insertRegularColumnsBadge') - setTimeout(() => { - dialogElement.find('div[for-strategy]').hide() + try { + if (!(columns.some((column) => ['map', 'set', 'list'].every((type) => !(`${column.type}`.includes(`${type}<`)))))) + throw 0 - let networkTopologyContainer = dialogElement.find('div[for-strategy="NetworkTopologyStrategy"]'), - simpleStrategyContainer = dialogElement.find('div[for-strategy="SimpleStrategy"]'), - dataCentersContainer = networkTopologyContainer.children('div.data-centers'), - dataCenters = [] + columnsRegularTreeElements.show() - try { - dataCenters = JSON.parse($(this).attr('data-datacenters')) - } catch (e) {} + tableFieldsTreeContainers.columnsRegular.jstree(groupStructure.regularColumns) + } catch (e) { + columnsRegularTreeElements.hide() + } + } - try { - dataCenters = dataCenters.filter((datacenter, index, datacenters) => datacenters.findIndex(_datacenter => _datacenter.name === datacenter.name) === index) - } catch (e) {} + { + let columnsCollectionTreeElements = tableFieldsTreeContainers.columnsCollection.add('div#insertCollectionColumnsBadge') - $(this).parent().find('div.invalid-feedback span[mulang][capitalize-first]').attr('mulang', 'SimpleStrategy is intended for development purposes only').text(I18next.capitalizeFirstLetter(I18next.t('SimpleStrategy is intended for development purposes only'))) + try { + if (!(columns.some((column) => ['map', 'set', 'list'].some((type) => `${column.type}`.includes(`${type}<`))))) + throw 0 - $(this).parent().css('margin-bottom', '50px') + columnsCollectionTreeElements.show() - try { - if (minifyText(replicationStrategy) == minifyText('simplestrategy')) - throw 0 + tableFieldsTreeContainers.columnsCollection.jstree(groupStructure.collectionColumns) + } catch (e) { + columnsCollectionTreeElements.hide() + } + } - $(this).removeClass('is-invalid') + { + let columnsUDTTreeElements = tableFieldsTreeContainers.columnsUDT.add('div#insertUDTColumnsBadge') + try { + if (udts.length <= 0) + throw 0 - $('input#keyspaceReplicationFactorSimpleStrategy').val(1).trigger('input') + columnsUDTTreeElements.show() - try { - dataCentersContainer.children('div.row.data-center').remove() + tableFieldsTreeContainers.columnsUDT.jstree(groupStructure.udtColumns) + } catch (e) { + columnsUDTTreeElements.hide() + } + } - let dataCentersRF = {} + setTimeout(() => { + for (let container of Object.keys(tableFieldsTreeContainers)) { + let tableFieldsTreeContainer = tableFieldsTreeContainers[container] try { - if (isAlterState) - dataCentersRF = JSON.parse($('div.modal#rightClickActionsMetadata').attr('data-datacenters-rf')) + tableFieldsTreeContainer.unbind('loaded.jstree') + tableFieldsTreeContainer.unbind('select_node.jstree') + tableFieldsTreeContainer.unbind('create_node.jstree') + tableFieldsTreeContainer.unbind('delete_node.jstree') + tableFieldsTreeContainer.unbind('hide_node.jstree') } catch (e) {} - try { - dataCentersRF = dataCentersRF.filter((datacenter, index, datacenters) => datacenters.findIndex(_datacenter => _datacenter.name === datacenter.name) === index) - } catch (e) {} - - for (let datacenter of dataCenters) { - let replicationFactor = parseInt(dataCentersRF[datacenter.datacenter]) || 0 - - try { - if (!isAlterState || dataCenters.length > 1 || dataCentersRF.class == 'NetworkTopologyStrategy') - throw 0 + tableFieldsTreeContainer.on('loaded.jstree', () => { + tableFieldsTreeContainer.find('input').each(function() { + setTimeout(() => { + try { + let mdbObject = getElementMDBObject($(this)) - replicationFactor = parseInt($('div.modal#rightClickActionsMetadata').attr('data-rf')) || 0 - } catch (e) {} + mdbObject.update() + } catch (e) {} + }, 100) + }) - let element = ` -
-
${datacenter.datacenter}
-
-
- - -
-
-
` - dataCentersContainer.append($(element).show(function() { - let dataCenter = $(this) + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) - setTimeout(() => { - dataCenter.find('input').each(function() { - getElementMDBObject($(this)) - }) + tableFieldsTreeContainer.find('a.jstree-anchor[add-hidden-node]').each(function() { + let hiddenNode = tableFieldsTreeContainer.find(`li.jstree-node[id="${$(this).attr('add-hidden-node')}"]`) - dataCenter.find('input[type="number"]').on('input', function() { - let rf = parseInt($(this).val()), - isInvalid = isNaN(rf) || rf < 0 + hiddenNode.css('margin-top', '-45px') - $(this).toggleClass('is-invalid', isInvalid) + hiddenNode.children('a').css('pointer-events', 'none') + }) - if (minifyText($('input#keyspaceName').val()).length <= 0) - return + setTimeout(() => { + { + let allActionsMenuToggleBtns = tableFieldsTreeContainer.find('button.dropdown-toggle').get() - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isInvalid ? '' : null) + for (let toggleBtn of allActionsMenuToggleBtns) { + let actionsDropDownObject = getElementMDBObject($(toggleBtn), 'Dropdown'), + actionsDropDownElement = $(actionsDropDownObject._menu) try { - updateActionStatusForKeyspaces() + $(toggleBtn).unbind('click') } catch (e) {} - }) - }) - - setTimeout(() => Modules.Localization.applyLanguageSpecific(dataCenter.find('span[mulang], [data-mulang]'))) - })) - } - } catch (e) {} - networkTopologyContainer.show() + $(toggleBtn).click(() => { + for (let subToggleBtn of allActionsMenuToggleBtns) { + if ($(subToggleBtn).is($(toggleBtn))) + continue - try { - updateActionStatusForKeyspaces() - } catch (e) {} + let subActionsDropDownObject = getElementMDBObject($(subToggleBtn), 'Dropdown'), + subActionsDropDownElement = $(subActionsDropDownObject._menu) - return - } catch (e) {} + try { + subActionsDropDownElement.hide() + subActionsDropDownObject.hide() + } catch (e) {} + } - setTimeout(() => $(this).addClass('is-invalid'), 250) + setTimeout(() => { + actionsDropDownElement.toggle() + actionsDropDownObject.toggle() + }) - try { - if (!isAlterState) - throw 0 + setTimeout(() => { + actionsDropDownElement.oneClickOutside({ + callback: function() { + try { + actionsDropDownElement.hide() + actionsDropDownObject.hide() + } catch (e) {} + }, + exceptions: toggleBtn + }) + }) - $('input#keyspaceReplicationFactorSimpleStrategy').val(parseInt($('div.modal#rightClickActionsMetadata').attr('data-rf')) || 1) + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) + } + } - $(`div.modal#rightClickActionsMetadata`).find('div.data-center').find('input[type="number"]').val(0).trigger('input') + { + let allAddItemActionBtns = tableFieldsTreeContainer.find('button[action="add-item"]').get() - if (dataCenters.length > 1) { - $(this).parent().find('div.invalid-feedback span[mulang][capitalize-first]').attr('mulang', 'avoid switching from SimpleStrategy to NetworkTopologyStrategy in multi-DC clusters; plan carefully').text(I18next.capitalizeFirstLetter(I18next.t('avoid switching from SimpleStrategy to NetworkTopologyStrategy in multi-DC clusters; plan carefully'))) + for (let addItemActionBtn of allAddItemActionBtns) { + try { + $(addItemActionBtn).unbind('click') + } catch (e) {} - $(this).parent().css('margin-bottom', '70px') - } - } catch (e) {} + $(addItemActionBtn).click(function() { + let relatedNode = $(this).parent().parent().parent(), + [ + id, + type, + fieldType, + mandatory, + hiddenNodeID + ] = getAttributes(relatedNode, ['id', 'type', 'field-type', 'mandatory', 'add-hidden-node']), + isTypeMap = `${type}`.includes('map<'), + relatedTreeObject = relatedNode.closest('div.table-fields-tree').jstree(), + relatedHiddenNode = relatedTreeObject.element.find(`li[id="hiddenNodeID"]`) - dialogElement.find('div[for-strategy="SimpleStrategy"]').show() + try { + type = removeFrozenKeyword(`${type}`) + } catch (e) {} - try { - updateActionStatusForKeyspaces() - } catch (e) {} - }, 150) - }) + try { + type = `${type}`.match(/<(.*?)>$/)[1] + } catch (e) {} - $('input#keyspaceReplicationFactorSimpleStrategy').on('input', function() { - let rf = parseInt($(this).val()), - isInvalid = isNaN(rf) || rf < 1 + try { + if (!isTypeMap) + throw 0 - $('input#keyspaceReplicationFactorSimpleStrategy').toggleClass('is-invalid', isInvalid) + type = `${type}`.replace(/\s+/, '').split(',') - if (minifyText($('input#keyspaceName').val()).length <= 0) - return + type = type.map((subType) => { + try { + subType = removeFrozenKeyword(`${subType}`) + } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isInvalid ? '' : null) + subType = subType.replace(/(.*?)(.*?)/gi, '') - try { - updateActionStatusForKeyspaces() - } catch (e) {} - }) + return subType + }) - // Clicks the `SWITCH TO EDITOR` button - dialogElement.find('button.switch-editor').click(function() { - // Point at the dialog's content element - let dialogBody = dialogElement.find('div.modal-body'), - // Determine if the editor is visible/shown or not - editorShown = dialogElement.hasClass('show-editor'), - currentActiveAction = $('div.modal#rightClickActionsMetadata div[action]').filter(':visible').attr('action') + } catch (e) {} - // Add the `show-editor` class if it is not added, otherwise it'll be removed - dialogElement.toggleClass('show-editor', !editorShown) + try { + if (typeof type != 'object') + throw 0 - // Update the current scroll value if the editor is not shown already, otherwise, keep the current saved value - scrollValue = !editorShown ? dialogBody[0].scrollTop : scrollValue + let itemMainNodeID = getRandomID(30), + itemMainNodeStrucutre = { + id: itemMainNodeID, + parent: hiddenNodeID, + state: { + opened: true, + selected: false + }, + a_attr: { + 'is-map-item': true + }, + text: ` +
+
+ + + + + Item #${getRandomID(3)} +
+
+ + + + + +
+
` + } - // Either scroll to the last saved position if the editor is visible, or scroll to the top of the dialog for the editor before showing it - dialogBody[0].scrollTo(0, editorShown ? scrollValue : 0) + let mapKeyUDTObject = keyspaceUDTs.find((udt) => udt.name == type[0]), + mapKeyObject = { + isUDT: mapKeyUDTObject != undefined, + name: ``, + type: type[0], + fieldType: 'collection-map-key', + isMandatory: false, + noEmptyValue: true + }, + mapKeyStructure = buildTableFieldsTreeview([], [], [], keyspaceUDTs, enableBlobPreview, mapKeyObject.isUDT ? { + ...mapKeyUDTObject, + ...mapKeyObject + } : mapKeyObject) + + let mapValueUDTObject = keyspaceUDTs.find((udt) => udt.name == type[1]), + mapValueObject = { + isUDT: mapValueUDTObject != undefined, + name: ``, + type: type[1], + fieldType: 'collection-map-value', + isMandatory: false, + noEmptyValue: true + }, + mapValueStructure = buildTableFieldsTreeview([], [], [], keyspaceUDTs, enableBlobPreview, mapValueObject.isUDT ? { + ...mapValueUDTObject, + ...mapValueObject + } : mapValueObject) - // Update the editor's layout - $(window.visualViewport).trigger('resize') + try { + mapKeyStructure.parent = itemMainNodeID + } catch (e) {} - try { - if (currentActiveAction != 'keyspaces') - throw 0 + try { + mapValueStructure.parent = itemMainNodeID + } catch (e) {} - updateActionStatusForKeyspaces() - } catch (e) {} + let allNewNodes = [] - try { - if (currentActiveAction != 'udts') - throw 0 + try { + if (Array.isArray(itemMainNodeStrucutre)) { + allNewNodes = allNewNodes.concat(itemMainNodeStrucutre) + } else { + allNewNodes.push(itemMainNodeStrucutre) + } + } catch (e) {} - updateActionStatusForUDTs() - } catch (e) {} + try { + if (Array.isArray(mapKeyStructure)) { + allNewNodes = allNewNodes.concat(mapKeyStructure) + } else { + allNewNodes.push(mapKeyStructure) + } + } catch (e) {} - try { - if (currentActiveAction != 'counter-tables') - throw 0 + try { + if (Array.isArray(mapValueStructure)) { + allNewNodes = allNewNodes.concat(mapValueStructure) + } else { + allNewNodes.push(mapValueStructure) + } + } catch (e) {} - updateActionStatusForCounterTables() - } catch (e) {} + try { + allNewNodes = flattenArray(allNewNodes) + } catch (e) {} - try { - if (currentActiveAction != 'standard-tables') - throw 0 + try { + allNewNodes = handleHiddenNodes(allNewNodes) + } catch (e) {} - updateActionStatusForStandardTables() - } catch (e) {} - }) + for (let i = 0; i < allNewNodes.length; i++) { + let node = allNewNodes[i] - $('input[type="checkbox"]#keyspaceDurableWrites').on('change', function() { - try { - updateActionStatusForKeyspaces() - } catch (e) {} - }) + try { + if (node.parent != '#') + throw 0 - $('button#executeActionStatement').click(function() { - let currentActiveAction = $('div.modal#rightClickActionsMetadata div[action]').filter(':visible').attr('action') + allNewNodes[i].parent = itemMainNodeID + } catch (e) {} - try { - if (currentActiveAction != 'keyspaces') - throw 0 + try { + let nodeText = Cheerio.load(allNewNodes[i].text) - updateActionStatusForKeyspaces() - } catch (e) {} + try { + nodeText('body').find('div.input-group-text.for-not-ignoring').attr('hidden', '') + } catch (e) {} - try { - if (currentActiveAction != 'udts') - throw 0 + allNewNodes[i].text = nodeText('body').html() + } catch (e) {} - updateActionStatusForUDTs() - } catch (e) {} + try { + relatedTreeObject.create_node(allNewNodes[i].parent, allNewNodes[i]) + } catch (e) {} - try { - if (currentActiveAction != 'counter-tables') - throw 0 + try { + handleNodeCreationDeletion() + } catch (e) {} + } - updateActionStatusForCounterTables() - } catch (e) {} + return + } catch (e) {} - try { - if (currentActiveAction != 'standard-tables') - throw 0 + let nodeUDTType = keyspaceUDTs.find((udt) => udt.name == type), + nodeTypeObject = { + isUDT: nodeUDTType != undefined, + name: ``, + type: type, + fieldType: 'collection-type', + isMandatory: false, + noEmptyValue: true + }, + nodeTypeStructure = buildTableFieldsTreeview([], [], [], keyspaceUDTs, enableBlobPreview, nodeTypeObject.isUDT ? { + ...nodeUDTType, + ...nodeTypeObject + } : nodeTypeObject) - updateActionStatusForStandardTables() - } catch (e) {} + try { + nodeTypeStructure = Array.isArray(nodeTypeStructure) ? flattenArray(nodeTypeStructure) : [nodeTypeStructure] + } catch (e) {} - try { - getElementMDBObject($(`a.nav-link.btn[href="#${$(this).attr('data-tab-id')}"]`), 'Tab').show() - } catch (e) {} + try { + nodeTypeStructure = handleHiddenNodes(nodeTypeStructure) + } catch (e) {} - let activeWorkarea = $(`div.body div.right div.content div[content="workarea"] div.workarea[cluster-id="${activeClusterID}"]`) + for (let i = 0; i < nodeTypeStructure.length; i++) { + try { + if (nodeTypeStructure[i].parent != '#') + throw 0 - try { - activeWorkarea.find('div.terminal-container').hide() - activeWorkarea.find('div.interactive-terminal-container').show() - } catch (e) {} + let nodeText = Cheerio.load(nodeTypeStructure[i].text), + deleteItemBtn = ` + ` - try { - let statementInputField = $(`textarea#${$(this).attr('data-textarea-id')}`) - statementInputField.val(actionEditor.getValue()) - statementInputField.trigger('input').focus() - AutoSize.update(statementInputField[0]) - } catch (e) {} + try { + nodeText('body').find('div.input-group-text.for-not-ignoring').attr('hidden', '') + } catch (e) {} - try { - setTimeout(() => $(`button#${$(this).attr('data-btn-id')}`).click(), 100) - } catch (e) {} + let actionsGroup = nodeText('div.input-group-text.for-actions') + + if (actionsGroup.length != 0) { + actionsGroup.find('button').before(deleteItemBtn) + } else { + let actionsGroupContainer = ` +
+ + + + + ${deleteItemBtn} +
` + + nodeText('div.input-group').append(actionsGroupContainer) + } - try { - setTimeout(() => getElementMDBObject($('#rightClickActionsMetadata'), 'Modal').hide(), 50) - } catch (e) {} - }) + nodeTypeStructure[i].text = nodeText('body').html() - let updateActionStatusForUDTs + nodeTypeStructure[i].parent = hiddenNodeID + } catch (e) {} - { - let getFieldElement = (keyspaceUDTs = []) => { - let typesList = ` -
  • -
  • int
  • -
  • bigint
  • -
  • smallint
  • -
  • tinyint
  • -
  • varint
  • -
  • float
  • -
  • double
  • -
  • decimal
  • -
  • -
  • text
  • -
  • varchar
  • -
  • ascii
  • -
  • -
  • boolean
  • -
  • -
  • timestamp
  • -
  • date
  • -
  • time
  • -
  • -
  • blob
  • -
  • -
  • uuid
  • -
  • timeuuid
  • -
  • -
  • inet
  • `, - collectionsTypesItems = ` -
  • -
  • list<type>
  • -
  • set<type>
  • -
  • map<key_type, value_type>
  • ` - defaultType = 'text' - - try { - if (keyspaceUDTs == null || keyspaceUDTs.length <= 0) - throw 0 + try { + relatedTreeObject.create_node(nodeTypeStructure[i].parent, { + ...nodeTypeStructure[i] + }) + } catch (e) {} - typesList = '' + try { + handleNodeCreationDeletion() + } catch (e) {} + } + }) + } - defaultType = keyspaceUDTs[0] + let allDeleteItemActionBtns = tableFieldsTreeContainer.find('button[action="delete-item"]').get() - for (let udt of keyspaceUDTs) - typesList += `
  • ${udt}
  • ` - } catch (e) {} + for (let deleteItemActionBtn of allDeleteItemActionBtns) { + try { + $(deleteItemActionBtn).unbind('click') + } catch (e) {} - let [ - collectionKeyTypeID, - collectionItemTypeID, - fieldDataTypeID - ] = getRandomID(10, 3).map((id) => `_${id}`), - element = ` -
    0 ? 'for-udt-data-field' : ''}> -
    -
    - - -
    -
    -
    -
    - - - -
    -
    -
    - -
    - - -
    - - - -
    -
    ` + $(deleteItemActionBtn).click(function() { + let relatedNode = $(this).parent().parent().parent(), + relatedTreeObject = relatedNode.closest('div.table-fields-tree').jstree() - return element - }, - updateRowsZIndex = (isTransformNegative = false) => { - setTimeout(() => { - let rows = dialogElement.find('div.data-field.row').get(), - rowsCount = rows.length + try { + let deletedNode = relatedTreeObject.get_node(relatedNode) - if (isTransformNegative) - rows = rows.reverse() + relatedTreeObject.delete_node(deletedNode) - for (let row of rows) { - $(row).css('z-index', `${rowsCount}`) + try { + handleNodeCreationDeletion() + } catch (e) {} + } catch (e) {} + }) + } + } - rowsCount -= 1 - } - }) - } + { + let AllIgnoreCheckboxes = tableFieldsTreeContainer.find('div.not-ignore-checkbox').get() - updateActionStatusForUDTs = () => { - let udtName = $('input#udtName').val(), - keyspaceName = dialogElement.find('div[action="udts"]').find('div.keyspace-name').text(), - allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row'), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state'), - dataFieldsText = '' + for (let ignoreCheckbox of AllIgnoreCheckboxes) { + try { + $(ignoreCheckbox).unbind('click') + } catch (e) {} - isAlterState = isAlterState != null && isAlterState == 'alter' + $(ignoreCheckbox).click(function() { + let relatedNode = $(this).closest('a.jstree-anchor'), + relatedTreeObject = relatedNode.closest('div.table-fields-tree').jstree(), + relatedNodeObject = relatedTreeObject.get_node(relatedNode) - try { - if (dialogElement.find('div[action="udts"]').find('.is-invalid:not(.ignore-invalid)').length <= 0 && - dialogElement.find('div[action="udts"]').find('div.data-field.row').length > 0 && - minifyText(udtName).length > 0) - throw 0 + $(this).attr('data-status', `${$(this).attr('data-status') == 'true' ? 'false' : 'true'}`) - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') + let checkBoxStatus = $(this).attr('data-status') - return - } catch (e) {} + relatedNode.toggleClass('ignored', checkBoxStatus != 'true') - try { - if (!isAlterState) - throw 0 + let allChildrenNodes = relatedNodeObject.children_d - let statements = [] + try { + if (relatedNode.attr('add-hidden-node') == undefined) + throw 0 - for (let row of dialogElement.find('div[action="udts"]').find('div.data-field.row')) { - let rowElement = $(row), - fieldName = rowElement.find('input.fieldName').attr('data-original-value'), - fieldType = rowElement.find('input.fieldDataType').attr('data-original-value') + let hiddenNodeObject = relatedTreeObject.get_node(`${relatedNode.attr('add-hidden-node')}`) - // Deleting a field is the first thing to be checked - if (rowElement.hasClass('deleted')) { - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} DROP ${fieldName};`) - continue - } + allChildrenNodes = allChildrenNodes.concat(hiddenNodeObject.children_d) + } catch (e) {} - // The type of the field has been changed - { - let isChangeInTypeDetected = false + for (let childrenNodeID of allChildrenNodes) { + let childrenNode = $(`a[id="${childrenNodeID}_anchor"]`) - try { - for (let typeRelatedField of [rowElement.find('input.fieldDataType'), rowElement.find('input.collectionItemType'), rowElement.find('input.collectionKeyType')]) { - let setValue = typeRelatedField.val(), - originalValue = typeRelatedField.attr('data-original-value') + childrenNode.toggleClass('ignored', checkBoxStatus != 'true') + } - if (setValue != originalValue && originalValue != undefined) { - isChangeInTypeDetected = true - break + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) } } - } catch (e) {} - try { - if (!isChangeInTypeDetected) - throw 0 + { + let allClearFieldBtns = tableFieldsTreeContainer.find('div.clear-field div.btn').get() - // Second is altering the type of the field - let newFieldType = rowElement.find('input.fieldDataType').val() + for (let clearFieldBtn of allClearFieldBtns) { + try { + $(clearFieldBtn).unbind('click') + } catch (e) {} - if (!([newFieldType, fieldName].some((data) => data == undefined))) { - try { - if (['map', 'set', 'list'].some((type) => newFieldType == type)) - throw 0 + $(clearFieldBtn).click(function() { + let relatedNode = $(this).closest('a.jstree-anchor'), + rlatedInutField = relatedNode.find('input'), + inputObject = getElementMDBObject(rlatedInutField) - let finalNewFieldType = `${newFieldType}` + try { + rlatedInutField.val('').trigger('input') + } catch (e) {} - if (rowElement.parent().hasClass('data-udt-fields')) - finalNewFieldType = `frozen<${finalNewFieldType}>` + try { + inputObject.update() + setTimeout(() => inputObject._deactivate()) + } catch (e) {} - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${finalNewFieldType};`) - } catch (e) {} + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) + } + } - try { - if (!(['map', 'set', 'list'].some((type) => newFieldType == type))) - throw 0 + { + let allNULLApplyBtns = tableFieldsTreeContainer.find('button[action="apply-null"]').get() - let collectionItemType = rowElement.find('input.collectionItemType').val() + for (let applyBtn of allNULLApplyBtns) { + try { + $(applyBtn).unbind('click') + } catch (e) {} - if (`${newFieldType}` != 'map') { - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${newFieldType}<${collectionItemType}>;`) - throw 0 - } + $(applyBtn).click(function() { + let isNULLApplied = $(this).hasClass('applied') - let collectionKeyType = rowElement.find('input.collectionKeyType').val() + $(this).closest('a.jstree-anchor').find('.null-related').toggleClass('null-applied', !isNULLApplied) + $(this).toggleClass('applied', !isNULLApplied) - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${newFieldType}<${collectionKeyType}, ${collectionItemType}>;`) - } catch (e) {} + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) + } } - } catch (e) {} - } + }) - // The field's name has been changed - { - try { - let setFieldName = rowElement.find('input.fieldName').val() + setTimeout(() => { + try { + tableFieldsTreeContainer.find('input').unbind('input') + tableFieldsTreeContainer.find('input').unbind('change') + } catch (e) {} - if (fieldName == setFieldName || fieldName == undefined) - throw 0 + tableFieldsTreeContainer.find('input').on('input change', function() { + let [ + inputCassandraType, + inputHTMLType, + inputID + ] = getAttributes($(this), ['data-field-type', 'type', 'id']), + relatedNode = $(this).closest('a.jstree-anchor'), + isMandatory = relatedNode.attr('mandatory') == 'true', + isEmptyValueNotAllowed = relatedNode.attr('no-empty-value') == 'true' - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} RENAME ${fieldName} TO ${setFieldName};`) - } catch (e) {} - } + try { + if (!((isMandatory || isEmptyValueNotAllowed) && $(this).val().length <= 0)) + throw 0 - // A new field has been added - { - try { - if (rowElement.attr('data-original-field') != undefined) - throw 0 - - fieldName = rowElement.find('input.fieldName').val() - fieldType = rowElement.find('input.fieldDataType').val() + relatedNode.find('div.clear-field').addClass('hide') - try { - if (['map', 'set', 'list'].some((type) => fieldType == type)) - throw 0 + $(this).addClass('is-invalid') - let finalFieldType = `${fieldType}` + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) - if (rowElement.parent().hasClass('data-udt-fields')) - finalFieldType = `frozen<${finalFieldType}>` + return + } catch (e) {} - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${finalFieldType};`) - } catch (e) {} + $(this).removeClass('is-invalid') - try { - if (!(['map', 'set', 'list'].some((type) => fieldType == type))) - throw 0 + /** + * Using `switch` here won't be suffiecnet as we need, in some cases, to check either one of the types, or both of them + * `boolean` type - `checkbox` input type + */ + try { + if (inputHTMLType != 'checkbox') + throw 0 - let collectionItemType = rowElement.find('input.collectionItemType').val() + $(`label[for="${$(this).attr('id')}"]`).text($(this).prop('checked') ? 'true' : 'false') - if (`${fieldType}` != 'map') { - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${fieldType}<${collectionItemType}>;`) + effectedNodes[inputID] = `${$(this).prop('checked')}` + } catch (e) {} - throw 0 - } + if (inputHTMLType != 'checkbox') + effectedNodes[inputID] = $(this).val() - let collectionKeyType = rowElement.find('input.collectionKeyType').val() + // `inet` Cassandra type - `text` input type + try { + if (inputCassandraType != 'inet') + throw 0 - statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${fieldType}<${collectionKeyType}, ${collectionItemType}>;`) - } catch (e) {} - } catch (e) {} - } - } + let ipValue = $(this).val(), + isValidIP = isIP(`${ipValue}`) != 0 - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', statements.length <= 0 ? '' : null) + if (minifyText(ipValue).length <= 0) + throw $(this).removeClass('is-invalid') - try { - actionEditor.setValue(statements.join(OS.EOL)) - } catch (e) {} + $(this).toggleClass('is-invalid', !isValidIP) + } catch (e) {} - return - } catch (e) {} + // `uuid` and `timeuuid` Cassandra types - `text` input type + try { + if (!(['uuid', 'timeuuid'].some((type) => inputCassandraType == type))) + throw 0 - try { - let currentIndex = 0 + let uuidValue = $(this).val(), + isValidUUID = isUUID(`${uuidValue}`), + uuidFunction = inputCassandraType == 'uuid' ? 'uuid' : 'now' - for (let dataField of allDataFields) { - let tempTxt = '', - dataFieldUIElement = $(dataField), - isUDTDataField = $(dataField).attr('for-udt-data-field') != undefined, - fieldName = dataFieldUIElement.find('input.fieldName').val(), - fieldType = dataFieldUIElement.find('input.fieldDataType').val(), - collectionKeyType = dataFieldUIElement.find('input.collectionKeyType').val(), - collectionItemType = dataFieldUIElement.find('input.collectionItemType').val() + if (minifyText(uuidValue).length <= 0 || uuidValue == `${uuidFunction}()`) + throw $(this).removeClass('is-invalid') - currentIndex += 1 + $(this).toggleClass('is-invalid', !isValidUUID) + } catch (e) {} - try { - if (!isUDTDataField) - throw 0 + // `timestamp` Cassandra type - `text` input type + try { + if (inputCassandraType != 'timestamp') + throw 0 - tempTxt = `${fieldName} frozen<${fieldType}>` - } catch (e) {} + let timestampValue = $(this).val() - try { - if (isUDTDataField) - throw 0 + try { + timestampValue = !isNaN(timestampValue) ? parseInt(timestampValue) : timestampValue + } catch (e) {} - let finalFieldType = `${fieldName} ${fieldType}` + let isValidTimestamp = !isNaN((new Date(timestampValue).getTime())) - if (['set', 'list'].some((type) => fieldType == type)) - finalFieldType = `${fieldName} ${fieldType}<${collectionItemType}>` + if (minifyText($(this).val()).length <= 0 || $(this).val() == 'current_timestamp()') + throw $(this).removeClass('is-invalid') - if (fieldType == 'map') - finalFieldType = `${fieldName} ${fieldType}<${collectionKeyType},${collectionItemType}>` + $(this).toggleClass('is-invalid', !isValidTimestamp) + } catch (e) {} - tempTxt = finalFieldType - } catch (e) {} + // `date` Cassandra type - `text` input type + try { + if (inputCassandraType != 'date') + throw 0 - if (currentIndex != allDataFields.length) - tempTxt = `${tempTxt},` + let dateValue = $(this).val(), + isValidDate = (`${dateValue}`.match(/^\d{4}-\d{2}-\d{2}$/) != null || !isNaN(dateValue)) && !(isNaN(new Date(parseInt(dateValue)).getTime())) - tempTxt = ` ${tempTxt}` + if (minifyText($(this).val()).length <= 0 || $(this).val() == 'current_date()') + throw $(this).removeClass('is-invalid') - dataFieldsText += tempTxt + OS.EOL - } - } catch (e) {} + $(this).toggleClass('is-invalid', !isValidDate) + } catch (e) {} - dataFieldsText = OS.EOL + dataFieldsText + // `time` Cassandra type - `text` input type + try { + if (inputCassandraType != 'time') + throw 0 - let statement = `CREATE TYPE ${keyspaceName}.${udtName} (${dataFieldsText});` + let timeValue = $(this).val(), + isValidTime = (`${timeValue}`.match(/^\d{2}:\d{2}:\d{2}(\.\d+)*$/) != null || !isNaN(timeValue)) && !(isNaN(new Date(parseInt(timeValue)).getTime())) - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + if (minifyText($(this).val()).length <= 0 || $(this).val() == 'current_time()') + throw $(this).removeClass('is-invalid') - try { - actionEditor.setValue(statement) - } catch (e) {} - } + $(this).toggleClass('is-invalid', !isValidTime) + } catch (e) {} - let dataFieldsContainer = dialogElement.find('div.data-fields'), - dataUDTFieldsContainer = dialogElement.find('div.data-udt-fields') + // `duration` Cassandra type - `text` input type + try { + if (inputCassandraType != 'duration') + throw 0 - $(`a[action]#addDataField`).on('click', function(_, fields = null) { - dataFieldsContainer.children('div.empty-fields').hide() + let durationValue = $(this).val(), + isValidDuration = `${durationValue}`.match(/^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(?!$)(\d+H)?(\d+M)?(\d*\.?\d*S)?)?$/) != null - try { - if (fields == null) - throw 0 + if (minifyText($(this).val()).length <= 0) + throw $(this).removeClass('is-invalid') - fields = JSON.parse(fields) + $(this).toggleClass('is-invalid', !isValidDuration) + } catch (e) {} - let filterdFields = fields.filter((field) => !field.type.includes('frozen<')) + // `blob` Cassandra type - `text` input type + try { + if (inputCassandraType != 'blob') + throw 0 - if (filterdFields.length <= 0) { - dataFieldsContainer.children('div.empty-fields').show() - return - } + let blobValue = $(this).val(), + isValidBlob = `${blobValue}`.match(/^(?:0x)[0-9a-fA-F]+(\.\.\.)?$/) != null - for (let field of filterdFields) { - dataFieldsContainer.prepend($(getFieldElement()).show(function() { - let row = $(this) + if (minifyText($(this).val()).length <= 0) + throw $(this).removeClass('is-invalid') - row.attr('data-original-field', 'true') + $(this).toggleClass('is-invalid', !isValidBlob) - setTimeout(() => { - let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + effectedNodes[inputID] = { + inputValue: $(this).val(), + dataValue: $(this).data('value') + } + } catch (e) {} - setTimeout(() => { try { - dropDownMDBObject.update() + relatedNode.find('div.clear-field').toggleClass('hide', $(this).val().length <= 0) } catch (e) {} - }, 500) - - { - row.find('div.dropdown[for-select]').each(function() { - let dropDownElement = $(this), - // Get the MDB object of the current dropdown element - selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), - // Point at the associated input field - input = row.find(`input#${dropDownElement.attr('for-select')}`) - - // Once the associated select element is being focused then show the dropdown element and vice versa - input.on('focus', () => { - try { - input.parent().find('div.invalid-feedback').addClass('transparent-color') - } catch (e) {} - - selectDropdown.show() - }).on('focusout', () => setTimeout(() => { - try { - input.parent().find('div.invalid-feedback').removeClass('transparent-color') - } catch (e) {} - - selectDropdown.hide() - }, 100)) - // Once the parent `form-outline` is clicked trigger the `focus` event - input.parent().click(() => input.trigger('focus')) + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} }) + }) - // Once one of the items is clicked - $(this).find(`div.dropdown[for-select]`).each(function() { - let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + let tippyInstances = [] - $(this).find(`ul.dropdown-menu`).mutate('transform', () => { - let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + tableFieldsTreeContainer.find('a.dropdown-item').on('click', function() { + let btnAction = $(this).attr('action') - $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + switch (btnAction) { + case 'function': { + let functionContent = $(this).attr('data-function'), + inputField = $(this).parent().parent().parent().parent().find('input'), + inputObject = getElementMDBObject(inputField) + + inputField.val(`${functionContent}`).trigger('input') try { - updateRowsZIndex(isTransformNegative) + inputObject.update() } catch (e) {} - }) - $(this).find(`ul.dropdown-menu`).find('a').click(function() { - // Point at the input field related to the list - let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), - selectedValue = $(this).attr('value'), - isTypeCollection = $(this).attr('data-is-collection') != undefined, - isCollectionMap = $(this).attr('data-is-map') != undefined + break + } + case 'datetimepicker': { + let viewMode = $(this).attr('data-view-mode'), + inputField = $(this).parent().parent().parent().parent().find('input'), + inputObject = getElementMDBObject(inputField) try { - if (!mainDropDown) + let tippyReference = $(this).parent().parent().parent().find('button'), + tippyInstance = tippyInstances.find((instance) => $(instance.reference).is(tippyReference)) + + if (tippyInstance != undefined) { + try { + tippyInstance.enable() + tippyInstance.show() + } catch (e) {} + throw 0 + } - row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + let pickerContainerID = getRandomID(30), + isDataCleared = false + + tippyInstance = tippy(tippyReference[0], { + content: `
    `, + appendTo: () => document.body, + allowHTML: true, + arrow: false, + interactive: true, + placement: 'right-start', + trigger: 'click', + theme: 'material', + showOnCreate: true, + onShow(instance) { + let popper = $(instance.popper), + reference = $(instance.reference), + inputField = reference.parent().parent().find('input'), + inputObject = getElementMDBObject(inputField) + + if (popper.find('div.row, form').length != 0) + return - row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + setTimeout(() => { + try { + if (viewMode != 'HMS-D') + throw 0 - row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) - } catch (e) {} + let [ + yearsInputID, + monthsInputID, + daysInputID, + hoursInputID, + minutesInputID, + secondsInputID, + clearBtnID, + confirmBtnID + ] = getRandomID(10, 8).map((id) => `_${id}_du`), + durationForm = ` +
    +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    ` + + $(`div#_${pickerContainerID}`).append($(durationForm).show(function() { + let durationFormElement = $(this) + + setTimeout(() => durationFormElement.find('input').each(function() { + setTimeout(() => getElementMDBObject($(this))) + })) + + setTimeout(() => durationFormElement.find('button').each(function() { + getElementMDBObject($(this), 'Button') + })) + + setTimeout(() => Modules.Localization.applyLanguageSpecific(durationFormElement.find('span[mulang], [data-mulang]'))) - // Update the input's value - selectElement.val(selectedValue).trigger('input') + setTimeout(() => { + $(`button#${clearBtnID}`).add(`button#${confirmBtnID}`).unbind('click') - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - }) - } + $(`button#${clearBtnID}`).click(function() { + for (let durationInputField of $(this).parent().parent().parent().find('input').get()) { + $(durationInputField).val(``).trigger('input') - $(this).find(`a[action="delete-udt"]`).click(function() { - row.toggleClass('deleted') + try { + let durationInputObject = getElementMDBObject($(durationInputObject)) - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + durationInputObject.update() + setTimeout(() => durationInputObject._deactivate()) + } catch (e) {} + } - $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { - let fieldName = $(this).val(), - fieldRow = $(this).parent().parent().parent(), - isNameDuplicated = false, - isNameInvalid = false, - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + inputField.val(``).trigger('input') - isAlterState = isAlterState != null && isAlterState == 'alter' + try { + inputObject.update() + setTimeout(() => inputObject._deactivate()) + } catch (e) {} - try { - if (`${fieldName}`.length <= 0) - throw 0 + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) - isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null - } catch (e) {} + $(`button#${confirmBtnID}`).click(function() { + try { + let duration = `P` - try { - let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + try { + let periodInputFields = [yearsInputID, monthsInputID, daysInputID] - for (let dataField of allDataFields) { - let dataFieldNameElement = $(dataField).find('input.fieldName') + for (let periodInputFieldID of periodInputFields) { + let inputElement = $(`input#${periodInputFieldID}`), + inputSuffix = inputElement.attr('data-part-suffix'), + inputValue = parseInt(inputElement.val()) >= 1 ? `${inputElement.val()}${inputSuffix}` : '' - if (triggerInput) - dataFieldNameElement.trigger('input', false) + duration += inputValue + } + } catch (e) {} - if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) - continue + try { + let timeInputFields = [hoursInputID, minutesInputID, secondsInputID], + tempTxt = '' - isNameDuplicated = true - break - } - } catch (e) {} + for (let timeInputFieldID of timeInputFields) { + let inputElement = $(`input#${timeInputFieldID}`), + inputSuffix = inputElement.attr('data-part-suffix'), + inputValue = parseInt(inputElement.val()) >= 1 ? `${inputElement.val()}${inputSuffix}` : '' - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + tempTxt += inputValue + } - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + if (tempTxt.length <= 0) + throw 0 - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + duration += `T${tempTxt}` + } catch (e) {} - setTimeout(() => { - $(this).find('input[type="text"]').each(function() { - let mdbObject = getElementMDBObject($(this)) + duration = duration == 'P' ? '' : duration - setTimeout(() => mdbObject.update(), 500) - }) - }) + inputField.val(duration).trigger('input') - setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + try { + inputObject.update() + } catch (e) {} + } catch (e) {} finally { + try { + instance.disable() + instance.hide() + } catch (e) {} + } - updateRowsZIndex() + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) + }) + })) - try { - row.find('input.fieldName').val(field.name).trigger('input') - row.find('input.fieldName').attr('data-original-value', field.name) + return + } catch (e) {} - if (['map', 'set', 'list'].some((type) => field.type == type)) - throw 0 + setTimeout(() => $(instance.popper).find('div.tippy-content').addClass('no-padding')) - row.find('input.fieldDataType').val(field.type).trigger('input') - row.find('input.fieldDataType').attr('data-original-value', field.type) - } catch (e) {} + setTimeout(() => $(`div#_${pickerContainerID}`).datetimepicker({ + date: new Date(), + viewMode, + onClear: () => { + inputField.val('').trigger('input') - try { - let extractData = field.type.match(/(.+)\<(.*?)\>/) + isDataCleared = true - if (extractData == null || !(['map', 'set', 'list'].some((type) => extractData[1].includes(type)))) - throw 0 + try { + inputObject.update() + setTimeout(() => inputObject._deactivate()) + } catch (e) {} - $(`div.dropdown[for-select="${row.find('input.fieldDataType').attr('id')}"]`).find(`li a[value="${extractData[1]}"]`).trigger('click') + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }, + onDateChange: function() { + isDataCleared = this.getValue() == null + }, + onOk: function() { + try { + if (isDataCleared) + throw 0 - row.find('input.fieldDataType').attr('data-original-value', extractData[1]) + let dateTimeValue = this.getText(viewMode == 'YMDHMS' ? 'YYYY-MM-DD HH:mm:ss.i' : (viewMode == 'YMD' ? 'YYYY-MM-DD' : 'HH:mm:ss.i')) - if (extractData[1] != 'map') { - row.find('input.collectionItemType').val(extractData[2]).trigger('input') - row.find('input.collectionItemType').attr('data-original-value', extractData[2]) - } else { - let mapValues = minifyText(extractData[2]).split(',') + try { + if (viewMode != 'YMDHMS') + throw 0 - row.find('input.collectionKeyType').val(mapValues[0]).trigger('input') - row.find('input.collectionKeyType').attr('data-original-value', mapValues[0]) + dateTimeValue = new Date(this.getValue()).getTime() + } catch (e) {} - row.find('input.collectionItemType').val(mapValues[1]).trigger('input') - row.find('input.collectionItemType').attr('data-original-value', mapValues[1]) - } - } catch (e) {} + inputField.val(`${dateTimeValue}`).trigger('input') - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - })) - } + try { + inputObject.update() + } catch (e) {} - return - } catch (e) {} + } catch (e) {} finally { + try { + instance.disable() + instance.hide() + } catch (e) {} + } - dataFieldsContainer.prepend($(getFieldElement()).show(function() { - let row = $(this) + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + } + })) + }) + }, + onHidden(instance) { + try { + instance.disable() + instance.hide() + } catch (e) {} + }, + }) - setTimeout(() => { - let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + tippyInstances.push(tippyInstance) + } catch (e) {} - setTimeout(() => { - try { - dropDownMDBObject.update() - } catch (e) {} - }, 500) + break + } + case 'upload-item': { + let inputField = $(this).parent().parent().parent().parent().find('input'), + inputObject = getElementMDBObject(inputField), + dialogID = getRandomID(5), + data = { + id: dialogID, + title: I18next.capitalizeFirstLetter(I18next.t('upload item for BLOB field')), + properties: ['showHiddenFiles', 'createDirectory', 'promptToCreate', 'openFile'] + } - { - row.find('div.dropdown[for-select]').each(function() { - let dropDownElement = $(this), - // Get the MDB object of the current dropdown element - selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), - // Point at the associated input field - input = row.find(`input#${dropDownElement.attr('for-select')}`) + // Send a request to the main thread to create a dialog + IPCRenderer.send('dialog:create', data) - // Once the associated select element is being focused then show the dropdown element and vice versa - input.on('focus', () => { - try { - input.parent().find('div.invalid-feedback').addClass('transparent-color') - } catch (e) {} + // Listen for the response + IPCRenderer.on(`dialog:${dialogID}`, (_, selected) => { + if (selected.length <= 0) { + inputField.val('').trigger('input') - selectDropdown.show() - }).on('focusout', () => setTimeout(() => { - try { - input.parent().find('div.invalid-feedback').removeClass('transparent-color') - } catch (e) {} + inputField.data('value', '') - selectDropdown.hide() - }, 100)) + try { + inputObject.update() - // Once the parent `form-outline` is clicked trigger the `focus` event - input.parent().click(() => input.trigger('focus')) - }) + setTimeout(() => inputObject._deactivate()) + } catch (e) {} - // Once one of the items is clicked - $(this).find(`div.dropdown[for-select]`).each(function() { - let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + return + } - $(this).find(`ul.dropdown-menu`).mutate('transform', () => { - let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + let selectedItem = selected[0] - $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + Modules.Config.getConfig((config) => { + let maxItemSize = config.get('limit', 'insertBlobSize') || '2MB' - try { - updateRowsZIndex(isTransformNegative) - } catch (e) {} - }) + try { + maxItemSize = Bytes(maxItemSize) || 2097152 + } catch (e) {} - $(this).find(`ul.dropdown-menu`).find('a').click(function() { - // Point at the input field related to the list - let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), - selectedValue = $(this).attr('value'), - isTypeCollection = $(this).attr('data-is-collection') != undefined, - isCollectionMap = $(this).attr('data-is-map') != undefined + FS.stat(selectedItem, (err, stats) => { + if (err) + return - try { - if (!mainDropDown) - throw 0 + let itemSize = stats.size - row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + if (itemSize > maxItemSize) + return showToast(I18next.capitalize(I18next.t('upload item')), I18next.capitalizeFirstLetter(I18next.replaceData('the size of the uploaded item is [b]$data[/b], which is greater than the maximum allowed size of [b]$data[/b]. Please consider to change that in the config file or try with smaller item', [Bytes(itemSize), Bytes(maxItemSize)])) + '.', 'failure') - row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + let requestID = getRandomID(20), + ringSpinnerElement = $(this).parent().parent().parent().children('l-ring-2'), + showRingSpinnerTimeout = null, + showRingSpinner = () => { + try { + clearTimeout(showRingSpinnerTimeout) + } catch (e) {} - row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) - } catch (e) {} + showRingSpinnerTimeout = setTimeout(() => ringSpinnerElement.addClass('show'), 500) + } - // Update the input's value - selectElement.val(selectedValue).trigger('input') + showRingSpinner() - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - }) - } + // Request to get the public key + IPCRenderer.send('blob:read-convert', { + requestID, + itemPath: selectedItem + }) - $(this).find(`a[action="delete-udt"]`).click(function() { - $(this).parent().parent().remove() + // Wait for the response + IPCRenderer.on(`blob:read-convert:result:${requestID}`, (_, data) => { + inputField.data('value', data.itemHEXString) - try { - updateActionStatusForUDTs() - } catch (e) {} + data.itemHEXString = data.itemHEXString.length <= 0 ? '' : (data.itemHEXString.length > 100 ? `${data.itemHEXString.slice(0, 100)}...` : data.itemHEXString) - if (dataFieldsContainer.children('div.data-field.row').length != 0) - return + inputField.val(data.itemHEXString).trigger('input') - dataFieldsContainer.children('div.empty-fields').fadeIn(250) - }) + try { + inputObject.update() + } catch (e) {} - $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { - let fieldName = $(this).val(), - fieldRow = $(this).parent().parent().parent(), - isNameDuplicated = false, - isNameInvalid = false, - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + try { + clearTimeout(showRingSpinnerTimeout) + } catch (e) {} - isAlterState = isAlterState != null && isAlterState == 'alter' + ringSpinnerElement.removeClass('show') + }) + }) + }) + }) - try { - if (`${fieldName}`.length <= 0) - throw 0 + break + } + case 'preview-item': { + let inputField = $(this).parent().parent().parent().parent().find('input') - isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null - } catch (e) {} + try { + let blobHEXString = inputField.data('value') - try { - let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + if (`${inputField.val()}`.slice(0, 100) != `${blobHEXString}`.slice(0, 100)) + throw 0 - for (let dataField of allDataFields) { - let dataFieldNameElement = $(dataField).find('input.fieldName') + getBlobType(blobHEXString, (err, result) => { + if (err || `${result.mime}`.includes('application')) + throw 0 - if (triggerInput) - dataFieldNameElement.trigger('input', false) + let itemType = '' - if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) - continue + try { + itemType = result.ext + } catch (e) {} - isNameDuplicated = true - break - } - } catch (e) {} + let requestID = getRandomID(20), + ringSpinnerElement = $(this).parent().parent().parent().children('l-ring-2'), + showRingSpinnerTimeout = null, + showRingSpinner = () => { + try { + clearTimeout(showRingSpinnerTimeout) + } catch (e) {} - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + showRingSpinnerTimeout = setTimeout(() => ringSpinnerElement.addClass('show'), 500) + } - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + showRingSpinner() - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + // Request to get the public key + IPCRenderer.send('blob:convert-write', { + requestID, + itemType, + blobHEXString, + randomID: getRandomID(15) + }) - setTimeout(() => { - $(this).find('input[type="text"]').each(function() { - let mdbObject = getElementMDBObject($(this)) + // Wait for the response + IPCRenderer.on(`blob:convert-write:result:${requestID}`, (_, data) => { + if (data.error) + throw 0 - setTimeout(() => mdbObject.update(), 500) - }) - }) + Open(data.itemTempFile) - setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + try { + clearTimeout(showRingSpinnerTimeout) + } catch (e) {} - updateRowsZIndex() + ringSpinnerElement.removeClass('show') + }) + }) + } catch (e) { + showToast(I18next.capitalize(I18next.t('preview item')), I18next.capitalizeFirstLetter(I18next.t('something went wrong, failed to finalize the preview process of the current item')) + '.', 'failure') + } - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - })) - }) + break + } + } - $(`a[action]#addUDTDataField`).click('click', function(_, fields = null) { - let keyspaceUDTs = [], - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + $(this).parent().parent().hide() - isAlterState = isAlterState != null && isAlterState == 'alter' + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) - try { - keyspaceUDTs = JSON.parse($(dialogElement).attr('data-keyspace-udts')) + setTimeout(() => { + tableFieldsTreeContainer.find('input').trigger('input') + tableFieldsTreeContainer.find('input').trigger('change') + }) + }) - keyspaceUDTs = keyspaceUDTs.map((udt) => udt.name) - } catch (e) {} + setTimeout(() => Modules.Localization.applyLanguageSpecific(tableFieldsTreeContainer.find('span[mulang], [data-mulang]'))) + }) - try { - if (!isAlterState) - throw 0 - - keyspaceUDTs = keyspaceUDTs.filter((udt) => udt != $('input#udtName').val()) - } catch (e) {} - - if (keyspaceUDTs.length <= 0) { - $('a[action]#addUDTDataField').addClass('disabled') - - dataUDTFieldsContainer.children('div.empty-fields').find('span').hide() - dataUDTFieldsContainer.children('div.empty-fields').find('span.one-udt').show() + let triggerLoadEventTimeOut = null, + triggerLoadEvent = () => { + try { + clearTimeout(triggerLoadEventTimeOut) + } catch (e) {} - return - } + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) - dataUDTFieldsContainer.children('div.empty-fields').hide() + triggerLoadEventTimeOut = setTimeout(() => tableFieldsTreeContainer.trigger('loaded.jstree')) + } - try { - if (fields == null) - throw 0 + tableFieldsTreeContainer.on('create_node.jstree', function(e, data) { + let parentNodeID = '_' - fields = JSON.parse(fields) + try { + parentNodeID = data.node.parents.at(-2) + } catch (e) {} - let filterdFields = fields.filter((field) => field.type.includes('frozen<')) + try { + if (tableFieldsTreeContainer.find(`a.jstree-anchor[add-hidden-node="${parentNodeID}"]`).length <= 0) + throw 0 - if (filterdFields.length <= 0) { - dataUDTFieldsContainer.children('div.empty-fields').show() - return - } + let hiddenNode = tableFieldsTreeContainer.find(`li.jstree-node[id="${parentNodeID}"]`) - for (let field of filterdFields) { - try { - let extractData = field.type.match(/.+\<(.*?)\>/)[1] + hiddenNode.css('margin-top', '-45px') - field.type = extractData - } catch (e) {} + hiddenNode.children('a').css('pointer-events', 'none') + } catch (e) {} - dataUDTFieldsContainer.prepend($(getFieldElement(keyspaceUDTs)).show(function() { - let row = $(this) + triggerLoadEvent() + }) - row.attr('data-original-field', 'true') + tableFieldsTreeContainer.on('delete_node.jstree', function(e, data) { + try { + data.instance.hide_node(data.node.id) + } catch (e) {} setTimeout(() => { - let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) - setTimeout(() => { - try { - dropDownMDBObject.update() - } catch (e) {} - }, 500) + tableFieldsTreeContainer.on('hide_node.jstree', () => triggerLoadEvent()) - { - row.find('div.dropdown[for-select]').each(function() { - let dropDownElement = $(this), - // Get the MDB object of the current dropdown element - selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), - // Point at the associated input field - input = row.find(`input#${dropDownElement.attr('for-select')}`) + tableFieldsTreeContainer.on('select_node.jstree', function(e, data) { + let clickedNode = tableFieldsTreeContainer.find(`li.jstree-node[id="${data.node.id}"]`) - // Once the associated select element is being focused then show the dropdown element and vice versa - input.on('focus', () => { - try { - input.parent().find('div.invalid-feedback').addClass('transparent-color') - } catch (e) {} + try { + let clickedTarget = $(data.event.target) - selectDropdown.show() - }).on('focusout', () => setTimeout(() => { - try { - input.parent().find('div.invalid-feedback').removeClass('transparent-color') - } catch (e) {} + if (minifyText(clickedTarget[0].tagName) != 'input') + throw 0 - selectDropdown.hide() - }, 100)) + setTimeout(() => clickedTarget.trigger(clickedTarget.attr('type') == 'checkbox' ? 'click' : 'focus')) + } catch (e) {} - // Once the parent `form-outline` is clicked trigger the `focus` event - input.parent().click(() => input.trigger('focus')) - }) - // Once one of the items is clicked - $(this).find(`div.dropdown[for-select]`).each(function() { - let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + setTimeout(() => { + try { + data.instance.deselect_all() + } catch (e) {} + }, 100) + }) + } + }) - $(this).find(`ul.dropdown-menu`).mutate('transform', () => { - let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + $('div.modal#rightClickActionsMetadata div[action]').hide() + $('div.modal#rightClickActionsMetadata div[action="insert-row"]').show() - $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + $('div.modal#rightClickActionsMetadata').addClass('insertion-action') - try { - updateRowsZIndex(isTransformNegative) - } catch (e) {} - }) + $('#rightClickActionsMetadata').removeClass('show-editor') - $(this).find(`ul.dropdown-menu`).find('a').click(function() { - // Point at the input field related to the list - let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), - selectedValue = $(this).attr('value'), - isTypeCollection = $(this).attr('data-is-collection') != undefined, - isCollectionMap = $(this).attr('data-is-map') != undefined + rightClickActionsMetadataModal.show() - try { - if (!mainDropDown) - throw 0 + setTimeout(() => { + try { + updateActionStatusForInsertRow() + } catch (e) {} + }) + }) + }) + } + } - row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + // Handle the request of closing a cluster's work area + { + IPCRenderer.on('workarea:close', (_, data) => $(`div.btn[data-id="${data.btnID}"]`).trigger('click', false)) + } - row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + { + setTimeout(() => { + // Define the scroll value, by default, the current scroll value is `0` - at the top of the dialog - + let scrollValue = 0, + dialogElement = $(`div.modal#rightClickActionsMetadata`), + actionEditor = monaco.editor.getEditors().find((editor) => dialogElement.find('div.action-editor div.editor div.monaco-editor').is(editor.getDomNode())), + updateActionStatusForKeyspaces = () => { + let replicationStrategy = $('input#keyspaceReplicationStrategy').val(), + keyspaceName = addDoubleQuotes($('input#keyspaceName').val()), + durableWrites = $('input#keyspaceDurableWrites').prop('checked'), + replication = {}, + isRFAcceptable = false - row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) - } catch (e) {} + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') - // Update the input's value - selectElement.val(selectedValue).trigger('input') + isAlterState = isAlterState != null && isAlterState == 'alter' - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - }) - } + try { + if (dialogElement.find('div[action="keyspaces"]').find('.is-invalid:not(.ignore-invalid)').length <= 0) + throw 0 - $(this).find(`a[action="delete-udt"]`).click(function() { - row.toggleClass('deleted') + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + return + } catch (e) {} - $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { - let fieldName = $(this).val(), - fieldRow = $(this).parent().parent().parent(), - isNameDuplicated = false, - isNameInvalid = false, - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + try { + if (minifyText(replicationStrategy) != 'simplestrategy') + throw 0 - isAlterState = isAlterState != null && isAlterState == 'alter' + let replicationFactor = $('input#keyspaceReplicationFactorSimpleStrategy').val() - try { - if (`${fieldName}`.length <= 0) - throw 0 + replication.class = 'SimpleStrategy' + replication.replication_factor = replicationFactor + } catch (e) {} - isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null - } catch (e) {} + try { + if (minifyText(replicationStrategy) != 'networktopologystrategy') + throw 0 - try { - let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + let replicationFactor = $('input#keyspaceReplicationFactorSimpleStrategy').val() - for (let dataField of allDataFields) { - let dataFieldNameElement = $(dataField).find('input.fieldName') + replication.class = 'NetworkTopologyStrategy' - if (triggerInput) - dataFieldNameElement.trigger('input', false) + let dataCenters = dialogElement.find('div[action="keyspaces"]').find('div[for-strategy="NetworkTopologyStrategy"]').children('div.data-centers').find('div.data-center') - if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) - continue + for (let dataCenter of dataCenters) { + dataCenter = $(dataCenter) + let rf = dataCenter.find('input[type="number"]').val() - isNameDuplicated = true - break - } - } catch (e) {} + if (rf <= 0) + continue - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + isRFAcceptable = true - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + replication[dataCenter.attr('data-datacenter')] = rf + } - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + if (isAlterState) + isRFAcceptable = true - setTimeout(() => { - $(this).find('input[type="text"]').each(function() { - let mdbObject = getElementMDBObject($(this)) + dialogElement.find('div.row.invalid-text-container').toggleClass('show', !isRFAcceptable) - setTimeout(() => mdbObject.update(), 500) - }) - }) + let invalidState = !isAlterState && (!isRFAcceptable || ($('input#keyspaceName').hasClass('is-invalid') || `${$('input#keyspaceName').val()}`.length <= 0)) - setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', invalidState ? '' : null) - updateRowsZIndex() + if (invalidState) + return + } catch (e) {} - try { - row.find('input.fieldName').val(field.name).trigger('input') - row.find('input.fieldName').attr('data-original-value', field.name) + let durableWritesSetValue = $('input#keyspaceDurableWrites').attr('set-value'), + replicationStrategySetValue = '' - row.find('input.fieldDataType').val(field.type).trigger('input') - row.find('input.fieldDataType').attr('data-original-value', field.type) - } catch (e) {} + try { + replicationStrategySetValue = JSONRepair(JSON.parse(JSONRepair($('#rightClickActionsMetadata').attr('data-keyspace-info'))).replication_strategy) + } catch (e) {} - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - })) - } + let durableWritesFinal = '', + replicationStrategyFinal = '' - return + try { + if (isAlterState && minifyText(replicationStrategySetValue) == minifyText(JSON.stringify(replication))) + throw 0 + + replicationStrategyFinal = OS.EOL + `WITH replication = ${JSON.stringify(replication).replace(/"/gm, "'")}` } catch (e) {} - dataUDTFieldsContainer.prepend($(getFieldElement(keyspaceUDTs)).show(function() { - let row = $(this) + try { + if (isAlterState && `${durableWritesSetValue}` == `${durableWrites}`) + throw 0 - setTimeout(() => { - let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + durableWritesFinal = OS.EOL + (replicationStrategyFinal.length <= 0 ? 'WITH' : 'AND') + ` durable_writes = ${durableWrites}` + } catch (e) {} - setTimeout(() => { - try { - dropDownMDBObject.update() - } catch (e) {} - }, 500) + try { + if (!isAlterState) + throw 0 - { - row.find('div.dropdown[for-select]').each(function() { - let dropDownElement = $(this), - // Get the MDB object of the current dropdown element - selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), - // Point at the associated input field - input = row.find(`input#${dropDownElement.attr('for-select')}`) + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', [replicationStrategyFinal, durableWritesFinal].every((str) => `${str}`.length <= 0) ? '' : null) + } catch (e) {} - // Once the associated select element is being focused then show the dropdown element and vice versa - input.on('focus', () => { - try { - input.parent().find('div.invalid-feedback').addClass('transparent-color') - } catch (e) {} + let statement = `${isAlterState ? 'ALTER' : 'CREATE'} KEYSPACE ${keyspaceName}${replicationStrategyFinal}${durableWritesFinal};` - selectDropdown.show() - }).on('focusout', () => setTimeout(() => { - try { - input.parent().find('div.invalid-feedback').removeClass('transparent-color') - } catch (e) {} + try { + actionEditor.setValue(statement) + } catch (e) {} + } - selectDropdown.hide() - }, 100)) + $('input#keyspaceName').on('input', function() { + let keyspaces = [], + keyspaceName = $(this).val(), + isNameDuplicated = false, + isNameInvalid = false, + invalidFeedback = $(this).parent().children('div.invalid-feedback'), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') - // Once the parent `form-outline` is clicked trigger the `focus` event - input.parent().click(() => input.trigger('focus')) - }) - // Once one of the items is clicked - $(this).find(`div.dropdown[for-select]`).each(function() { - let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + isAlterState = isAlterState != null && isAlterState == 'alter' - $(this).find(`ul.dropdown-menu`).mutate('transform', () => { - let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') - $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + $(this).attr('disabled', isAlterState ? '' : null) + $(this).parent().toggleClass('invalid-warning', isAlterState) + $(this).toggleClass('is-invalid ignore-invalid', isAlterState) - try { - updateRowsZIndex(isTransformNegative) - } catch (e) {} - }) + try { + if (!isAlterState) + throw 0 - $(this).find(`ul.dropdown-menu`).find('a').click(function() { - // Point at the input field related to the list - let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), - selectedValue = $(this).attr('value'), - isTypeCollection = $(this).attr('data-is-collection') != undefined, - isCollectionMap = $(this).attr('data-is-map') != undefined + invalidFeedback.find('span').attr('mulang', 'the keyspace name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the keyspace name can\'t be altered'))) - try { - if (!mainDropDown) - throw 0 + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) - row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + return + } catch (e) {} - row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) - row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { - return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') - }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) - } catch (e) {} + try { + keyspaces = JSON.parse(JSONRepair($('#rightClickActionsMetadata').attr('data-keyspaces'))) + } catch (e) {} - // Update the input's value - selectElement.val(selectedValue).trigger('input') + try { + if (keyspaces.length <= 0) + throw 0 - try { - updateActionStatusForUDTs() - } catch (e) {} - }) - }) - } + isNameDuplicated = keyspaces.some((keyspace) => `${keyspace}` == `${keyspaceName}`) - $(this).find(`a[action="delete-udt"]`).click(function() { - $(this).parent().parent().remove() + if (isAlterState && isNameDuplicated && (keyspaceName == $('div.modal#rightClickActionsMetadata').attr('data-keyspacename'))) + isNameDuplicated = false - try { - updateActionStatusForUDTs() - } catch (e) {} + if (isNameDuplicated) + invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) + } catch (e) {} - if (dataUDTFieldsContainer.children('div.data-field.row').length != 0) - return + try { + if (`${keyspaceName}`.length <= 0) + throw 0 - dataUDTFieldsContainer.children('div.empty-fields').fadeIn(250) - }) + isNameInvalid = `${keyspaceName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null - $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { - let fieldName = $(this).val(), - fieldRow = $(this).parent().parent().parent(), - isNameDuplicated = false, - isNameInvalid = false, - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + if (isNameInvalid) + invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) + } catch (e) {} - isAlterState = isAlterState != null && isAlterState == 'alter' + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) - try { - if (`${fieldName}`.length <= 0) - throw 0 + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 ? '' : null) - isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null - } catch (e) {} + try { + updateActionStatusForKeyspaces() + } catch (e) {} + }) - try { - let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + $('input#keyspaceReplicationStrategy').on('input', function() { + let replicationStrategy = $(this).val(), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') - for (let dataField of allDataFields) { - let dataFieldNameElement = $(dataField).find('input.fieldName') + isAlterState = isAlterState != null && isAlterState == 'alter' - if (triggerInput) - dataFieldNameElement.trigger('input', false) + setTimeout(() => { + dialogElement.find('div[for-strategy]').hide() - if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) - continue + let networkTopologyContainer = dialogElement.find('div[for-strategy="NetworkTopologyStrategy"]'), + simpleStrategyContainer = dialogElement.find('div[for-strategy="SimpleStrategy"]'), + dataCentersContainer = networkTopologyContainer.children('div.data-centers'), + dataCenters = [] - isNameDuplicated = true - break - } - } catch (e) {} + try { + dataCenters = JSON.parse(JSONRepair($(this).attr('data-datacenters'))) + } catch (e) {} - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + try { + dataCenters = dataCenters.filter((datacenter, index, datacenters) => datacenters.findIndex(_datacenter => _datacenter.name === datacenter.name) === index) + } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + $(this).parent().find('div.invalid-feedback span[mulang][capitalize-first]').attr('mulang', 'SimpleStrategy is intended for development purposes only').text(I18next.capitalizeFirstLetter(I18next.t('SimpleStrategy is intended for development purposes only'))) - try { - updateActionStatusForUDTs() - } catch (e) {} - }) + $(this).parent().css('margin-bottom', '50px') - setTimeout(() => { - $(this).find('input[type="text"]').each(function() { - let mdbObject = getElementMDBObject($(this)) + try { + if (minifyText(replicationStrategy) == minifyText('simplestrategy')) + throw 0 - setTimeout(() => mdbObject.update(), 500) - }) - }) + $(this).removeClass('is-invalid') - setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + $('input#keyspaceReplicationFactorSimpleStrategy').val(1).trigger('input') - updateRowsZIndex() + try { + dataCentersContainer.children('div.row.data-center').remove() + + let dataCentersRF = {} try { - updateActionStatusForUDTs() + if (isAlterState) + dataCentersRF = JSON.parse(JSONRepair($('div.modal#rightClickActionsMetadata').attr('data-datacenters-rf'))) } catch (e) {} - }) - })) - }) + try { + dataCentersRF = dataCentersRF.filter((datacenter, index, datacenters) => datacenters.findIndex(_datacenter => _datacenter.name === datacenter.name) === index) + } catch (e) {} - $('input#udtName').on('input', function() { - let keyspaceUDTs = [], - udtName = $(this).val(), - isNameDuplicated = false, - isNameInvalid = false, - invalidFeedback = $(this).parent().children('div.invalid-feedback'), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + for (let datacenter of dataCenters) { + let replicationFactor = parseInt(dataCentersRF[datacenter.datacenter]) || 0 - isAlterState = isAlterState != null && isAlterState == 'alter' + try { + if (!isAlterState || dataCenters.length > 1 || dataCentersRF.class == 'NetworkTopologyStrategy') + throw 0 - $(this).attr('disabled', isAlterState ? '' : null) - $(this).parent().toggleClass('invalid-warning', isAlterState) - $(this).toggleClass('is-invalid ignore-invalid', isAlterState) + replicationFactor = parseInt($('div.modal#rightClickActionsMetadata').attr('data-rf')) || 0 + } catch (e) {} - try { - if (!isAlterState) - throw 0 + let element = ` +
    +
    ${datacenter.datacenter}
    +
    +
    + + +
    +
    +
    ` + dataCentersContainer.append($(element).show(function() { + let dataCenter = $(this) - invalidFeedback.find('span').attr('mulang', 'the UDT name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the UDT name can\'t be altered'))) + setTimeout(() => { + dataCenter.find('input').each(function() { + getElementMDBObject($(this)) + }) - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + dataCenter.find('input[type="number"]').on('input', function() { + let rf = parseInt($(this).val()), + isInvalid = isNaN(rf) || rf < 0 - return - } catch (e) {} + $(this).toggleClass('is-invalid', isInvalid) - try { - keyspaceUDTs = JSON.parse($('#rightClickActionsMetadata').attr('data-keyspace-udts')) - } catch (e) {} + if (minifyText($('input#keyspaceName').val()).length <= 0) + return - try { - if (keyspaceUDTs.length <= 0) - throw 0 + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isInvalid ? '' : null) - isNameDuplicated = keyspaceUDTs.some((udt) => minifyText(`${udt.name}`) == minifyText(`${udtName}`)) + try { + updateActionStatusForKeyspaces() + } catch (e) {} + }) + }) - if (isAlterState && isNameDuplicated && (udtName == $('div.modal#rightClickActionsMetadata').attr('data-udt-name'))) - isNameDuplicated = false + setTimeout(() => Modules.Localization.applyLanguageSpecific(dataCenter.find('span[mulang], [data-mulang]'))) + })) + } + } catch (e) {} - if (isNameDuplicated) - invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) + networkTopologyContainer.show() + + try { + updateActionStatusForKeyspaces() + } catch (e) {} + + return } catch (e) {} + setTimeout(() => $(this).addClass('is-invalid'), 250) + try { - if (`${udtName}`.length <= 0) + if (!isAlterState) throw 0 - isNameInvalid = `${udtName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + $('input#keyspaceReplicationFactorSimpleStrategy').val(parseInt($('div.modal#rightClickActionsMetadata').attr('data-rf')) || 1) - if (isNameInvalid) - invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) - } catch (e) {} + $(`div.modal#rightClickActionsMetadata`).find('div.data-center').find('input[type="number"]').val(0).trigger('input') - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) + if (dataCenters.length > 1) { + $(this).parent().find('div.invalid-feedback span[mulang][capitalize-first]').attr('mulang', 'avoid switching from SimpleStrategy to NetworkTopologyStrategy in multi-DC clusters; plan carefully').text(I18next.capitalizeFirstLetter(I18next.t('avoid switching from SimpleStrategy to NetworkTopologyStrategy in multi-DC clusters; plan carefully'))) - let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row'), - invalidInputFields = allDataFields.find('input.is-invalid') + $(this).parent().css('margin-bottom', '70px') + } + } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 || allDataFields.length <= 0 || invalidInputFields.length > 0 ? '' : null) + dialogElement.find('div[for-strategy="SimpleStrategy"]').show() try { - updateActionStatusForUDTs() + updateActionStatusForKeyspaces() } catch (e) {} - }) - } + }, 150) + }) - let updateActionStatusForCounterTables + $('input#keyspaceReplicationFactorSimpleStrategy').on('input', function() { + let rf = parseInt($(this).val()), + isInvalid = isNaN(rf) || rf < 1 - { - let updateRowsZIndex = (isTransformNegative = false) => { - setTimeout(() => { - let rows = dialogElement.find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field').get(), - rowsCount = rows.length + $('input#keyspaceReplicationFactorSimpleStrategy').toggleClass('is-invalid', isInvalid) - if (isTransformNegative) - rows = rows.reverse() + if (minifyText($('input#keyspaceName').val()).length <= 0) + return - for (let row of rows) { - $(row).css('z-index', `${rowsCount}`) + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isInvalid ? '' : null) - rowsCount -= 1 - } - }) - } + try { + updateActionStatusForKeyspaces() + } catch (e) {} + }) - updateActionStatusForCounterTables = () => { - let counterTableName = $('input#countertableName').val() + // Clicks the `SWITCH TO EDITOR` button + dialogElement.find('button.switch-editor').click(function() { + // Point at the dialog's content element + let dialogBody = dialogElement.find('div.modal-body'), + // Determine if the editor is visible/shown or not + editorShown = dialogElement.hasClass('show-editor'), + currentActiveAction = $('div.modal#rightClickActionsMetadata div[action]').filter(':visible').attr('action') - try { - if (dialogElement.find('div[action="counter-tables"]').find('.is-invalid:not(.ignore-invalid)').length <= 0 && - dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field.row').length > 0 && - dialogElement.find('div[action="counter-tables"]').find('div.counter-table-column-field.row').length > 0 && - minifyText(counterTableName).length > 0) - throw 0 + // Add the `show-editor` class if it is not added, otherwise it'll be removed + dialogElement.toggleClass('show-editor', !editorShown) - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') + // Update the current scroll value if the editor is not shown already, otherwise, keep the current saved value + scrollValue = !editorShown ? dialogBody[0].scrollTop : scrollValue - return - } catch (e) {} + // Either scroll to the last saved position if the editor is visible, or scroll to the top of the dialog for the editor before showing it + dialogBody[0].scrollTo(0, editorShown ? scrollValue : 0) - let keyspaceName = dialogElement.find('div[action="counter-tables"]').find('div.keyspace-name').text(), - allDataFields = dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field'), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + // Update the editor's layout + $(window.visualViewport).trigger('resize') - isAlterState = isAlterState != null && isAlterState == 'alter' + try { + if (currentActiveAction != 'keyspaces') + throw 0 - try { - if (!isAlterState) - throw 0 + updateActionStatusForKeyspaces() + } catch (e) {} - let alteringStatements = [], - alteredOptions = [], - droppedColumns = [] + try { + if (currentActiveAction != 'udts') + throw 0 - for (let dataField of allDataFields) { - if ($(dataField).hasClass('counter-table-partition-key-field')) - continue + updateActionStatusForUDTs() + } catch (e) {} - try { - if (!$(dataField).hasClass('counter-table-clustering-key-field')) - throw 0 + try { + if (currentActiveAction != 'counter-tables') + throw 0 - let clusteringKeyName = $(dataField).find('input.clusteringKeyName').val(), - clusteringKeyTypeElement = $(dataField).find('input.clusteringKeyType'), - currentType = clusteringKeyTypeElement.val(), - originalType = clusteringKeyTypeElement.attr('data-original-type') + updateActionStatusForCounterTables() + } catch (e) {} - if (currentType != originalType) - alteringStatements.push(`ALTER ${clusteringKeyName} TYPE ${currentType}`) + try { + if (currentActiveAction != 'standard-tables') + throw 0 - continue - } catch (e) {} + updateActionStatusForStandardTables() + } catch (e) {} - try { - if (!$(dataField).hasClass('counter-table-column-field')) - throw 0 + try { + if (currentActiveAction != 'insert-row') + throw 0 - let counterColumnNameElement = $(dataField).find('input.counterColumnName'), - counterColumnName = counterColumnNameElement.val(), - originalName = counterColumnNameElement.attr('data-original-name') + updateActionStatusForInsertRow() + } catch (e) {} + }) - if ($(dataField).hasClass('deleted')) { - alteringStatements.push(`DROP ${counterColumnName}`) + $('input[type="checkbox"]#keyspaceDurableWrites').on('change', function() { + try { + updateActionStatusForKeyspaces() + } catch (e) {} + }) - continue - } + $('button#executeActionStatement').click(function() { + let currentActiveAction = $('div.modal#rightClickActionsMetadata div[action]').filter(':visible').attr('action') - if (originalName != undefined && counterColumnName != originalName) - alteringStatements.push(`RENAME ${counterColumnName} TO ${originalName}`) + try { + if (currentActiveAction != 'keyspaces') + throw 0 - if (originalName == undefined) - alteringStatements.push(`ADD ${counterColumnName} counter`) + updateActionStatusForKeyspaces() + } catch (e) {} - continue - } catch (e) {} + try { + if (currentActiveAction != 'udts') + throw 0 - try { - if (!$(dataField).hasClass('counter-table-option-field')) - throw 0 + updateActionStatusForUDTs() + } catch (e) {} - let tableOptionName = $(dataField).find('input.tableOptionName').val(), - tableOptionValue = $(dataField).find('input.tableOptionValue').val(), - [originalName, originalValue] = getAttributes($(dataField), ['data-original-name', 'data-original-value']) + try { + if (currentActiveAction != 'insert-row') + throw 0 - if (tableOptionName != originalName || tableOptionValue != originalValue) - alteredOptions.push(`${alteredOptions.length <= 0 ? 'WITH' : 'AND'} ${tableOptionName} = ${tableOptionValue}`) - } catch (e) {} - } + updateActionStatusForInsertRow() + } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', [...alteringStatements, ...alteredOptions, ...droppedColumns].length <= 0 ? '' : null) + try { + if (currentActiveAction != 'counter-tables') + throw 0 - let statement = [...alteringStatements, ...droppedColumns].map((statement) => `ALTER TABLE ${keyspaceName}.${counterTableName} ${statement}`).join(';' + OS.EOL) + ';' + updateActionStatusForCounterTables() + } catch (e) {} - try { - if (alteredOptions.length <= 0) - throw 0 + try { + if (currentActiveAction != 'standard-tables') + throw 0 - statement = alteringStatements.length <= 0 ? '' : `${statement}` + OS.EOL + updateActionStatusForStandardTables() + } catch (e) {} - statement += `ALTER TABLE ${keyspaceName}.${counterTableName} ` + alteredOptions.join(' ') + ';' - } catch (e) {} + try { + getElementMDBObject($(`a.nav-link.btn[href="#${$(this).attr('data-tab-id')}"]`), 'Tab').show() + } catch (e) {} - try { - actionEditor.setValue(statement) - } catch (e) {} + let activeWorkarea = $(`div.body div.right div.content div[content="workarea"] div.workarea[cluster-id="${activeClusterID}"]`) - return - } catch (e) {} + try { + activeWorkarea.find('div.terminal-container').hide() + activeWorkarea.find('div.interactive-terminal-container').show() + } catch (e) {} - let partitionKeys = [], - clusteringKeys = [], - counterColumns = [], - tableOptions = [], - primaryKeys = '', - order = { - asc: [], - desc: [] - } + try { + let statementInputField = $(`textarea#${$(this).attr('data-textarea-id')}`) + statementInputField.val(actionEditor.getValue()) + statementInputField.trigger('input').focus() + AutoSize.update(statementInputField[0]) + } catch (e) {} - try { - for (let dataField of allDataFields) { - try { - if (!$(dataField).hasClass('counter-table-partition-key-field')) - throw 0 + try { + setTimeout(() => $(`button#${$(this).attr('data-btn-id')}`).click(), 100) + } catch (e) {} - let name = $(dataField).find('input.partitionKeyName').val(), - type = $(dataField).find('input.partitionKeyType').val(), - isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == type), - isCollectionMap = isTypeCollection && type == 'map' + try { + setTimeout(() => getElementMDBObject($('#rightClickActionsMetadata'), 'Modal').hide(), 50) + } catch (e) {} + }) - try { - if (isTypeCollection) - throw 0 + let updateActionStatusForUDTs - partitionKeys.push({ - name, - type - }) + { + let getFieldElement = (keyspaceUDTs = []) => { + let typesList = ` +
  • +
  • int
  • +
  • bigint
  • +
  • smallint
  • +
  • tinyint
  • +
  • varint
  • +
  • float
  • +
  • double
  • +
  • decimal
  • +
  • +
  • text
  • +
  • varchar
  • +
  • ascii
  • +
  • +
  • boolean
  • +
  • +
  • timestamp
  • +
  • date
  • +
  • duration
  • +
  • time
  • +
  • +
  • blob
  • +
  • +
  • uuid
  • +
  • timeuuid
  • +
  • +
  • inet
  • `, + collectionsTypesItems = ` +
  • +
  • list<type>
  • +
  • set<type>
  • +
  • map<key_type, value_type>
  • ` + defaultType = 'text' + + try { + if (keyspaceUDTs == null || keyspaceUDTs.length <= 0) + throw 0 + + typesList = '' + + defaultType = keyspaceUDTs[0] + + for (let udt of keyspaceUDTs) + typesList += `
  • ${udt}
  • ` + } catch (e) {} + + let [ + collectionKeyTypeID, + collectionItemTypeID, + fieldDataTypeID + ] = getRandomID(10, 3).map((id) => `_${id}`), + element = ` +
    0 ? 'for-udt-data-field' : ''}> +
    +
    + + +
    +
    +
    +
    + + + +
    +
    +
    + +
    + + +
    + + + +
    +
    ` + + return element + }, + updateRowsZIndex = (isTransformNegative = false) => { + setTimeout(() => { + let rows = dialogElement.find('div.data-field.row').get(), + rowsCount = rows.length + + if (isTransformNegative) + rows = rows.reverse() + + for (let row of rows) { + $(row).css('z-index', `${rowsCount}`) + + rowsCount -= 1 + } + }) + } + + updateActionStatusForUDTs = () => { + let udtName = addDoubleQuotes($('input#udtName').val()), + keyspaceName = addDoubleQuotes(dialogElement.find('div[action="udts"]').find('div.keyspace-name').text()), + allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row'), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state'), + dataFieldsText = '' + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (dialogElement.find('div[action="udts"]').find('.is-invalid:not(.ignore-invalid)').length <= 0 && + dialogElement.find('div[action="udts"]').find('div.data-field.row').length > 0 && + minifyText(udtName).length > 0) + throw 0 + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') + + return + } catch (e) {} + + try { + if (!isAlterState) + throw 0 + + let statements = [] + + for (let row of dialogElement.find('div[action="udts"]').find('div.data-field.row')) { + let rowElement = $(row), + fieldName = addDoubleQuotes(rowElement.find('input.fieldName').attr('data-original-value')), + fieldType = addDoubleQuotes(rowElement.find('input.fieldDataType').attr('data-original-value')) + + // Deleting a field is the first thing to be checked + if (rowElement.hasClass('deleted')) { + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} DROP ${fieldName};`) + continue + } + + // The type of the field has been changed + { + let isChangeInTypeDetected = false + + try { + for (let typeRelatedField of [rowElement.find('input.fieldDataType'), rowElement.find('input.collectionItemType'), rowElement.find('input.collectionKeyType')]) { + let setValue = typeRelatedField.val(), + originalValue = typeRelatedField.attr('data-original-value') + + if (setValue != originalValue && originalValue != undefined) { + isChangeInTypeDetected = true + break + } + } } catch (e) {} try { - if (!isTypeCollection) + if (!isChangeInTypeDetected) throw 0 - let tempJSON = { - value: $(dataField).find('input.collectionItemType').val() + // Second is altering the type of the field + let newFieldType = rowElement.find('input.fieldDataType').val() + + if (!([newFieldType, fieldName].some((data) => data == undefined))) { + try { + if (['map', 'set', 'list'].some((type) => newFieldType == type)) + throw 0 + + let finalNewFieldType = addDoubleQuotes(`${newFieldType}`) + + if (rowElement.parent().hasClass('data-udt-fields')) + finalNewFieldType = `frozen<${finalNewFieldType}>` + + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${finalNewFieldType};`) + } catch (e) {} + + try { + if (!(['map', 'set', 'list'].some((type) => newFieldType == type))) + throw 0 + + let collectionItemType = addDoubleQuotes(rowElement.find('input.collectionItemType').val()) + + if (`${newFieldType}` != 'map') { + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${newFieldType}<${collectionItemType}>;`) + throw 0 + } + + let collectionKeyType = addDoubleQuotes(rowElement.find('input.collectionKeyType').val()) + + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ALTER ${fieldName} TYPE ${newFieldType}<${collectionKeyType}, ${collectionItemType}>;`) + } catch (e) {} } + } catch (e) {} + } - if (isCollectionMap) - tempJSON.key = $(dataField).find('input.collectionKeyType').val() + // The field's name has been changed + { + try { + let setFieldName = addDoubleQuotes(rowElement.find('input.fieldName').val()) - partitionKeys.push({ - name, - type, - ...tempJSON - }) + if (fieldName == setFieldName || fieldName == undefined) + throw 0 + + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} RENAME ${fieldName} TO ${setFieldName};`) } catch (e) {} + } + + // A new field has been added + { + try { + if (rowElement.attr('data-original-field') != undefined) + throw 0 + + fieldName = addDoubleQuotes(rowElement.find('input.fieldName').val()) + fieldType = addDoubleQuotes(rowElement.find('input.fieldDataType').val()) + + try { + if (['map', 'set', 'list'].some((type) => fieldType == type)) + throw 0 + + let finalFieldType = `${fieldType}` + + if (rowElement.parent().hasClass('data-udt-fields')) + finalFieldType = `frozen<${finalFieldType}>` + + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${finalFieldType};`) + } catch (e) {} + + try { + if (!(['map', 'set', 'list'].some((type) => fieldType == type))) + throw 0 + + let collectionItemType = addDoubleQuotes(rowElement.find('input.collectionItemType').val()) + + if (`${fieldType}` != 'map') { + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${fieldType}<${collectionItemType}>;`) + + throw 0 + } + + let collectionKeyType = rowElement.find('input.collectionKeyType').val() + + statements.push(`ALTER TYPE ${keyspaceName}.${udtName} ADD ${fieldName} ${fieldType}<${collectionKeyType}, ${collectionItemType}>;`) + } catch (e) {} + } catch (e) {} + } + } + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', statements.length <= 0 ? '' : null) + + try { + actionEditor.setValue(statements.join(OS.EOL)) + } catch (e) {} + + return + } catch (e) {} + + try { + let currentIndex = 0 + + for (let dataField of allDataFields) { + let tempTxt = '', + dataFieldUIElement = $(dataField), + isUDTDataField = $(dataField).attr('for-udt-data-field') != undefined, + fieldName = addDoubleQuotes(dataFieldUIElement.find('input.fieldName').val()), + fieldType = addDoubleQuotes(dataFieldUIElement.find('input.fieldDataType').val()), + collectionKeyType = addDoubleQuotes(dataFieldUIElement.find('input.collectionKeyType').val()), + collectionItemType = addDoubleQuotes(dataFieldUIElement.find('input.collectionItemType').val()) + + currentIndex += 1 + + try { + if (!isUDTDataField) + throw 0 + + tempTxt = `${fieldName} frozen<${fieldType}>` + } catch (e) {} + + try { + if (isUDTDataField) + throw 0 + + let finalFieldType = `${fieldName} ${fieldType}` + + if (['set', 'list'].some((type) => fieldType == type)) + finalFieldType = `${fieldName} ${fieldType}<${collectionItemType}>` + + if (fieldType == 'map') + finalFieldType = `${fieldName} ${fieldType}<${collectionKeyType},${collectionItemType}>` + + tempTxt = finalFieldType + } catch (e) {} + + if (currentIndex != allDataFields.length) + tempTxt = `${tempTxt},` + + tempTxt = ` ${tempTxt}` + + dataFieldsText += tempTxt + OS.EOL + } + } catch (e) {} + + dataFieldsText = OS.EOL + dataFieldsText + + let statement = `CREATE TYPE ${keyspaceName}.${udtName} (${dataFieldsText});` + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + + try { + actionEditor.setValue(statement) + } catch (e) {} + } + + let dataFieldsContainer = dialogElement.find('div.data-fields'), + dataUDTFieldsContainer = dialogElement.find('div.data-udt-fields') + + $(`a[action]#addDataField`).on('click', function(_, fields = null) { + dataFieldsContainer.children('div.empty-fields').hide() + + try { + if (fields == null) + throw 0 + + fields = JSON.parse(fields) + + let filterdFields = fields.filter((field) => !field.type.includes('frozen<')) + + if (filterdFields.length <= 0) { + dataFieldsContainer.children('div.empty-fields').show() + return + } + + for (let field of filterdFields) { + dataFieldsContainer.prepend($(getFieldElement()).show(function() { + let row = $(this) + + row.attr('data-original-field', 'true') + + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + row.find('div.dropdown[for-select]').each(function() { + let dropDownElement = $(this), + // Get the MDB object of the current dropdown element + selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), + // Point at the associated input field + input = row.find(`input#${dropDownElement.attr('for-select')}`) + + // Once the associated select element is being focused then show the dropdown element and vice versa + input.on('focus', () => { + try { + input.parent().find('div.invalid-feedback').addClass('transparent-color') + } catch (e) {} + + selectDropdown.show() + }).on('focusout', () => setTimeout(() => { + try { + input.parent().find('div.invalid-feedback').removeClass('transparent-color') + } catch (e) {} + + selectDropdown.hide() + }, 100)) + + // Once the parent `form-outline` is clicked trigger the `focus` event + input.parent().click(() => input.trigger('focus')) + }) + + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) + + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined + + try { + if (!mainDropDown) + throw 0 + + row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + } catch (e) {} + + // Update the input's value + selectElement.val(selectedValue).trigger('input') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + }) + } + + $(this).find(`a[action="delete-udt"]`).click(function() { + row.toggleClass('deleted') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { + let fieldName = $(this).val(), + fieldRow = $(this).parent().parent().parent(), + isNameDuplicated = false, + isNameInvalid = false, + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (`${fieldName}`.length <= 0) + throw 0 + + isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + } catch (e) {} + + try { + let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + + for (let dataField of allDataFields) { + let dataFieldNameElement = $(dataField).find('input.fieldName') + + if (triggerInput) + dataFieldNameElement.trigger('input', false) + + if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) + continue + + isNameDuplicated = true + break + } + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) + + setTimeout(() => mdbObject.update(), 500) + }) + }) + + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + + updateRowsZIndex() + + try { + row.find('input.fieldName').val(field.name).trigger('input') + row.find('input.fieldName').attr('data-original-value', field.name) + + if (['map', 'set', 'list'].some((type) => field.type == type)) + throw 0 + + row.find('input.fieldDataType').val(field.type).trigger('input') + row.find('input.fieldDataType').attr('data-original-value', field.type) + } catch (e) {} + + try { + let extractData = field.type.match(/(.+)\<(.*?)\>/) + + if (extractData == null || !(['map', 'set', 'list'].some((type) => extractData[1].includes(type)))) + throw 0 + + $(`div.dropdown[for-select="${row.find('input.fieldDataType').attr('id')}"]`).find(`li a[value="${extractData[1]}"]`).trigger('click') + + row.find('input.fieldDataType').attr('data-original-value', extractData[1]) + + if (extractData[1] != 'map') { + row.find('input.collectionItemType').val(extractData[2]).trigger('input') + row.find('input.collectionItemType').attr('data-original-value', extractData[2]) + } else { + let mapValues = minifyText(extractData[2]).split(',') + + row.find('input.collectionKeyType').val(mapValues[0]).trigger('input') + row.find('input.collectionKeyType').attr('data-original-value', mapValues[0]) + + row.find('input.collectionItemType').val(mapValues[1]).trigger('input') + row.find('input.collectionItemType').attr('data-original-value', mapValues[1]) + } + } catch (e) {} + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + })) + } + + return + } catch (e) {} + + dataFieldsContainer.prepend($(getFieldElement()).show(function() { + let row = $(this) + + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + row.find('div.dropdown[for-select]').each(function() { + let dropDownElement = $(this), + // Get the MDB object of the current dropdown element + selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), + // Point at the associated input field + input = row.find(`input#${dropDownElement.attr('for-select')}`) + + // Once the associated select element is being focused then show the dropdown element and vice versa + input.on('focus', () => { + try { + input.parent().find('div.invalid-feedback').addClass('transparent-color') + } catch (e) {} + + selectDropdown.show() + }).on('focusout', () => setTimeout(() => { + try { + input.parent().find('div.invalid-feedback').removeClass('transparent-color') + } catch (e) {} + + selectDropdown.hide() + }, 100)) + + // Once the parent `form-outline` is clicked trigger the `focus` event + input.parent().click(() => input.trigger('focus')) + }) + + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) + + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined + + try { + if (!mainDropDown) + throw 0 + + row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + } catch (e) {} + + // Update the input's value + selectElement.val(selectedValue).trigger('input') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + }) + } + + $(this).find(`a[action="delete-udt"]`).click(function() { + $(this).parent().parent().remove() + + try { + updateActionStatusForUDTs() + } catch (e) {} + + if (dataFieldsContainer.children('div.data-field.row').length != 0) + return + + dataFieldsContainer.children('div.empty-fields').fadeIn(250) + }) + + $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { + let fieldName = $(this).val(), + fieldRow = $(this).parent().parent().parent(), + isNameDuplicated = false, + isNameInvalid = false, + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (`${fieldName}`.length <= 0) + throw 0 + + isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + } catch (e) {} + + try { + let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + + for (let dataField of allDataFields) { + let dataFieldNameElement = $(dataField).find('input.fieldName') + + if (triggerInput) + dataFieldNameElement.trigger('input', false) + + if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) + continue + + isNameDuplicated = true + break + } + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) + + setTimeout(() => mdbObject.update(), 500) + }) + }) + + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + + updateRowsZIndex() + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + })) + }) + + $(`a[action]#addUDTDataField`).click('click', function(_, fields = null) { + let keyspaceUDTs = [], + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + keyspaceUDTs = JSON.parse(JSONRepair($(dialogElement).attr('data-keyspace-udts'))) + + keyspaceUDTs = keyspaceUDTs.map((udt) => udt.name) + } catch (e) {} + + try { + if (!isAlterState) + throw 0 + + keyspaceUDTs = keyspaceUDTs.filter((udt) => udt != $('input#udtName').val()) + } catch (e) {} + + if (keyspaceUDTs.length <= 0) { + $('a[action]#addUDTDataField').addClass('disabled') + + dataUDTFieldsContainer.children('div.empty-fields').find('span').hide() + dataUDTFieldsContainer.children('div.empty-fields').find('span.one-udt').show() + + return + } + + dataUDTFieldsContainer.children('div.empty-fields').hide() + + try { + if (fields == null) + throw 0 + + fields = JSON.parse(fields) + + let filterdFields = fields.filter((field) => field.type.includes('frozen<')) + + if (filterdFields.length <= 0) { + dataUDTFieldsContainer.children('div.empty-fields').show() + return + } + + for (let field of filterdFields) { + try { + let extractData = field.type.match(/.+\<(.*?)\>/)[1] + + field.type = extractData + } catch (e) {} + + dataUDTFieldsContainer.prepend($(getFieldElement(keyspaceUDTs)).show(function() { + let row = $(this) + + row.attr('data-original-field', 'true') + + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + row.find('div.dropdown[for-select]').each(function() { + let dropDownElement = $(this), + // Get the MDB object of the current dropdown element + selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), + // Point at the associated input field + input = row.find(`input#${dropDownElement.attr('for-select')}`) + + // Once the associated select element is being focused then show the dropdown element and vice versa + input.on('focus', () => { + try { + input.parent().find('div.invalid-feedback').addClass('transparent-color') + } catch (e) {} + + selectDropdown.show() + }).on('focusout', () => setTimeout(() => { + try { + input.parent().find('div.invalid-feedback').removeClass('transparent-color') + } catch (e) {} + + selectDropdown.hide() + }, 100)) + + // Once the parent `form-outline` is clicked trigger the `focus` event + input.parent().click(() => input.trigger('focus')) + }) + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) + + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined + + try { + if (!mainDropDown) + throw 0 + + row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + } catch (e) {} + + // Update the input's value + selectElement.val(selectedValue).trigger('input') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + }) + } + + $(this).find(`a[action="delete-udt"]`).click(function() { + row.toggleClass('deleted') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { + let fieldName = $(this).val(), + fieldRow = $(this).parent().parent().parent(), + isNameDuplicated = false, + isNameInvalid = false, + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (`${fieldName}`.length <= 0) + throw 0 + + isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + } catch (e) {} + + try { + let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + + for (let dataField of allDataFields) { + let dataFieldNameElement = $(dataField).find('input.fieldName') + + if (triggerInput) + dataFieldNameElement.trigger('input', false) + + if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) + continue + + isNameDuplicated = true + break + } + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) + + setTimeout(() => mdbObject.update(), 500) + }) + }) + + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + + updateRowsZIndex() + + try { + row.find('input.fieldName').val(field.name).trigger('input') + row.find('input.fieldName').attr('data-original-value', field.name) + + row.find('input.fieldDataType').val(field.type).trigger('input') + row.find('input.fieldDataType').attr('data-original-value', field.type) + } catch (e) {} + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + })) + } + + return + } catch (e) {} + + dataUDTFieldsContainer.prepend($(getFieldElement(keyspaceUDTs)).show(function() { + let row = $(this) + + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + row.find('div.dropdown[for-select]').each(function() { + let dropDownElement = $(this), + // Get the MDB object of the current dropdown element + selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), + // Point at the associated input field + input = row.find(`input#${dropDownElement.attr('for-select')}`) + + // Once the associated select element is being focused then show the dropdown element and vice versa + input.on('focus', () => { + try { + input.parent().find('div.invalid-feedback').addClass('transparent-color') + } catch (e) {} + + selectDropdown.show() + }).on('focusout', () => setTimeout(() => { + try { + input.parent().find('div.invalid-feedback').removeClass('transparent-color') + } catch (e) {} + + selectDropdown.hide() + }, 100)) + + // Once the parent `form-outline` is clicked trigger the `focus` event + input.parent().click(() => input.trigger('focus')) + }) + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'fieldDataType' + + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) + + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined + + try { + if (!mainDropDown) + throw 0 + + row.find(`div[col="fieldDataType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) + + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 2 : 3) : 6}`) + } catch (e) {} + + // Update the input's value + selectElement.val(selectedValue).trigger('input') + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + }) + } + + $(this).find(`a[action="delete-udt"]`).click(function() { + $(this).parent().parent().remove() + + try { + updateActionStatusForUDTs() + } catch (e) {} + + if (dataUDTFieldsContainer.children('div.data-field.row').length != 0) + return + + dataUDTFieldsContainer.children('div.empty-fields').fadeIn(250) + }) + + $(this).find('input.fieldName').on('input', function(_, triggerInput = true) { + let fieldName = $(this).val(), + fieldRow = $(this).parent().parent().parent(), + isNameDuplicated = false, + isNameInvalid = false, + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (`${fieldName}`.length <= 0) + throw 0 + + isNameInvalid = `${fieldName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + } catch (e) {} + + try { + let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row').not(fieldRow[0]) + + for (let dataField of allDataFields) { + let dataFieldNameElement = $(dataField).find('input.fieldName') + + if (triggerInput) + dataFieldNameElement.trigger('input', false) + + if (minifyText(`${dataFieldNameElement.val()}`) != minifyText(fieldName)) + continue + + isNameDuplicated = true + break + } + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(fieldName).length <= 0) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) + + setTimeout(() => mdbObject.update(), 500) + }) + }) + + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + + updateRowsZIndex() + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + + })) + }) + + $('input#udtName').on('input', function() { + let keyspaceUDTs = [], + udtName = $(this).val(), + isNameDuplicated = false, + isNameInvalid = false, + invalidFeedback = $(this).parent().children('div.invalid-feedback'), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + $(this).attr('disabled', isAlterState ? '' : null) + $(this).parent().toggleClass('invalid-warning', isAlterState) + $(this).toggleClass('is-invalid ignore-invalid', isAlterState) + + try { + if (!isAlterState) + throw 0 + + invalidFeedback.find('span').attr('mulang', 'the UDT name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the UDT name can\'t be altered'))) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + + return + } catch (e) {} + + try { + keyspaceUDTs = JSON.parse(JSONRepair($('#rightClickActionsMetadata').attr('data-keyspace-udts'))) + } catch (e) {} + + try { + if (keyspaceUDTs.length <= 0) + throw 0 + + isNameDuplicated = keyspaceUDTs.some((udt) => minifyText(`${udt.name}`) == minifyText(`${udtName}`)) + + if (isAlterState && isNameDuplicated && (udtName == $('div.modal#rightClickActionsMetadata').attr('data-udt-name'))) + isNameDuplicated = false + + if (isNameDuplicated) + invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) + } catch (e) {} + + try { + if (`${udtName}`.length <= 0) + throw 0 + + isNameInvalid = `${udtName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + + if (isNameInvalid) + invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) + + let allDataFields = dialogElement.find('div[action="udts"]').find('div.data-field.row'), + invalidInputFields = allDataFields.find('input.is-invalid') + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 || allDataFields.length <= 0 || invalidInputFields.length > 0 ? '' : null) + + try { + updateActionStatusForUDTs() + } catch (e) {} + }) + } + + let updateActionStatusForCounterTables + + { + let updateRowsZIndex = (isTransformNegative = false) => { + setTimeout(() => { + let rows = dialogElement.find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field').get(), + rowsCount = rows.length + + if (isTransformNegative) + rows = rows.reverse() + + for (let row of rows) { + $(row).css('z-index', `${rowsCount}`) + + rowsCount -= 1 + } + }) + } + + updateActionStatusForCounterTables = () => { + let counterTableName = addDoubleQuotes($('input#countertableName').val()) + + try { + if (dialogElement.find('div[action="counter-tables"]').find('.is-invalid:not(.ignore-invalid)').length <= 0 && + dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field.row').length > 0 && + dialogElement.find('div[action="counter-tables"]').find('div.counter-table-column-field.row').length > 0 && + minifyText(counterTableName).length > 0) + throw 0 + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', '') + + return + } catch (e) {} + + let keyspaceName = addDoubleQuotes(dialogElement.find('div[action="counter-tables"]').find('div.keyspace-name').text()), + allDataFields = dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field'), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + if (!isAlterState) + throw 0 + + let alteringStatements = [], + alteredOptions = [], + droppedColumns = [] + + for (let dataField of allDataFields) { + if ($(dataField).hasClass('counter-table-partition-key-field')) + continue + + try { + if (!$(dataField).hasClass('counter-table-clustering-key-field')) + throw 0 + + let clusteringKeyName = addDoubleQuotes($(dataField).find('input.clusteringKeyName').val()), + clusteringKeyTypeElement = $(dataField).find('input.clusteringKeyType'), + currentType = addDoubleQuotes(clusteringKeyTypeElement.val()), + originalType = addDoubleQuotes(clusteringKeyTypeElement.attr('data-original-type')) + + if (currentType != originalType) + alteringStatements.push(`ALTER ${clusteringKeyName} TYPE ${currentType}`) + + continue + } catch (e) {} + + try { + if (!$(dataField).hasClass('counter-table-column-field')) + throw 0 + + let counterColumnNameElement = $(dataField).find('input.counterColumnName'), + counterColumnName = addDoubleQuotes(counterColumnNameElement.val()), + originalName = addDoubleQuotes(counterColumnNameElement.attr('data-original-name')) + + if ($(dataField).hasClass('deleted')) { + alteringStatements.push(`DROP ${counterColumnName}`) + + continue + } + + if (originalName != undefined && counterColumnName != originalName) + alteringStatements.push(`RENAME ${counterColumnName} TO ${originalName}`) + + if (originalName == undefined) + alteringStatements.push(`ADD ${counterColumnName} counter`) + + continue + } catch (e) {} + + try { + if (!$(dataField).hasClass('counter-table-option-field')) + throw 0 + + let tableOptionName = $(dataField).find('input.tableOptionName').val(), + tableOptionValue = $(dataField).find('input.tableOptionValue').val(), + [originalName, originalValue] = getAttributes($(dataField), ['data-original-name', 'data-original-value']) + + if (tableOptionName != originalName || tableOptionValue != originalValue) + alteredOptions.push(`${alteredOptions.length <= 0 ? 'WITH' : 'AND'} ${tableOptionName} = ${tableOptionValue}`) + } catch (e) {} + } + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', [...alteringStatements, ...alteredOptions, ...droppedColumns].length <= 0 ? '' : null) + + let statement = [...alteringStatements, ...droppedColumns].map((statement) => `ALTER TABLE ${keyspaceName}.${counterTableName} ${statement}`).join(';' + OS.EOL) + ';' + + try { + if (alteredOptions.length <= 0) + throw 0 + + statement = alteringStatements.length <= 0 ? '' : `${statement}` + OS.EOL + + statement += `ALTER TABLE ${keyspaceName}.${counterTableName} ` + alteredOptions.join(' ') + ';' + } catch (e) {} + + try { + actionEditor.setValue(statement) + } catch (e) {} + + return + } catch (e) {} + + let partitionKeys = [], + clusteringKeys = [], + counterColumns = [], + tableOptions = [], + primaryKeys = '', + order = { + asc: [], + desc: [] + } + + try { + for (let dataField of allDataFields) { + try { + if (!$(dataField).hasClass('counter-table-partition-key-field')) + throw 0 + + let name = $(dataField).find('input.partitionKeyName').val(), + type = $(dataField).find('input.partitionKeyType').val(), + isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == type), + isCollectionMap = isTypeCollection && type == 'map' + + try { + if (isTypeCollection) + throw 0 + + partitionKeys.push({ + name, + type + }) + } catch (e) {} + + try { + if (!isTypeCollection) + throw 0 + + let tempJSON = { + value: $(dataField).find('input.collectionItemType').val() + } + + if (isCollectionMap) + tempJSON.key = $(dataField).find('input.collectionKeyType').val() + + partitionKeys.push({ + name, + type, + ...tempJSON + }) + } catch (e) {} + + order[$(dataField).find('div.btn.field-sort-type').attr('data-current-sort') != 'asc' ? 'desc' : 'asc'].push(name) + } catch (e) {} + + try { + if (!$(dataField).hasClass('counter-table-clustering-key-field')) + throw 0 + + let name = $(dataField).find('input.clusteringKeyName').val(), + type = $(dataField).find('input.clusteringKeyType').val(), + isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == type), + isCollectionMap = isTypeCollection && type == 'map' + + try { + if (isTypeCollection) + throw 0 + + clusteringKeys.push({ + name, + type + }) + } catch (e) {} + + try { + if (!isTypeCollection) + throw 0 + + let tempJSON = { + value: $(dataField).find('input.collectionItemType').val() + } + + if (isCollectionMap) + tempJSON.key = $(dataField).find('input.collectionKeyType').val() + + clusteringKeys.push({ + name, + type, + ...tempJSON + }) + } catch (e) {} + + order[$(dataField).find('div.btn.field-sort-type').attr('data-current-sort') != 'asc' ? 'desc' : 'asc'].push(name) + } catch (e) {} + + try { + if (!$(dataField).hasClass('counter-table-column-field')) + throw 0 + + counterColumns.push({ + name: $(dataField).find('input.counterColumnName').val(), + type: 'counter' + }) + } catch (e) {} + + try { + if (!$(dataField).hasClass('counter-table-option-field')) + throw 0 + + let name = $(dataField).find('input.tableOptionName').val(), + value = $(dataField).find('input.tableOptionValue').val() + + try { + if ($(dataField).attr('data-is-default') != 'true') + throw 0 + + let defaultName = $(dataField).attr('data-default-name'), + defaultValue = $(dataField).attr('data-default-value') + + if (defaultName == name && defaultValue == value) + continue + } catch (e) {} + + tableOptions.push({ + name, + value + }) + } catch (e) {} + } + } catch (e) {} + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + + let manipulatedKeysAndColumns = ([...partitionKeys, ...clusteringKeys, ...counterColumns].map((key) => { + let isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == key.type), + isCollectionMap = isTypeCollection && key.type == 'map', + keyspaceUDTs = [], + isTypeUDT = false + + + try { + key.name = addDoubleQuotes(key.name) + } catch (e) {} + + try { + key.type = addDoubleQuotes(key.type) + } catch (e) {} + + try { + key.key = addDoubleQuotes(key.key) + } catch (e) {} + + try { + key.value = addDoubleQuotes(key.value) + } catch (e) {} + + try { + keyspaceUDTs = JSON.parse(JSONRepair($('div.modal#rightClickActionsMetadata').attr('data-keyspace-udts'))).map((udt) => udt.name) + } catch (e) {} + + isTypeUDT = keyspaceUDTs.find((udt) => key.type == udt) + + try { + if (!isTypeUDT) + throw 0 + + key.type = `frozen<${key.type}>` + } catch (e) {} + + try { + if (!isTypeCollection) + throw 0 + + let collectionType = isCollectionMap ? `${key.key}, ${key.value}` : `${key.value}` + + key.type = `frozen<${key.type}<${collectionType}>>` + } catch (e) {} + + return ` ${key.name} ${key.type},` + OS.EOL + })).join('') + + try { + primaryKeys = `(` + (partitionKeys.map((key) => key.name)).join(', ') + `)` + } catch (e) {} + + try { + if (clusteringKeys.length <= 0) + throw 0 + + primaryKeys += `, ` + (clusteringKeys.map((key) => key.name)).join(', ') + } catch (e) {} + + let descOrder = [...order.asc, ...order.desc] + + try { + if (tableOptions.length <= 0) + throw 0 + + let tempTxt = descOrder.length > 0 ? OS.EOL + ' AND ' : ` WITH ` + + tempTxt += (tableOptions.map((option) => { + option.value = option.value.startsWith('{') && option.value.endsWith('}') ? option.value : `'${option.value}'` + + return `${option.name} = ${option.value}` + })).join(OS.EOL + ' AND ') + + tableOptions = tempTxt + } catch (e) { + tableOptions = '' + } + + try { + if (descOrder.length <= 0) + throw 0 + + descOrder = ` WITH CLUSTERING ORDER BY (` + (clusteringKeys.map((key) => `${key.name} ${order.desc.includes(key.name) ? 'DESC' : 'ASC'}`)).join(', ') + `)` + } catch (e) { + descOrder = '' + } + + let statement = `CREATE TABLE ${keyspaceName}.${counterTableName} (` + OS.EOL + `${manipulatedKeysAndColumns}` + ` PRIMARY KEY (${primaryKeys})` + OS.EOL + ')' + `${descOrder}` + `${tableOptions}` + ';' + + try { + actionEditor.setValue(statement) + } catch (e) {} + } + + setTimeout(() => { + try { + dialogElement.find('div.counter-table-columns-fields, div.counter-table-partition-keys-fields').sortable({ + handle: '.sort-handler', + animation: 150, + ghostClass: 'ghost-field', + onSort: () => updateRowsZIndex() + }) + } catch (e) {} + }, 1000) + + $('input#countertableName').on('input', function() { + let keyspaceTables = [], + countertableName = $(this).val(), + isNameDuplicated = false, + isNameInvalid = false, + invalidFeedback = $(this).parent().children('div.invalid-feedback'), + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + $(this).attr('disabled', isAlterState ? '' : null) + $(this).parent().toggleClass('invalid-warning', isAlterState) + $(this).toggleClass('is-invalid ignore-invalid', isAlterState) + + try { + if (!isAlterState) + throw 0 + + invalidFeedback.find('span').attr('mulang', 'the table name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the table name can\'t be altered'))) + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + + return + } catch (e) {} + + try { + keyspaceTables = JSON.parse(JSONRepair($('#rightClickActionsMetadata').attr('data-keyspace-tables'))) + } catch (e) {} + + try { + if (keyspaceTables.length <= 0) + throw 0 + + isNameDuplicated = keyspaceTables.some((name) => minifyText(`${name}`) == minifyText(`${countertableName}`)) + + if (isAlterState && isNameDuplicated && (countertableName == $('div.modal#rightClickActionsMetadata').attr('data-table-name'))) + isNameDuplicated = false + + if (isNameDuplicated) + invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) + } catch (e) {} + + try { + if (`${countertableName}`.length <= 0) + throw 0 + + isNameInvalid = `${countertableName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + + if (isNameInvalid) + invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) + } catch (e) {} + + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) + + let allDataFields = dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field'), + invalidInputFields = allDataFields.find('input.is-invalid') + + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 || allDataFields.length <= 0 || invalidInputFields.length > 0 ? '' : null) + + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + + $(`a[action]#addCounterTablePartitionKey`).on('click', function(_, fields = null) { + let dataFieldsContainer = dialogElement.find('div.counter-table-partition-keys-fields'), + getPartitionKeyFieldElement = (keyspaceUDTs = []) => { + let typesList = ` +
  • +
  • int
  • +
  • bigint
  • +
  • smallint
  • +
  • tinyint
  • +
  • varint
  • +
  • float
  • +
  • double
  • +
  • decimal
  • +
  • +
  • text
  • +
  • varchar
  • +
  • ascii
  • +
  • +
  • boolean
  • +
  • +
  • timestamp
  • +
  • date
  • +
  • duration
  • +
  • time
  • +
  • +
  • blob
  • +
  • +
  • uuid
  • +
  • timeuuid
  • +
  • +
  • inet
  • `, + collectionsTypesItems = ` +
  • +
  • list<type>
  • +
  • set<type>
  • +
  • map<key_type, value_type>
  • ` + defaultType = 'text' + + try { + if (keyspaceUDTs.length <= 0) + throw 0 + + typesList += '
  • ' + + for (let udt of keyspaceUDTs) + typesList += `
  • ${udt}
  • ` + } catch (e) {} + + let [ + collectionKeyTypeID, + collectionItemTypeID, + partitionKeyTypeID + ] = getRandomID(10, 3).map((id) => `_${id}`), + element = ` +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + + + +
    +
    +
    + +
    + + + +
    + + + +
    +
    ` + + return element + } + + dataFieldsContainer.children('div.empty-counter-table-partition-keys').hide() + + let keyspaceUDTs = [], + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + + isAlterState = isAlterState != null && isAlterState == 'alter' + + try { + keyspaceUDTs = JSON.parse(JSONRepair($(dialogElement).attr('data-keyspace-udts'))) + + keyspaceUDTs = keyspaceUDTs.map((udt) => udt.name) + } catch (e) {} + + try { + if (fields == null) + throw 0 + + fields = JSON.parse(fields) + + for (let field of fields) { + dataFieldsContainer.append($(getPartitionKeyFieldElement(keyspaceUDTs)).show(function() { + let row = $(this) + + $(this).attr('data-is-altered', 'true') + + setTimeout(() => { + $(this).find('div.sort-handler').parent().hide() + + $(this).find('a:not([value]), input').removeClass('is-invalid').addClass('disabled').attr('disabled', 'disabled').css('background-color', '') + + $(this).find(`a[action="delete-counter-table-partition-key"]`).parent().hide() + + $(this).find('div[col="partitionKeyName"], div[col="partitionKeyType"]').removeClass('col-md-5').addClass('col-md-6') + + $(this).find('ion-icon[name="arrow-down"]').hide() + }) + + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') + + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'partitionKeyType' + + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') + + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') + + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) + + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined, + isAltered = row.attr('data-is-altered') != undefined + + try { + if (!mainDropDown) + throw 0 + + let newColMD = isTypeCollection ? (isCollectionMap ? 3 : 4) : 5 - order[$(dataField).find('div.btn.field-sort-type').attr('data-current-sort') != 'asc' ? 'desc' : 'asc'].push(name) - } catch (e) {} + if (isAltered) + newColMD += 1 - try { - if (!$(dataField).hasClass('counter-table-clustering-key-field')) - throw 0 + row.find(`div[col="partitionKeyName"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${newColMD}`) - let name = $(dataField).find('input.clusteringKeyName').val(), - type = $(dataField).find('input.clusteringKeyType').val(), - isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == type), - isCollectionMap = isTypeCollection && type == 'map' + row.find(`div[col="partitionKeyType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${newColMD}`) - try { - if (isTypeCollection) - throw 0 + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) - clusteringKeys.push({ - name, - type - }) - } catch (e) {} + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection) + } catch (e) {} - try { - if (!isTypeCollection) - throw 0 + // Update the input's value + selectElement.val(selectedValue).trigger('input') - let tempJSON = { - value: $(dataField).find('input.collectionItemType').val() + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + }) } + }) - if (isCollectionMap) - tempJSON.key = $(dataField).find('input.collectionKeyType').val() + setTimeout(() => { + $(this).find('input.partitionKeyName').val(`${field.name}`).trigger('input') - clusteringKeys.push({ - name, - type, - ...tempJSON - }) - } catch (e) {} + let fieldType = field.type - order[$(dataField).find('div.btn.field-sort-type').attr('data-current-sort') != 'asc' ? 'desc' : 'asc'].push(name) - } catch (e) {} + try { + fieldType = field.type.match(/frozen\<(.*?)(\<|\>)/)[1] + } catch (e) {} - try { - if (!$(dataField).hasClass('counter-table-column-field')) - throw 0 + try { + if (!(['map', 'set', 'list'].some((type) => type == fieldType))) + throw 0 - counterColumns.push({ - name: $(dataField).find('input.counterColumnName').val(), - type: 'counter' - }) - } catch (e) {} + let fieldKeyType = field.type.match(/frozen\<.*?\<(.*?)\>/)[1] - try { - if (!$(dataField).hasClass('counter-table-option-field')) - throw 0 + if (fieldType == 'map') { + let mapValues = minifyText(fieldKeyType).split(',') - let name = $(dataField).find('input.tableOptionName').val(), - value = $(dataField).find('input.tableOptionValue').val() + $(this).find('input.collectionKeyType').val(`${mapValues[0]}`).trigger('input') + $(this).find('input.collectionItemType').val(`${mapValues[1]}`).trigger('input') + } else { + $(this).find('input.collectionKeyType').val(`${fieldKeyType}`).trigger('input') + } + } catch (e) {} - try { - if ($(dataField).attr('data-is-default') != 'true') - throw 0 + $(this).find('div.dropdown[for-data-type="partitionKeyType"]').find(`a[value="${fieldType}"]`).trigger('click') + }) - let defaultName = $(dataField).attr('data-default-name'), - defaultValue = $(dataField).attr('data-default-value') + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) - if (defaultName == name && defaultValue == value) - continue + setTimeout(() => mdbObject.update(), 500) + }) + }) + + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) + + try { + updateRowsZIndex() } catch (e) {} - tableOptions.push({ - name, - value + $(`a[action]#addCounterTableClusteringKey`).addClass('disabled') + + setTimeout(() => { + try { + updateActionStatusForCounterTables() + } catch (e) {} }) - } catch (e) {} + })) } + + return } catch (e) {} - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + dataFieldsContainer.append($(getPartitionKeyFieldElement(keyspaceUDTs)).show(function() { + let row = $(this) - let manipulatedKeysAndColumns = ([...partitionKeys, ...clusteringKeys, ...counterColumns].map((key) => { - let isTypeCollection = ['map', 'set', 'list'].some((collectionType) => collectionType == key.type), - isCollectionMap = isTypeCollection && key.type == 'map', - keyspaceUDTs = [], - isTypeUDT = false + setTimeout(() => { + $(this).find(`a[action="delete-counter-table-partition-key"]`).click(function() { + $(this).parent().parent().remove() - try { - keyspaceUDTs = JSON.parse($('div.modal#rightClickActionsMetadata').attr('data-keyspace-udts')).map((udt) => udt.name) - } catch (e) {} + try { + updateActionStatusForCounterTables() + } catch (e) {} - isTypeUDT = keyspaceUDTs.find((udt) => key.type == udt) + if (dataFieldsContainer.children('div.counter-table-partition-key-field.row').length != 0) + return - try { - if (!isTypeUDT) - throw 0 + $(`a[action]#addCounterTableClusteringKey`).addClass('disabled') - key.type = `frozen<${key.type}>` - } catch (e) {} + dataFieldsContainer.children('div.empty-counter-table-partition-keys').fadeIn(250) + }) - try { - if (!isTypeCollection) - throw 0 + $(this).find(`div.btn.field-sort-type`).click(function() { + let currentSort = $(this).attr('data-current-sort'), + newSort = (currentSort == 'asc' ? 'desc' : 'asc') - let collectionType = isCollectionMap ? `${key.key}, ${key.value}` : `${key.value}` + $(this).attr('data-current-sort', newSort) - key.type = `frozen<${key.type}<${collectionType}>>` - } catch (e) {} + $(this).find('ion-icon').attr('name', `sort-${newSort}`) + $(this).find('span').text(`${newSort}`) - return ` ${key.name} ${key.type},` + OS.EOL - })).join('') + setTimeout(() => { + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + }) - try { - primaryKeys = `(` + (partitionKeys.map((key) => key.name)).join(', ') + `)` - } catch (e) {} + $(this).find('input.partitionKeyName').on('input', function(_, triggerInput = true) { + let partitionKeyName = $(this).val(), + fieldRow = $(this).parent().parent().parent(), + isNameDuplicated = false, + isNameInvalid = false, + isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') - try { - if (clusteringKeys.length <= 0) - throw 0 + isAlterState = isAlterState != null && isAlterState == 'alter' - primaryKeys += `, ` + (clusteringKeys.map((key) => key.name)).join(', ') - } catch (e) {} + try { + if (`${partitionKeyName}`.length <= 0) + throw 0 - let descOrder = [...order.asc, ...order.desc] + isNameInvalid = `${partitionKeyName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + } catch (e) {} - try { - if (tableOptions.length <= 0) - throw 0 + try { + let allDataFields = dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field').not(fieldRow[0]) - let tempTxt = descOrder.length > 0 ? OS.EOL + ' AND ' : ` WITH ` + for (let dataField of allDataFields) { + let dataPartitionKeyNameElement = $(dataField).find('input.partitionKeyName, input.clusteringKeyName, input.counterColumnName') - tempTxt += (tableOptions.map((option) => { - option.value = option.value.startsWith('{') && option.value.endsWith('}') ? option.value : `'${option.value}'` + if (triggerInput) + dataPartitionKeyNameElement.trigger('input', false) - return `${option.name} = ${option.value}` - })).join(OS.EOL + ' AND ') + if (minifyText(`${dataPartitionKeyNameElement.val()}`) != minifyText(partitionKeyName)) + continue - tableOptions = tempTxt - } catch (e) { - tableOptions = '' - } + isNameDuplicated = true + break + } + } catch (e) {} - try { - if (descOrder.length <= 0) - throw 0 + $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid || minifyText(partitionKeyName).length <= 0) - descOrder = ` WITH CLUSTERING ORDER BY (` + (clusteringKeys.map((key) => `${key.name} ${order.desc.includes(key.name) ? 'DESC' : 'ASC'}`)).join(', ') + `)` - } catch (e) { - descOrder = '' - } + dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', isNameDuplicated || isNameInvalid ? '' : null) - let statement = `CREATE TABLE ${keyspaceName}.${counterTableName} (` + OS.EOL + `${manipulatedKeysAndColumns}` + ` PRIMARY KEY (${primaryKeys})` + OS.EOL + ')' + `${descOrder}` + `${tableOptions}` + ';' + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + }) - try { - actionEditor.setValue(statement) - } catch (e) {} - } + setTimeout(() => { + let dropDownMDBObject = getElementMDBObject(row.find(`div.dropdown[for-select]`), 'Dropdown') - setTimeout(() => { - try { - dialogElement.find('div.counter-table-columns-fields, div.counter-table-partition-keys-fields').sortable({ - handle: '.sort-handler', - animation: 150, - ghostClass: 'ghost-field', - onSort: () => updateRowsZIndex() - }) - } catch (e) {} - }, 1000) + setTimeout(() => { + try { + dropDownMDBObject.update() + } catch (e) {} + }, 500) + + { + row.find('div.dropdown[for-select]').each(function() { + let dropDownElement = $(this), + // Get the MDB object of the current dropdown element + selectDropdown = getElementMDBObject(dropDownElement, 'Dropdown'), + // Point at the associated input field + input = row.find(`input#${dropDownElement.attr('for-select')}`) + + // Once the associated select element is being focused then show the dropdown element and vice versa + input.on('focus', () => { + try { + input.parent().find('div.invalid-feedback').addClass('transparent-color') + } catch (e) {} - $('input#countertableName').on('input', function() { - let keyspaceTables = [], - countertableName = $(this).val(), - isNameDuplicated = false, - isNameInvalid = false, - invalidFeedback = $(this).parent().children('div.invalid-feedback'), - isAlterState = $('div.modal#rightClickActionsMetadata').attr('data-state') + selectDropdown.show() + }).on('focusout', () => setTimeout(() => { + try { + input.parent().find('div.invalid-feedback').removeClass('transparent-color') + } catch (e) {} - isAlterState = isAlterState != null && isAlterState == 'alter' + selectDropdown.hide() + }, 100)) - $(this).attr('disabled', isAlterState ? '' : null) - $(this).parent().toggleClass('invalid-warning', isAlterState) - $(this).toggleClass('is-invalid ignore-invalid', isAlterState) + // Once the parent `form-outline` is clicked trigger the `focus` event + input.parent().click(() => input.trigger('focus')) + }) - try { - if (!isAlterState) - throw 0 + // Once one of the items is clicked + $(this).find(`div.dropdown[for-select]`).each(function() { + let mainDropDown = $(this).attr('for-data-type') == 'partitionKeyType' - invalidFeedback.find('span').attr('mulang', 'the table name can\'t be altered').text(I18next.capitalizeFirstLetter(I18next.t('the table name can\'t be altered'))) + $(this).find(`ul.dropdown-menu`).mutate('transform', () => { + let isTransformNegative = `${$(this).find(`ul.dropdown-menu`).css('transform')}`.includes('-') - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', null) + $(this).find(`ul.dropdown-menu`).find('li').last().css('margin-bottom', isTransformNegative ? '20px' : '') - return - } catch (e) {} + try { + updateRowsZIndex(isTransformNegative) + } catch (e) {} + }) - try { - keyspaceTables = JSON.parse($('#rightClickActionsMetadata').attr('data-keyspace-tables')) - } catch (e) {} + $(this).find(`ul.dropdown-menu`).find('a').click(function() { + // Point at the input field related to the list + let selectElement = $(`input#${$(this).parent().parent().parent().attr('for-select')}`), + selectedValue = $(this).attr('value'), + isTypeCollection = $(this).attr('data-is-collection') != undefined, + isCollectionMap = $(this).attr('data-is-map') != undefined - try { - if (keyspaceTables.length <= 0) - throw 0 + try { + if (!mainDropDown) + throw 0 - isNameDuplicated = keyspaceTables.some((name) => minifyText(`${name}`) == minifyText(`${countertableName}`)) + row.find(`div[col="partitionKeyName"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 3 : 4) : 5}`) - if (isAlterState && isNameDuplicated && (countertableName == $('div.modal#rightClickActionsMetadata').attr('data-table-name'))) - isNameDuplicated = false + row.find(`div[col="partitionKeyType"]`).removeClass(function(index, className) { + return (className.match(/(^|\s)col-md-\S+/g) || []).join(' ') + }).addClass(`col-md-${isTypeCollection ? (isCollectionMap ? 3 : 4) : 5}`) - if (isNameDuplicated) - invalidFeedback.find('span').attr('mulang', 'provided name is already in use').text(I18next.capitalizeFirstLetter(I18next.t('provided name is already in use'))) - } catch (e) {} + row.find(`div[col="collectionKeyType"]`).toggle(isCollectionMap) - try { - if (`${countertableName}`.length <= 0) - throw 0 + row.find(`div[col="collectionItemType"]`).toggle(isTypeCollection) + } catch (e) {} - isNameInvalid = `${countertableName}`.match(/^(?:[a-zA-Z][a-zA-Z0-9_]*|".+?")$/gm) == null + // Update the input's value + selectElement.val(selectedValue).trigger('input') - if (isNameInvalid) - invalidFeedback.find('span').attr('mulang', 'provided name is invalid, only alphanumeric and underscores are allowed').text(I18next.capitalizeFirstLetter(I18next.t('provided name is invalid, only alphanumeric and underscores are allowed'))) - } catch (e) {} + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + }) + } + }) - $(this).toggleClass('is-invalid', isNameDuplicated || isNameInvalid) + setTimeout(() => { + $(this).find('input[type="text"]').each(function() { + let mdbObject = getElementMDBObject($(this)) - let allDataFields = dialogElement.find('div[action="counter-tables"]').find('div.counter-table-partition-key-field, div.counter-table-clustering-key-field, div.counter-table-column-field, div.counter-table-option-field'), - invalidInputFields = allDataFields.find('input.is-invalid') + setTimeout(() => mdbObject.update(), 500) + }) + }) - dialogElement.find('button.switch-editor').add($('#executeActionStatement')).attr('disabled', $(this).hasClass('is-invalid') || `${keyspaceName}`.length <= 0 || allDataFields.length <= 0 || invalidInputFields.length > 0 ? '' : null) + setTimeout(() => Modules.Localization.applyLanguageSpecific($(this).find('span[mulang], [data-mulang]'))) - try { - updateActionStatusForCounterTables() - } catch (e) {} + try { + updateRowsZIndex() + } catch (e) {} + + if (fields == null) + $(`a[action]#addCounterTableClusteringKey`).removeClass('disabled') + + setTimeout(() => { + try { + updateActionStatusForCounterTables() + } catch (e) {} + }) + })) }) - $(`a[action]#addCounterTablePartitionKey`).on('click', function(_, fields = null) { - let dataFieldsContainer = dialogElement.find('div.counter-table-partition-keys-fields'), - getPartitionKeyFieldElement = (keyspaceUDTs = []) => { + $(`a[action]#addCounterTableClusteringKey`).on('click', function(_, fields = null) { + let dataFieldsContainer = dialogElement.find('div.counter-table-clustering-keys-fields'), + getClusteringKeyFieldElement = (keyspaceUDTs = []) => { let typesList = `
  • int
  • @@ -12313,6 +14342,7 @@
  • timestamp
  • date
  • +
  • duration
  • time
  • blob
  • @@ -12322,10 +14352,10 @@
  • inet
  • `, collectionsTypesItems = ` -
  • -
  • list<type>
  • -
  • set<type>
  • -
  • map<key_type, value_type>
  • ` +
  • +
  • list<type>
  • +
  • set<type>
  • +
  • map<key_type, value_type>
  • ` defaultType = 'text' try { @@ -12341,34 +14371,34 @@ let [ collectionKeyTypeID, collectionItemTypeID, - partitionKeyTypeID + clusteringKeyTypeID ] = getRandomID(10, 3).map((id) => `_${id}`), element = ` -
    +
    -
    +
    - +
    -
    +
    - +
    - -