CodeMirror: how to allow double quote in SQL? - codemirror

I'm using codeMirror for SQL editor and have a requirement of using double quote for string variable, how can I achieve that? I used this code learning from this post:
CodeMirror.defineMode('allowDoubleQuote', () => ({
startState: () => {
return { inString: false };
},
token: (stream: StringStream, state) => {
//If a string starts here
if (!state.inString && stream.peek() === '"') {
stream.next(); // Skip quote
state.inString = true; // Update state
}
if (state.inString) {
if (stream.skipTo('"')) {
// Quote found on this line
stream.next(); // Skip quote
state.inString = false; // Clear flag
} else {
stream.skipToEnd(); // Rest of line is string
}
return 'string'; // Token style
} else {
stream.skipTo('"') || stream.skipToEnd();
return null; // Unstyled token
}
},
}));
const editor = CodeMirror.fromTextArea(xxxx, {
mode: 'allowDoubleQuote',
...
})
The state inString is true for all the text inside the double quote and false for others, however, when I run editor.getLineTokens, the texts inside the double quote are still cut into pieces and identified as keyword.
What’s wrong, or, what’s a better approach to achieve that?

Related

How to customise the ghost-text (drag item name) with AgGrid

As per title, is there a way to customise the "ghost text" with the (unmanaged) row-dragging implementation in AgGrid via the API?
I found workaround using valueGetter property to change this text.
Example configuration of column:
private dragDropColumn= {
rowDrag: true,
...
valueGetter: (params) => {
return params.data.myVariableFromRow;
}
}
Hope this helps
You can now use colDef.rowDragText to set a callback function to set the dragged text.
https://www.ag-grid.com/javascript-grid-row-dragging/#custom-row-drag-text
onRowDragEnter event,
you could search for the ghost element
and then append your custom class to it.
document.querySelectorAll('.ag-dnd-ghost')[0]
.classlist.add('my-custom-ghost-element');
Dont forget to follow the hierarchy of classes, otherwise you would end up using !important in the custom class :-)
// For updating text
search for the element with className ag-dnd-ghost-label
document.querySelectorAll('.ag-dnd-ghost-label')[0]
.innerHTML = 'your_custom_text';
Ghost element is added only during drag, once drag is ended, its removed from dom by Ag-Grid.
Hope this helps
I've researched this and there is nothing built in to ag-grid. I accomplished this by attaching to the onRowDragMove and onRowDragMove events.
I set a class variable 'isRowDragging' to only do this once while dragging.
I use the Angular Renderer2 class (this.ui) to find and update the Element of the ghost label.
All of this is available with vanilla javascript or other supported ag-grid frameworks.
this.gridOptions.onRowDragMove = (params: RowDragMoveEvent) => {
const overNode = params.overNode;
const movingNode = params.node;
if (!this.isRowDragging) {
this.isRowDragging = true;
if (!movingNode.isSelected()) {
if (params.event && params.event.ctrlKey) {
movingNode.setSelected(true);
}
if (params.event && !params.event.ctrlKey) {
movingNode.setSelected(true, true);
}
}
let labelText: string = '';
const selectedNodes = this.gridOptions.api.getSelectedNodes();
if (selectedNodes.length === 1) {
labelText = selectedNodes[0].data.Name;
}
else {
const guids: string[] = [];
let folderCount: number = 0;
let docCount: number = 0;
selectedNodes.forEach((node: RowNode) => {
guids.push((node.data.FolderGuid || node.data.DocumentGuid).toLowerCase());
if (node.data.FolderGuid !== undefined) {
folderCount++;
}
else {
docCount++;
}
});
if (folderCount === 1) {
labelText = '1 folder';
}
else if (folderCount > 1) {
labelText = folderCount + ' folders';
}
if (docCount === 1) {
labelText += (labelText !== '' ? ', ' : '') + '1 document';
}
else if (docCount > 1) {
labelText += (labelText !== '' ? ', ' : '') + docCount + ' documents';
}
}
console.log(this.ui.find(document.body, '.ag-dnd-ghost').outerHTML);
this.ui.find(document.body, '.ag-dnd-ghost-label').innerHTML = labelText;
}
}
this.gridOptions.onRowDragEnd = (event: RowDragEndEvent) => {
this.isRowDragging = false;
}
I am using ag-grid with react and using cellRendererFramework property of ColDef to use custom react component, my value getter returns object, so when I start dragging I am getting [object object], I added toString method to returning object and did the trick, below is my sample code
const col1 = {
rowDrag: true,
...
valueGetter: (params) => {
const {id, name} = params.data;
return {id, name, toString: () => name}; // so when dragging it will display name property
}
}

How can I customize the 'home' and 'end' key navigation on ag-Grid?

I've customized the arrow navigation keys using the callback 'navigateToNextCell'.
But now I want to customize 'Home' and 'End' key.
There is any way to do it?
It's not super easy at the moment... here are some relevant "enhancements" that have been requested on the github page:
support page up/ down, home and end keys
allow overriding of keyboard events
Suggestion: Allow any key for navigation (not just tab)
That last link has something useful. There is a comment on how to disable any propagation of the 'home' and 'end' keys:
// note, this is angular 2 code, `this.el.nativeElement` is just the grid component
document.documentElement.addEventListener(
'keydown',
(e: KeyboardEvent) => {
// this runs on the document element, so check that we're in the grid
if (this.el.nativeElement.contains(e.target)) {
if (e.key === 'Tab' || e.key === 'Home' || e.key === 'End') {
e.stopImmediatePropagation();
e.stopPropagation();
}
if (e.key === 'Home' || e.key === 'End') {
// we don't want to prevent the default tab behaviour
e.preventDefault();
}
}
},
true
);
AFTER you have done that, you could add new event listeners to the grid, or depending on what you are trying to do, you could add listeners to specific cells as it mentions in the Keyboard Navigation section of the Ag-Grid docs under Custom Actions:
Custom Actions
Custom cell renderers can listen to key presses on the focused div.
The grid element that receives the focus is provided to the cell
renderers via the eGridCell parameter. You can add your own listeners
to this cell. Via this method you can listen to any key press and do
your own action on the cell eg hitting 'x' may execute a command in
your application for that cell.
I initially wrote this code for navigating home/end with command+arrow key, but have put comments where logic differs since 98% will be the same. Comments are untested but should work
I will divide this into 4 parts:
suppress the event so ag-grid does nothing by default
find a way for us to place a event listener on the right keys
compute which cell should be navigated to next
tell ag grid which cell should be selected
For #1, we can use suppressKeyboardEvent and either set it on defaultColDef(in `GridOptions´) or on every col def.
Mine looks like this:
const suppressKeyboardEventFn: ColDef["suppressKeyboardEvent"] = (
params: SuppressKeyboardEventParams
) => {
const key = params.event.key;
const isControl = isControlKey(params.event);
// For end/home, test for key === "Home" || key === "End" instead
const isNavigation =
key === "ArrowUp" ||
key === "ArrowDown" ||
key === "ArrowRight" ||
key === "ArrowLeft";
if (isNavigation && isControl) {
return true;
}
return false;
};
isControlKey is so it works on both mac&windows, and is written as recommended by MDN (not relevant if using home/end key):
const isMac = navigator.platform.startsWith("Mac");
export const isControlKey = (event: KeyboardEvent) => {
return isMac ? event.metaKey : event.ctrlKey;
};
For #2 where we want to handle the event ourselves, we can use onCellKeyDown:
const onCellKeyDownFn: GridOptions["onCellKeyDown"] = (cellKeyDown) => {
if (cellKeyDown.event == null) {
return;
}
const event = cellKeyDown.event as KeyboardEvent;
const key = event.key;
const isControl = isControlKey(event);
// Instead write cases here for case "Home": and case "End":
switch (key) {
case "ArrowUp": {
if (isControl) {
dispatch(navigateCell({ direction: "up", end: true }));
}
break;
}
case "ArrowDown": {
if (isControl) {
dispatch(navigateCell({ direction: "down", end: true }));
}
break;
}
case "ArrowRight": {
if (isControl) {
dispatch(navigateCell({ direction: "right", end: true }));
}
break;
}
case "ArrowLeft": {
if (isControl) {
dispatch(navigateCell({ direction: "left", end: true }));
}
break;
}
}
};
Using the same isControlKey function. I have implemented redux navigation actions that handles navigating to home and end.
For #3, we can implement this in different ways. Here is how I FIND the correct cell to navigate to. Either one step to a direction, or to the end/home (which is our scenario, since I pass end: true)
// For using Home/End, add a case for each and mix logic from
// below to find the correct cell. Last column logic is in
// right when end===true and last row is in down when end===true etc
switch (params.direction) {
case "up": {
let newRowIndex;
if (params.end === true) {
newRowIndex = 0;
} else {
newRowIndex = selectedCell.lastKnownRowIndex - 1;
if (newRowIndex < 0) {
return;
}
}
const node = gridApi.getDisplayedRowAtIndex(newRowIndex);
if (node?.id == null) {
log.warn("Could not find Node ID");
return;
}
dispatch(
selectCell({
nodeId: node.id,
colKey: selectedCell.colKey,
})
);
break;
}
case "down": {
let newRowIndex;
if (params.end === true) {
newRowIndex = maxRowIndex;
} else {
newRowIndex = selectedCell.lastKnownRowIndex + 1;
if (newRowIndex > maxRowIndex) {
return;
}
}
const node = gridApi.getDisplayedRowAtIndex(newRowIndex);
if (node?.id == null) {
log.warn("Could not find Node ID");
return;
}
dispatch(
selectCell({
nodeId: node.id,
colKey: selectedCell.colKey,
})
);
break;
}
case "right": {
let nextColumn;
if (params.end === true) {
const columns = columnApi.getAllGridColumns();
nextColumn = columns[columns.length - 1];
} else {
const column = columnApi.getColumn(selectedCell.colKey);
if (column == null) {
log.warn("Could not find column");
return;
}
nextColumn = columnApi.getDisplayedColAfter(column);
}
if (nextColumn == null) {
return;
}
dispatch(
selectCell({
nodeId: selectedCell.nodeId,
colKey: nextColumn.getColId(),
})
);
break;
}
case "left": {
let nextColumn;
if (params.end === true) {
const columns = columnApi.getAllGridColumns();
nextColumn = columns[1];
} else {
const column = columnApi.getColumn(selectedCell.colKey);
if (column == null) {
log.warn("Could not find column");
return;
}
nextColumn = columnApi.getDisplayedColBefore(column);
}
if (nextColumn == null) {
return;
}
dispatch(
selectCell({
nodeId: selectedCell.nodeId,
colKey: nextColumn.getColId(),
})
);
break;
}
default: {
assertNever(params.direction);
}
}
Lastly #4, here is parts of my selectCell implementation that can give you some idea of how to make sure ag-grid focuses the right cell.
This code will focus the cell, add a range highlight, add a row selection, and make sure that it is visible if the viewport needs to jump:
// no difference in logic when using Home/End!
// just make sure correct cell is passed
gridApi.ensureNodeVisible(node);
gridApi.ensureColumnVisible(params.colKey);
gridApi.clearRangeSelection();
gridApi.addCellRange({
rowStartIndex: rowIndex,
rowEndIndex: rowIndex,
columns: [params.colKey],
});
gridApi.setFocusedCell(rowIndex, params.colKey);
node.setSelected(true, true)

Web speech api recognition word in sentence

I try with my programm to recognise a specific word when I speek a sentence, it's use Web speech API.
When I speek the word alone, my programm work fine, but when I speek it in a sentence, my programm doesn't run.
Thank's to tell me where is the problem with my programm.
<script>
(function($)
{
var $btn = $('#btn');
var $result = $('#result');
var words = null;
if ('webkitSpeechRecognition' in window)
{
var recognition = new webkitSpeechRecognition();
recognition.lang = "fr-FR";
recognition.continuous = false;
recognition.interimResults = true;
$btn.click(function(e)
{
e.preventDefault();
$btn.removeClass('btn-primary');
recognition.start();
});
recognition.onresult = function (event)
{
$result.text('');
for (var i = event.resultIndex; i < event.results.length; i++)
{
var transcript = event.results[i][0].transcript;
if (event.results[i].isFinal)
{
$result.text(transcript);
recognition.stop();
$btn.addClass('btn-primary');
words = transcript.split(' ');
if(words[0] == 'test')
{
// do something
}
return true;
}
else
{
$result.text($('#result').text() + event.results[i][0].transcript);
}
}
};
}
else{$btn.hide();}
})(jQuery);
</script>
When you do words = transcript.split(' ') you make words an array of all of the recognized words. Then when you do if(words[0] == 'test') you are only checking the first word, so it will only work if the word you are trying to recognize is the first (or only) word.
Instead of this:
if(words[0] == 'test')
{
// do something
}
Try this:
words.some(function (word) {
if (word === 'test') {
// do something
return true;
}
});

How to check if text is found in column in Protractor

I'm trying to assert that a name is displayed in a column of a table. I've written an inResults function that will iterate through a column's text to see if a name exists. Here's what I'm trying:
Page object:
this.names = element.all(by.repeater('row in rows').column('{{row}}'));
this.inResults = function(nameString) {
var foundit = '';
this.names.each(function(name) {
name.getText().then(function(it) {
console.log(it); // each name IS printed...
if(it == nameString) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
});
});
return foundit; // returns '' but should be true?
};
Spec expect:
expect(friendPage.inResults('Jo')).toBeTruthy();
Both console statements print as expected... but my expect fails as foundit's value is still ''. I've tried this a number of ways and none are working. What am I missing?
I've devised what I think is a better/cleaner way to solve this. It's less complex and doesn't require locator/css code in the method.
friend.page.js
// locator
this.friendName = function(text) { return element.all(by.cssContainingText('td.ng-binding', text)) };
// method
this.inResults = function(name) {
return this.friendName(name).then(function(found) {
return found.length > 0;
});
};
friend.spec.js
expect(friendPage.inResults('Jo')).toBeTruthy();
I've added this to my protractor_example project on GitHub...
I would recommend you to use filter: http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.filter
this.inResults = function(nameString) {
return this.names.filter(function(name) {
return name.getText().then(function(text) {
return text === nameString;
});
}).then(function(filteredElements) {
// Only the elements that passed the filter will be here. This is an array.
return filteredElements.length > 0;
});
});
// This will be a promise that resolves to a boolean.
expect(friendPage.inResults('Jo')).toBe(true);
Use map to do this.This will return a deferred that will resolve with the values in an array, so if you have this:
this.mappedVals =element.all(by.repeater('row in rows').column('{{row}}')).map(function (elm) {
return elm.getText();
});
It will resolve like this:
this.inResults = function(nameString) {
var foundit = '';
mappedVals.then(function (textArr) {
// textArr will be an actual JS array of the text from each node in your repeater
for(var i=0; i<textArr.length; i++){
if(it == textArr[i]) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
}
return foundit;
});
}
And Use that in Spec file like,
friendPage.inResults('Jo').then(function(findIt){
expect(findIt).toBeTruthy();
});

Do not submit empty form fields in ExtJS

I have an extjs form with fields. The user isn't required to enter data into each field so I do not want to submit the fields with no data. I want it to post only fields that actually have data. Is this possible?
I recommend using form's beforeaction event. While handling this event you can check all fields. If all values are empty just return false;. The following example works in ExtJS4 and has to work in ExtJS3:
myform.on('beforeaction', function(form, action) {
if (action.type == 'submit') {
var doSubmit = false, vals = form.getValues();
for (var i in vals)
if (vals[i] !== '') {
doSubmit = true;
break;
}
return doSubmit;
}
});
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).
Another options is to override component's getSubmitValue() method and return null if this field is empty, this way it won't be included into submit fields.
{
xtype: 'combo',
getSubmitValue: function(){
var value = this.getValue();
if(Ext.isEmpty(value)) {
return null;
}
return value;
}
}
Instead of using form's submit, directly call Ext.Ajax.request(...) with the url, method type (GET/POST) and params (and any other options as explained in the call documentation).
To generate params, iterate over the form fields and check for null value before adding to params.
This bug is present in ExtJS 4.0.7 too.
As Molecule Man pointed:
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).
A possible solution to fix this bug is by overriding 2 functions, getValues in "Ext.form.Basic" (where the bug is) and createForm (to create our basic form) in "Ext.form.Panel" by extension in the following way:
Ext.define("My.form.Basic", {
alias: "form.mybasic",
extend: "Ext.form.Basic",
getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
var values = {};
this.getFields().each(function(field) {
if (!dirtyOnly || field.isDirty()) {
var data = field[useDataValues ? "getModelData" : "getSubmitData"](includeEmptyText);
if (Ext.isObject(data)) {
var isArray = Ext.isArray;
Ext.iterate(data, function(name, val) {
if (includeEmptyText && val === "") {
val = field.emptyText || "";
}
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0))) {
if (name in values) {
var bucket = values[name];
if (!isArray(bucket)) {
bucket = values[name] = [bucket];
}
if (isArray(val)) {
values[name] = bucket.concat(val);
}
else {
bucket.push(val);
}
}
else {
values[name] = val;
}
}
});
}
}
});
if (asString) {
values = Ext.Object.toQueryString(values);
}
return values;
}
});
Ext.define("My.form.Panel", {
alias: "form.mypanel",
extend: "Ext.form.Panel",
createForm: function() {
return Ext.create("My.form.Basic", this, Ext.applyIf({listeners: {}}, this.initialConfig));
}
});
The code is copied from the ext source code. The only change is inside the iteration of each field: introduced the following wrapping "if":
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0)))
I am a bit late but, better later than never...