Skip to content

Commit

Permalink
Beta 1.0 version
Browse files Browse the repository at this point in the history
Many things added including unit_tests
  • Loading branch information
allada committed Oct 10, 2015
1 parent 48b4e0b commit a5ba8b8
Show file tree
Hide file tree
Showing 34 changed files with 2,392 additions and 304 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.komodoproject
104 changes: 104 additions & 0 deletions get_caret_pos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* jshint browser: true */

// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
var properties = [
'direction', // RTL support
'boxSizing',
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
'height',
'overflowX',
'overflowY', // copy the scrollbar for IE

'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth',
'borderStyle',

'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',

// https://developer.mozilla.org/en-US/docs/Web/CSS/font
'fontStyle',
'fontVariant',
'fontWeight',
'fontStretch',
'fontSize',
'fontSizeAdjust',
'lineHeight',
'fontFamily',

'textAlign',
'textTransform',
'textIndent',
'textDecoration', // might not make a difference, but better be safe

'letterSpacing',
'wordSpacing',

'tabSize',
'MozTabSize'

];

var isFirefox = window.mozInnerScreenX != null;

export function getCaretCoordinates(element, position) {
// mirrored div
var div = document.createElement('div');
div.id = 'input-textarea-caret-position-mirror-div';
document.body.appendChild(div);

var style = div.style;
var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9

// default textarea styles
style.whiteSpace = 'nowrap';
if (element.nodeName !== 'INPUT')
style.wordWrap = 'nowrap'; // only for textarea-s

// position off-screen
style.position = 'absolute'; // required to return coordinates properly
style.visibility = 'hidden'; // not 'display: none' because we want rendering

// transfer the element's properties to the div
properties.forEach(function (prop) {
style[prop] = computed[prop];
});

if (isFirefox) {
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if (element.scrollHeight > parseInt(computed.height))
style.overflowY = 'scroll';
} else {
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
}

div.textContent = element.value.substring(0, position);
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if (element.nodeName === 'INPUT')
div.textContent = div.textContent.replace(/\s/g, "\u00a0");

var span = document.createElement('span');
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
div.appendChild(span);

var coordinates = {
top: span.offsetTop + parseInt(computed['borderTopWidth']),
left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
};

document.body.removeChild(div);

return coordinates;
}
129 changes: 105 additions & 24 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,37 @@
font-size:12pt;
text-align:center;
}
body {
zoom: 1.5;
body, html {
padding: 0px;
margin: 0px;
}
html {
zoom: 150%;
}
</style>
</head>
<body>
<div id="suggestion-box" style="position:absolute;top:0px;left:0px;background-color:yellow;border:1px solid black;">&nbsp;</div>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tr>
<th>Query:</th>
<td><input type="text" id="query" style="width:300px;" value="sum(order_total)>500 customer.id!:- customer.name~John" /></td>
<td width="315"><input type="text" id="query" style="width:300px;" value="count(if(product_items_reports.product.brand_id:1054, 1, -)) > 1" /></td>
<td rowspan="6" style="border:1px solid black;height:100%"><table border="0" cellpadding="0" cellspacing="0" style="width:100%;height:100%;">
<tr>
<th>Avail Functions</th>
</tr>
<tr>
<td style="height:230px;"><div style="overflow:auto;height:100%;width:100%;"><table id="function-list" style="white-space:nowrap;"></table></div></td>
</tr>
</table></td>
</tr>
<tr>
<th>Table:</th>
<td><select id="table"></select></td>
</tr>
<tr>
<th>Group By:</th>
<td><input type="text" id="group_by" style="width:300px;" /></td>
<td><input type="text" id="group_by" style="width:300px;" value="id" /></td>
</tr>
<tr>
<th>Order Bys:</th>
Expand All @@ -45,10 +58,10 @@
<td style="width:10%;"><input type="button" value="+" onclick="addOrderBy()" /></td>
</tr>
<tr>
<th><input type="text" name="order_values[]" style="width:95%;" value="customer.name" /></th>
<th><input type="text" name="order_values[]" style="width:95%;" value="sum(product_items_reports.sell_price)" /></th>
<th colspan="2"><select name="order_dirs[]" style="width:95%;">
<option value="asc">ASC</option>
<option value="desc">DESC</option>
<option value="desc" selected="selected">DESC</option>
</select></th>
</tr>
</table></td>
Expand All @@ -69,35 +82,37 @@
<th colspan="2"><input type="text" name="select_values[]" style="width:95%;" value="id" /></th>
</tr>
<tr>
<th><input type="text" name="select_names[]" style="width:95%;" value="ship_info" /></th>
<th colspan="2"><input type="text" name="select_values[]" style="width:95%;" value="concat(customer.name, '\n', customer.address1, if(customer.address2, concat('\n', customer.address2)), '\n', customer.city, ' ', customer.state, ' ', customer.postalcode)" /></th>
<th><input type="text" name="select_names[]" style="width:95%;" value="order_total" /></th>
<th colspan="2"><input type="text" name="select_values[]" style="width:95%;" value="sum(product_items_reports.sell_price)" /></th>
</tr>
<tr>
<th><input type="text" name="select_names[]" style="width:95%;" value="total" /></th>
<th colspan="2"><input type="text" name="select_values[]" style="width:95%;" value="sum(order_total)" /></th>
<th><input type="text" name="select_names[]" style="width:95%;" value="total_item_qty_on_order" /></th>
<th colspan="2"><input type="text" name="select_values[]" style="width:95%;" value="count(id)" /></th>
</tr>
</table></td>
</tr>
<tr>
<td colspan="2"><hr /></td>
<td colspan="3"><hr /></td>
</tr>
<tr>
<th style="height:200px;">Output:</th>
<td><div id="output_query" style="width:80%;height:300px;border:1px solid black;white-space:pre;font-size:8pt;font-family:monospace;overflow:auto;"></div></td>
<td colspan="2"><div id="output_query" style="width:80%;height:300px;border:1px solid black;white-space:pre;font-size:8pt;font-family:monospace;overflow:auto;"></div></td>
</tr>
</table>
<!-- <textarea id="psudoql" style="width:100%;height:300px"></textarea>
<textarea id="output" style="width:100%;height:300px"></textarea> -->
<script type="module">
import { PQL } from './pql/PQL.js';
import { PARSER } from './pql/parser.js';
import { Config } from './pql/config.js';
import { getCaretCoordinates } from './get_caret_pos.js';

(() => {
class EmptyString {
toString () {
return '';
}
}
let output_query_el, query_el, table_el, group_by_el, select_names, select_values, order_dirs, order_values;
let suggestion_box = document.getElementById('suggestion-box');

function refreshMappings () {
output_query_el = document.getElementById('output_query');
Expand All @@ -113,15 +128,20 @@
order_values = document.getElementsByName('order_values[]');

PQL.setDefaultConfig(Config);
while (table_el.firstChild) {
table_el.removeChild(table_el.firstChild);
}
for (let i in Config.DB_MAP) {
if (Config.DB_MAP.hasOwnProperty(i)) {
let option = document.createElement('option');
option.value = i;
option.appendChild(document.createTextNode(i));
table_el.appendChild(option);
if (!table_el.firstChild) {
while (table_el.firstChild) {
table_el.removeChild(table_el.firstChild);
}
for (let i in Config.DB_MAP) {
if (Config.DB_MAP.hasOwnProperty(i)) {
let option = document.createElement('option');
option.value = i;
if (i === 'order') {
option.selected = "selected";
}
option.appendChild(document.createTextNode(i));
table_el.appendChild(option);
}
}
}

Expand Down Expand Up @@ -152,6 +172,44 @@
v.addEventListener('input', runQuery);
});
runQuery();

let function_list_el = document.getElementById('function-list');
while (function_list_el.firstChild) {
function_list_el.removeChild(function_list_el.firstChild);
}
for (let i in Config.FUNCTION_MAP) {
if (Config.FUNCTION_MAP.hasOwnProperty(i)) {
let tr = function_list_el.appendChild(document.createElement('tr'));
let td = tr.appendChild(document.createElement('td'));
let required_args = [];
let arg_count = 1;
for (let j = 0; j < Config.FUNCTION_MAP[i].min_args; j++, arg_count++) {
required_args.push('arg'+arg_count.toString());
}
let option_args = [];
if (Config.FUNCTION_MAP[i].max_args === Infinity) {
option_args.push('arg' + arg_count);
arg_count++;
option_args.push('...');
} else {
for (let j = 0; j < Config.FUNCTION_MAP[i].max_args; j++, arg_count++) {
option_args.push('arg'+arg_count.toString());
}
}
if (option_args) {
if (required_args.length) {
option_args = `[,${ option_args.join(',') }]`;
} else {
option_args = `[${ option_args.join(',') }]`;
}
} else {
option_args = '';
}
td.appendChild(document.createTextNode(i + `(${ required_args.join(',') }${ option_args })`));
let td2 = tr.appendChild(document.createElement('td'));
td2.appendChild(document.createTextNode(Config.FUNCTION_MAP[i].description))
}
}
}
let runQuery = function () {
try {
Expand All @@ -170,6 +228,10 @@
group: group_by_el.value,
selects: fields,
orderBys: orders,
variables: {
id: 5,
name: "John",
}
});
while (output_query_el.firstChild) {
output_query_el.removeChild(output_query_el.firstChild);
Expand All @@ -194,8 +256,8 @@
} else {
output_query_el.appendChild(document.createTextNode(e.message || e));
}
return;
}
getSuggestion();
};
window.addSelects = () => {
var select_table = document.getElementById('select_table');
Expand Down Expand Up @@ -249,6 +311,25 @@

refreshMappings();
};
function getSuggestion () {
return;
let el = document.activeElement;
if (el && document.activeElement.tagName == 'INPUT') {
let str = el.value.substr(0, el.selectionStart);
let {top, left} = getCaretCoordinates(el, el.selectionStart);
top += el.getBoundingClientRect().top - document.body.getBoundingClientRect().top;
left += el.getBoundingClientRect().left - document.body.getBoundingClientRect().left;
suggestion_box.style.top = (top + 15).toString() + 'px';
suggestion_box.style.left = left.toString() + 'px';
try {
let parser = new PARSER(str, table_el.value, false, Config);
suggestion_box.innerHTML = (parser.getError() || '') + '<br />' + parser.getSuggestions().join(',');
} catch (e) {
suggestion_box.innerHTML = e.message || e;
}

}
}
})();
window.addSelects();
</script>
Expand Down
21 changes: 10 additions & 11 deletions pql/PQL.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,24 @@ export class PQL {
PQL._defaultConfig = v;
}

static getSQL ({ query, table, group, selects, orderBys }) {
var query_parser = new PARSER(query, table, false, this.defaultConfig);
static getSQL ({ query, table, group, selects, orderBys, variables }) {
var query_parser = new PARSER(query, table, false, this.defaultConfig, [], variables);
if (query_parser.hasError()) {
throw query_parser.getError();
}

var group_parser = new PARSER(group, table, true, this.defaultConfig);
if (group === undefined) {
group = 'id';
}
var group_parser = new PARSER(group, table, true, this.defaultConfig, [], variables);
if (group_parser.hasError()) {
throw group_parser.getError();
}

let select_parsers = new Map();
if (selects instanceof Map) {
selects.forEach((v, k) => {
let val = new PARSER(v, table, false, this.defaultConfig);
let val = new PARSER(v, table, false, this.defaultConfig, [], variables);
if (val.hasError()) {
throw val.getError();
}
Expand All @@ -32,7 +35,7 @@ export class PQL {
} else {
for (let k in selects) {
if (selects.hasOwnProperty(k)) {
let v = new PARSER(selects[k], table, false, this.defaultConfig);
let v = new PARSER(selects[k], table, false, this.defaultConfig, [], variables);
if (v.hasError()) {
throw v.getError();
}
Expand All @@ -41,15 +44,11 @@ export class PQL {
}
}

if (!select_parsers.size) {
throw "Must have at least 1 select";
}

let order_by_parsers = new Map();
if (orderBys instanceof Map) {
orderBys.forEach((v, k) => {
// This one is backwards... be warned that k is the string v is the [desc, asc]
let val = new PARSER(k, table, false, this.defaultConfig);
let val = new PARSER(k, table, false, this.defaultConfig, [], variables);
if (val.hasError()) {
throw val.getError();
}
Expand All @@ -58,7 +57,7 @@ export class PQL {
} else {
for (let k in orderBys) {
if (orderBys.hasOwnProperty(k)) {
let v = new PARSER(k, table, false, this.defaultConfig);
let v = new PARSER(k, table, false, this.defaultConfig, [], variables);
if (v.hasError()) {
throw v.getError();
}
Expand Down
Loading

0 comments on commit a5ba8b8

Please sign in to comment.