rsel-exprparser-basic

Parses RSel-specific expression text and rebuilds it in the UI.

As of 2016-03-05. See the latest version.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/17641/111510/rsel-exprparser-basic.js

  1. // ==UserScript==
  2. // @name rsel-exprparser-basic
  3. // @namespace https://greatest.deepsurf.us/users/11629-TheLastTaterTot
  4. // @version 0.2.6
  5. // @description Parses RSel-specific expression text and rebuilds it in the UI.
  6. // @author TheLastTaterTot
  7. // @include https://editor-beta.waze.com/*editor/*
  8. // @include https://www.waze.com/*editor/*
  9. // @exclude https://www.waze.com/*user/editor/*
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. // Main usage: RSelExprParser.updateExpression(<rsel expression text>)
  15.  
  16. var RSelExprParser = {
  17. new__EXPR_DEBUGINFO: function(m, exprWord, exprPhrase) {
  18. return {
  19. m: m,
  20. exprMatches: exprWord,
  21. exprMatchPhrases: exprPhrase,
  22. exprBuild: {},
  23. err: null ,
  24. errorMsg: null
  25. };
  26. },
  27. //reset for error debugging
  28. _rsel: {
  29. getSelectionIndex: function(selector, selText) {
  30. return selector.map(function(i) {
  31. if (new RegExp(selText,'i').test(this.innerText))
  32. return this.value
  33. }).get(0);
  34. },
  35. getSelectOptions: function(selector) {
  36. var opts = [];
  37. selector.map(function(i, a) {
  38. opts.push(a.innerText.toLowerCase());
  39. });
  40. return opts;
  41. },
  42. getNewExprBuild: function() {
  43. return {
  44. cond: null ,
  45. op: null ,
  46. op2: null ,
  47. val: null ,
  48. val2: null ,
  49. condmod: null ,
  50. errorCode: 0
  51.  
  52. }
  53. }
  54. },
  55. /*Using RSel DOM elements rather than requesting dev to provide direct modifiction of RSel's expr object.
  56. This is so the RSel dev can feel free to significantly change his object storage structure if needed. */
  57. rselBtns: {
  58. lfParens: function() {
  59. try {
  60. $('#btnRSLBkt').click();
  61. } catch (err) {}
  62. },
  63. rtParens: function() {
  64. try {
  65. $('#btnRSRBkt').click();
  66. } catch (err) {}
  67. },
  68. and: function() {
  69. try {
  70. $('#btnRSAnd').click()
  71. } catch (err) {}
  72. },
  73. or: function() {
  74. try {
  75. $('#btnRSOr').click()
  76. } catch (err) {}
  77. },
  78. not: function() {
  79. try {
  80. $('#btnRSNot').click()
  81. } catch (err) {}
  82. },
  83. clear: function() {
  84. try {
  85. $('#btnRSClear').click()
  86. } catch (err) {}
  87. }
  88. },
  89. rselCond: {
  90. country: {
  91. op: function(selText) {
  92. $('#opRSCountry').val(RSelExprParser._rsel.getSelectionIndex($('#opRSCountry option'), selText));
  93. },
  94. val: function(selText) {
  95. $('#selRSCountry').val(RSelExprParser._rsel.getSelectionIndex($('#selRSCountry option'), selText));
  96. },
  97. add: function() {
  98. $('#btnRSAddCountry').click();
  99. }
  100. },
  101. state: {
  102. op: function(selText) {
  103. $('#opRSState').val(RSelExprParser._rsel.getSelectionIndex($('#opRSState option'), selText));
  104. },
  105. val: function(val) {
  106. $('#inRSState').val(val);
  107. },
  108. add: function() {
  109. $('#btnRSAddState').click();
  110. }
  111. },
  112. city: {
  113. op: function(selText) {
  114. $('#opRSCity').val(RSelExprParser._rsel.getSelectionIndex($('#opRSCity option'), selText));
  115. },
  116. val: function(val) {
  117. $('#inRSCity').val(val);
  118. },
  119. condmod: function(val) {
  120. $('#selRSAltCity').val(val);
  121. },
  122. add: function() {
  123. $('#btnRSAddCity').click();
  124. }
  125. },
  126. street: {
  127. op: function(selText) {
  128. $('#opRSStreet').val(RSelExprParser._rsel.getSelectionIndex($('#opRSStreet option'), selText));
  129. },
  130. val: function(val) {
  131. $('#inRSStreet').val(val);
  132. },
  133. condmod: function(val) {
  134. $('#selRSAlttStreet').val(val);
  135. },
  136. add: function() {
  137. $('#btnRSAddStreet').click();
  138. }
  139. },
  140. unnamed: {
  141. op: function(chkVal) {
  142. $('#cbRSNoName').prop('checked', chkVal);
  143. },
  144. //checked - has no name
  145. op2: function(chkVal) {
  146. $('#cbRSAltNoName').prop('checked', chkVal);
  147. },
  148. //checked - alt name
  149. add: function() {
  150. $('#btnRSAddNoName').click();
  151. }
  152. },
  153. road: {
  154. op: function(selText) {
  155. $('#opRSRoadType').val(RSelExprParser._rsel.getSelectionIndex($('#opRSRoadType option'), selText));
  156. },
  157. val: function(selText) {
  158. $('#selRSRoadType').val(RSelExprParser._rsel.getSelectionIndex($('#selRSRoadType option'), selText));
  159. },
  160. add: function() {
  161. $('#btnRSAddRoadType').click();
  162. }
  163. },
  164. direction: {
  165. op: function(selText) {
  166. $('#opRSDirection').val(RSelExprParser._rsel.getSelectionIndex($('#opRSDirection option'), selText));
  167. },
  168. val: function(selText) {
  169. $('#selRSDirection').val(RSelExprParser._rsel.getSelectionIndex($('#selRSDirection option'), selText));
  170. },
  171. add: function() {
  172. $('#btnRSAddDirection').click();
  173. }
  174. },
  175. elevation: {
  176. op: function(selText) {
  177. $('#opRSElevation').val(RSelExprParser._rsel.getSelectionIndex($('#opRSElevation option'), selText));
  178. },
  179. val: function(selText) {
  180. $('#selRSElevation').val(RSelExprParser._rsel.getSelectionIndex($('#selRSElevation option'), selText));
  181. },
  182. add: function() {
  183. $('#btnRSAddElevation').click();
  184. }
  185. },
  186. manlock: {
  187. op: function(selText) {
  188. $('#opRSManLock').val(RSelExprParser._rsel.getSelectionIndex($('#opRSManLock option'), selText));
  189. },
  190. val: function(val) {
  191. $('#selRSManLock').val(val);
  192. },
  193. add: function() {
  194. $('#btnRSAddManLock').click();
  195. }
  196. },
  197. traflock: {
  198. op: function(selText) {
  199. $('#opRSTrLock').val(RSelExprParser._rsel.getSelectionIndex($('#opRSTrLock option'), selText));
  200. },
  201. val: function(val) {
  202. $('#selRSTrLock').val(val);
  203. },
  204. add: function() {
  205. $('#btnRSAddTrLock').click();
  206. }
  207. },
  208. speed: {
  209. opOptNodes: $('#opRSSpeed option'),
  210. op: function(selText) {
  211. $('#opRSSpeed').val(RSelExprParser._rsel.getSelectionIndex($('#opRSSpeed option'), selText));
  212. },
  213. val: function(val) {
  214. $('#inRSSpeed').val(val);
  215. },
  216. add: function() {
  217. $('#btnRSAddSpeed').click();
  218. }
  219. },
  220. closure: {
  221. op: function(checked) {
  222. $('#cbRSClsr').prop('checked', checked);
  223. },
  224. op2: function(selText) {
  225. $('#opRSClsrStrtEnd').val(RSelExprParser._rsel.getSelectionIndex($('#opRSClsrStrtEnd option'), selText));
  226. },
  227. val: function(val) {
  228. $('#inRSClsrDays').val(val);
  229. },
  230. condmod: function(selText) {
  231. $('#opRSClsrBeforeAter').val(RSelExprParser._rsel.getSelectionIndex($('#opRSClsrBeforeAter option'), selText));
  232. },
  233. add: function() {
  234. $('#btnRSAddClsr').click();
  235. }
  236. },
  237. updatedby: {
  238. op: function(selText) {
  239. $('#opRSUpdtd').val(RSelExprParser._rsel.getSelectionIndex($('#opRSUpdtd option'), selText));
  240. },
  241. val: function(val) {
  242. $('#inRSUpdtd').val(val);
  243. },
  244. add: function() {
  245. $('#btnRSAddUpdtd').click();
  246. }
  247. },
  248. createdby: {
  249. op: function(selText) {
  250. $('#opRSCrtd').val(RSelExprParser._rsel.getSelectionIndex($('#opRSCrtd option'), selText));
  251. },
  252. val: function(val) {
  253. $('#inRSCrtd').val(val);
  254. },
  255. add: function() {
  256. $('#btnRSAddCrtd').click();
  257. }
  258. },
  259. last: {
  260. op: function(selText) {
  261. $('#opRSLastU').val(RSelExprParser._rsel.getSelectionIndex($('#opRSLastU option'), selText));
  262. },
  263. val: function(val) {
  264. $('#inRSLastU').val(val);
  265. },
  266. add: function() {
  267. $('#btnRSAddLastU').click();
  268. }
  269. },
  270. length: {
  271. op: function(selText) {
  272. $('#opRSLength').val(RSelExprParser._rsel.getSelectionIndex($('#opRSLength option'), selText));
  273. },
  274. val: function(val) {
  275. $('#inRSLength').val(val);
  276. },
  277. condmod: function(selText) {
  278. $('#unitRSLength').val(RSelExprParser._rsel.getSelectionIndex($('#unitRSLength option'), selText));
  279. },
  280. add: function() {
  281. $('#btnRSAddLength').click();
  282. }
  283. },
  284. id: {
  285. op: function(selText) {
  286. $('#opRSSegId').val(RSelExprParser._rsel.getSelectionIndex($('#opRSSegId option'), selText));
  287. },
  288. val: function(val) {
  289. $('#inRSSegId').val(val);
  290. },
  291. add: function() {
  292. $('#btnRSAddSegId').click();
  293. }
  294. },
  295. roundabout: {
  296. op: function(checked) {
  297. $('#cbRSIsRound').prop('checked', checked);
  298. },
  299. add: function() {
  300. $('#btnRSAddIsRound').click();
  301. }
  302. },
  303. toll: {
  304. op: function(checked) {
  305. $('#cbRSIsToll').prop('checked', checked);
  306. },
  307. add: function() {
  308. $('#btnRSAddIsToll').click();
  309. }
  310. },
  311. tunnel: {
  312. op: function(checked) {
  313. $('#cbRSTunnel').prop('checked', checked);
  314. },
  315. add: function() {
  316. $('#btnRSAddTunnel').click();
  317. }
  318. },
  319. new: {
  320. op: function(checked) {
  321. $('#cbRSIsNew').prop('checked', checked);
  322. },
  323. add: function() {
  324. $('#btnRSAddIsNew').click();
  325. }
  326. },
  327. changed: {
  328. op: function(checked) {
  329. $('#cbRSIsChngd').prop('checked', checked);
  330. },
  331. add: function() {
  332. $('#btnRSAddIsChngd').click();
  333. }
  334. },
  335. screen: {
  336. op: function(checked) {
  337. $('#cbRSOnScr').prop('checked', checked);
  338. },
  339. add: function() {
  340. $('#btnRSAddOnScr').click();
  341. }
  342. },
  343. restriction: {
  344. op: function(checked) {
  345. $('#cbRSRestr').prop('checked', checked);
  346. },
  347. add: function() {
  348. $('#btnRSAddRestr').click();
  349. }
  350. },
  351. editable: {
  352. op: function(checked) {
  353. $('#cbRSEdtbl').prop('checked', checked);
  354. },
  355. add: function() {
  356. $('#btnRSAddEdtbl').click();
  357. }
  358. }
  359. },
  360. addExpr: function(eb) {
  361. var checkKeys = false;
  362. Object.keys(this.rselCond).map(function(a, i) {
  363. if (a === eb.cond)
  364. checkKeys = true;
  365. });
  366. if (checkKeys) {
  367. try {
  368. this.rselCond[eb.cond].op(eb.op);
  369. if (eb.op2 !== null )
  370. this.rselCond[eb.cond].op2(eb.op2);
  371. if (eb.condmod !== null )
  372. this.rselCond[eb.cond].condmod(eb.condmod);
  373.  
  374. if (eb.val2 === null ) {
  375. if (eb.val !== null )
  376. this.rselCond[eb.cond].val(eb.val);
  377. this.rselCond[eb.cond].add();
  378. } else {
  379. this.rselBtns.lfParens();
  380. this.rselCond[eb.cond].val(eb.val);
  381. this.rselCond[eb.cond].add();
  382. this.rselBtns.or();
  383. this.rselCond[eb.cond].val(eb.val2);
  384. this.rselCond[eb.cond].add();
  385. this.rselBtns.rtParens();
  386. }
  387.  
  388. } catch (err) {
  389. return {
  390. errorCode: 101,
  391. errorMsg: 'Error: Unable to parse expression text.',
  392. err: err
  393. };
  394. }
  395. } else {
  396. return {
  397. errorCode: 3,
  398. errorMsg: 'Selection condition was not recognized'
  399. };
  400. //
  401. }
  402. return {
  403. errorCode: 0
  404. };
  405. },
  406. //=============================================================================
  407. parseExpr: function(parseThis) {
  408. //---------------------------------------------------------------
  409. parseThis = parseThis.replace(/\bpri?m?(?:ary|\.)?\s?(?:or)\s?alt(?:ern|s)?(?:\.)?/ig, 'any');
  410. parseThis = parseThis.replace(/\b((?:un)?name[ds]?)\b|\b(road) type\b|\b(last) update\b|\b(speed) limits?\b/ig, '$1$2$3$4')
  411. parseThis = parseThis.replace(/\b(man)ual (lock)s?\b|\b(traf)[fic]* (lock)s?\b/ig, '$1$2$3$4');
  412. parseThis = parseThis.replace(/\b(created|updated)\s(by)\b/ig, '$1$2');
  413. parseThis = parseThis.replace(/\bon screen/ig, 'onscreen');
  414. //\b(?:in|on|off|out|outside)(?: of)?[- ]?screen\b
  415. parseThis = parseThis.replace(/\b(?:off|out)(?: of)?[- ]?screen/ig, 'offscreen');
  416.  
  417. var parseExprArray = parseThis.match(
  418. /(\(['"].*?['"]\)|".*?"|'.*?')|\bno[\s-]alt|\b(?:street[\s-]?)?name\(s\)|\bstreet(?:\snames?)\b|\btoll(?:[-\s]?ro?a?d)?\b|\bdoes(?:\s?n[o']t)\b|(?:!\s?)?contains?\b|!=|>=|<=|[ab][<->]{2}[ab]|\w+(\(s\))?|&&|\|\||!=|[|&<>=()!~]/gi
  419. ),
  420. parseExprHistory = [],
  421. condMatches = [],
  422. condMatchPhrases = [],
  423. exprMatches = [],
  424. exprMatchPhrases = [],
  425. exprFragment, unwantedWordsSearch,
  426. e, f, b, fLength;
  427.  
  428. // The following parses the expression text into unique chunks within separate array elements
  429. e = parseExprArray.length;
  430. while (e-- > 0) {
  431. try {
  432. exprFragment = parseExprArray.shift();
  433. //console.info(exprFragment);
  434.  
  435. // Find operators that join individual expressions (AND|OR|!|parenthesis)
  436. if (/^(?:and|or|&&|\|\||!=|[=&|()!])$/i.test(exprFragment)) {
  437. exprMatches.push(exprFragment.toLowerCase());
  438. exprMatchPhrases.push(exprFragment.toLowerCase());
  439. }
  440.  
  441. // Identify elements that contain selection condition names
  442. if (
  443. /^country|^state|^city|^street|^(?:un|street[\s-]?)?name|^road|^round|^toll|^speed|^dir|^elevation|^tun|^manlock|^traflock|^speed|^new|^changed|screen$|^restrict|^clos|^createdby|^last|^updatedby|^length|^id|^editable/i
  444. .test(exprFragment)) {
  445. condMatches.push(exprFragment.toLowerCase());
  446. // lists specific selection conditions
  447. exprMatches.push(exprFragment.toLowerCase());
  448. //same as condMatches, but includes operations as separate array elements
  449.  
  450. try {
  451. //search phrase fowards
  452. fLength = parseExprArray.length;
  453. f = 0;
  454. while (!(/^(and|or|&&|\|\||[&|)])$/i.test(parseExprArray[f])) && (++f < fLength)) {}
  455. //search phrase backwards
  456. b = parseExprHistory.length;
  457. while (!(/^(and|or|&&|\|\||[&|(])$/i.test(parseExprHistory[b - 1])) && (--b > 0)) {}
  458.  
  459. condMatchPhrases.push(parseExprHistory.slice(b).concat(exprFragment, parseExprArray.slice(0, f)));
  460. //list specific selection conditions and its criteria
  461.  
  462. unwantedWordsSearch = parseExprHistory.slice(b);
  463. if (unwantedWordsSearch && unwantedWordsSearch.length) {
  464. unwantedWordsSearch = unwantedWordsSearch.filter(function(a) {
  465. return !/\b(has|have|is|=|are|does|was|were)\b/i.test(a)
  466. });
  467. }
  468. if (/!|!=/.test(unwantedWordsSearch[0]))
  469. unwantedWordsSearch.splice(0, 1);
  470.  
  471. exprMatchPhrases.push(unwantedWordsSearch.concat(parseExprArray.slice(0, f)));
  472. //excludes the match cond
  473.  
  474. parseExprHistory = parseExprHistory.concat(exprFragment, parseExprArray.slice(0, f));
  475. parseExprArray = parseExprArray.slice(f);
  476. e -= f;
  477. } catch (err) {
  478. return {
  479. errorCode: 101,
  480. errorMsg: 'Error parsing expression at ' + exprFragment,
  481. err: err
  482. };
  483. }
  484. } else {
  485. parseExprHistory.push(exprFragment);
  486. }
  487. } catch (err) {
  488. return {
  489. errorCode: 101,
  490. errdebug: 'Error parsing expression at ' + exprFragment,
  491. err: err
  492. };
  493. }
  494. }
  495. //while
  496.  
  497.  
  498. //---------------------------------------------------------------
  499. // Quick crude check for unmatched parentheses
  500. var nOpenParens = exprMatches.toString().match(/\(/g),
  501. nCloseParens = exprMatches.toString().match(/\)/g);
  502. if (!nOpenParens) nOpenParens = [];
  503. if (!nCloseParens) nCloseParens = [];
  504. if (nOpenParens.length !== nCloseParens.length)
  505. return {
  506. errorCode: 1,
  507. errorMsg: 'Warning: Open and close paretheses may be unmatched.'
  508. };
  509.  
  510. //---------------------------------------------------------------
  511.  
  512. return {
  513. errorCode: 0,
  514. exprMatches: exprMatches,
  515. exprMatchPhrases: exprMatchPhrases,
  516. condMatches: condMatches,
  517. condMatchPhrases: condMatchPhrases
  518. };
  519. },
  520. buildExpr: function(exprWord, exprPhrase) {
  521.  
  522. var exprBuild = RSelExprParser._rsel.getNewExprBuild();
  523. exprBuild.cond = exprWord;
  524.  
  525. //if (m===10) debugger;
  526.  
  527. //============================================================
  528. // Where the magic happens... sort of.
  529. //============================================================
  530. switch (true) {
  531. case exprWord === '(':
  532. this.rselBtns.lfParens();
  533. return false;
  534. case exprWord === ')':
  535. this.rselBtns.rtParens();
  536. return false;
  537. case 'and' === exprWord:
  538. this.rselBtns.and();
  539. return false;
  540. case 'or' === exprWord:
  541. this.rselBtns.or();
  542. return false;
  543. case /no alt/i.test(exprPhrase):
  544. exprBuild.cond = 'unnamed';
  545. exprBuild.op = true;
  546. exprBuild.op2 = true;
  547. return exprBuild;
  548. case '!' === exprWord:
  549. this.rselBtns.not();
  550. return false;
  551. case /^unnamed/.test(exprBuild.cond):
  552. exprBuild.cond = 'unnamed';
  553. exprBuild.op = true;
  554. exprBuild.op2 = false;
  555. return exprBuild;
  556.  
  557. // SPEED LIMITS
  558. case 'speed' === exprBuild.cond:
  559. try {
  560. if (exprPhrase.length < 2 && /\bnot?\b|!|!=/i.test(exprPhrase[0])) {
  561. exprBuild.op = 'none';
  562. } else {
  563. exprPhrase = exprPhrase.join(' ');
  564.  
  565. if (/\bnot?\b|!|!=/i.test(exprPhrase))
  566. RSelExprParser.rselBtns.not();
  567.  
  568. var optionText = RSelExprParser._rsel.getSelectOptions(RSelExprParser.rselCond.speed.opOptNodes);
  569. optionText = RegExp(optionText.join('|'), 'i').exec(exprPhrase);
  570. if (optionText)
  571. exprBuild.op = optionText[0];
  572. else
  573. exprBuild.op = 'any';
  574. }
  575.  
  576. var speedVal = exprPhrase.match(/(\d+)\s?mph|(\d+)\s?km/i);
  577. if (speedVal && speedVal.length === 2)
  578. exprBuild.val = speedVal[1];
  579. } catch (err) {
  580. exprBuild.errorCode = 101;
  581. exprBuild.err = err;
  582. return exprBuild;
  583. }
  584. return exprBuild;
  585.  
  586. // BINARY CONDITIONS:
  587. case exprPhrase.length === 0 || //suggests binary
  588. /^(screen|roundabout|toll|tun|new|changed|restrict|editable)/.test(exprBuild.cond) || //binary selection conditions
  589. (/^name.*|^closure/i.test(exprBuild.cond) && exprPhrase.length <= 1):
  590. //selection conditions that have both binary and multiple options
  591.  
  592. exprPhrase = exprPhrase.join(' ');
  593.  
  594. exprBuild.cond = exprBuild.cond.replace(/^name.*/, 'name');
  595. exprBuild.cond = exprBuild.cond.replace(/^toll\s.*/, 'toll');
  596.  
  597. if (/\bnot?\b|!|!=/i.test(exprPhrase)) {
  598. exprBuild.op = false;
  599. } else {
  600. exprBuild.op = true;
  601. }
  602. switch (exprBuild.cond) {
  603. case 'name':
  604. try {
  605. if (/alt/i.test(exprPhrase)) {
  606. exprBuild.cond = 'unnamed';
  607. exprBuild.op = false;
  608. exprBuild.op2 = true;
  609. } else {
  610. exprBuild.cond = 'unnamed';
  611. exprBuild.op = false;
  612. exprBuild.op2 = false;
  613. }
  614. return exprBuild;
  615. } catch (err) {
  616. exprBuild.errorCode = 101;
  617. exprBuild.err = err;
  618. return exprBuild;
  619. }
  620. case 'closure':
  621. exprBuild.op2 = '---';
  622. return exprBuild;
  623. case 'onscreen':
  624. exprBuild.cond = 'screen';
  625. exprBuild.op = true;
  626. return exprBuild;
  627. case 'offscreen':
  628. exprBuild.cond = 'screen';
  629. exprBuild.op = false;
  630. return exprBuild;
  631. case 'roundabout':
  632. case 'toll':
  633. case 'tunnel':
  634. case 'new':
  635. case 'changed':
  636. case 'restriction':
  637. case 'editable':
  638. return exprBuild;
  639. default:
  640. exprBuild.errorCode = 101;
  641. exprBuild.errorMsg = 'Error: Presumed binary selector had no match.';
  642. return exprBuild;
  643. }
  644. //switch
  645.  
  646. //--------------------------------------------------------------------
  647.  
  648. case /^closure/.test(exprBuild.cond):
  649. try {
  650. exprPhrase = exprPhrase.join().toLowerCase();
  651. exprBuild.op = !(/does\s?n['o]t|!|!=/.test(exprPhrase));
  652. //checkbox
  653. exprBuild.op2 = /start|end/.exec(exprPhrase) + 's';
  654. //starts/ends
  655. exprBuild.condmod = /before|after|\bin\b/.exec(exprPhrase) + '';
  656. //in/before/after
  657. if (!exprBuild.condmod)
  658. exprBuild.condmod = 'in';
  659. exprBuild.val = /\d+/.exec(exprPhrase) + '';
  660. //days ago
  661. } catch (err) {
  662. exprBuild.errorCode = 101;
  663. exprBuild.err = err;
  664. return exprBuild;
  665. }
  666. return exprBuild;
  667.  
  668. default:
  669. // CONDITION NAME MATCHING (TYPE OF SELECTION)
  670. try {
  671. if (/^(str.*|cit.*)/.test(exprBuild.cond)) {
  672. exprBuild.cond = exprBuild.cond.replace(/^str.*/, 'street');
  673. exprBuild.cond = exprBuild.cond.replace(/^cit.*/, 'city');
  674. var exprStart = exprPhrase.slice(0, -1), //don't include last element bc it should be the name itself
  675. prim, alt;
  676. if (exprStart) {
  677. //exprStart = exprStart.toString().toLowerCase();
  678. prim = /\bprim?(?:ary|\.)?\b/i.test(exprStart);
  679. alt = /\balt(?:ern\w*|\.)?\b/i.test(exprStart);
  680. exprPhrase = exprStart.filter(function(a) {
  681. return !/^pri|^alt/i.test(a)
  682. }).concat(exprPhrase.slice(-1));
  683. } else {
  684. prim = false;
  685. alt = false;
  686. }
  687. if (prim && alt)
  688. exprBuild.condmod = 2;
  689. else if (prim)
  690. exprBuild.condmod = 0;
  691. else if (alt)
  692. exprBuild.condmod = 1;
  693. else
  694. exprBuild.condmod = 0;
  695. }
  696. } catch (err) {
  697. exprBuild.errorCode = 101;
  698. exprBuild.err = err;
  699. return exprBuild;
  700. }
  701.  
  702. // COMPARATOR OPERATION MATCHING
  703. try {
  704. // Convert natural lang representation to standard comparator operations
  705. var exprPhraseStr = exprPhrase.join(' ').replace(/\bcontains?/i, 'contains').replace(/(?:\bdo(?:es)?\s?n[o']t\s|!\s?)(contains)/i, '! $1');
  706. //.replace(/\b(?:do(?:es)?\s?n[o']t\s|!\s?)contains?/i, '!^').replace(/\bcontains?/i,'\u220b');
  707.  
  708. // Comparator operations with standard representation
  709. exprBuild.op = /(?:! )?contains|[!<>=~]{1,2}/i.exec(exprPhraseStr) + '';
  710.  
  711. } catch (err) {
  712. exprBuild.errorCode = 101;
  713. exprBuild.err = err;
  714. return exprBuild;
  715. }
  716.  
  717. // SELECTION VALUE MATCHING
  718. try {
  719. if (/^length|^last/.test(exprBuild.cond)) {
  720. exprBuild.val = exprPhraseStr.match(/\b\d+/) + ''
  721. } else {
  722. try {
  723. // The following line is kind of elaborate bc it needed to grab text between parens/quotes while keeping the inner quotes
  724. exprBuild.val = exprPhraseStr.replace(new RegExp('^(?:\\s?' + exprBuild.op + '\\s)(.*)','i'), '$1').replace(/^\(["'](.*?)['"]\)$|^\s?["'](.*)["']$|^["'](.*)['"]$|\b(\w*?)\b/, '$1$2$3$4').replace(/(") (\w) (")/, '$1$2$3');
  725.  
  726. } catch (err) {
  727. exprBuild.errorCode = 2;
  728. exprBuild.err = err;
  729. return exprBuild;
  730. }
  731.  
  732. if (/^direction/.test(exprBuild.cond)) {
  733. exprBuild.val = exprBuild.val.match(/A[<>-\s]*B|B[<>-\s]*A|unknown/i) + '';
  734. //reduce to unique key words...
  735. }
  736. }
  737.  
  738. return exprBuild;
  739.  
  740. } catch (err) {
  741. exprBuild.errorCode = 101;
  742. exprBuild.err = err;
  743. return exprBuild;
  744. }
  745. }
  746. //switch
  747. },
  748. //parseExpr()
  749. updateExpression: function(parseThis) {
  750. console.info('*** Begin parsing expression... ***');
  751. this.rselBtns.clear();
  752.  
  753. var parsed = this.parseExpr(parseThis);
  754.  
  755. if (parsed && !parsed.errorCode) {
  756. var exprMatches = parsed.exprMatches,
  757. exprMatchPhrases = parsed.exprMatchPhrases,
  758. exprFragment, exprFragPhrase, mLength, m, __EXPR_DEBUGINFO;
  759.  
  760. mLength = exprMatchPhrases.length;
  761. for (m = 0; m < mLength; m++) {
  762. __EXPR_DEBUGINFO = this.new__EXPR_DEBUGINFO(m, exprMatches[m], exprMatchPhrases[m]);
  763.  
  764. //if (m > 3) debugger;
  765.  
  766. exprFragment = exprMatches[m];
  767. exprFragPhrase = exprMatchPhrases[m];
  768.  
  769. if (exprFragPhrase.constructor !== Array) exprFragPhrase = [exprFragPhrase];
  770.  
  771. var exprBuild = this.buildExpr(exprFragment, exprFragPhrase);
  772.  
  773. if (exprBuild && !exprBuild.errorCode) {
  774. __EXPR_DEBUGINFO.errorStatus = this.addExpr(exprBuild);
  775.  
  776. if (__EXPR_DEBUGINFO.errorStatus && __EXPR_DEBUGINFO.errorStatus.errorCode) {
  777. console.warn('updateExpression() may have partly failed. Check results.');
  778. __EXPR_DEBUGINFO.exprBuild = exprBuild;
  779. console.debug(__EXPR_DEBUGINFO);
  780. }
  781. } else if (exprBuild && exprBuild.errorCode) {
  782. console.warn('updateExpression() may have partly failed. Check results.');
  783. __EXPR_DEBUGINFO.exprBuild = exprBuild;
  784. console.debug(__EXPR_DEBUGINFO);
  785. }
  786. }
  787. //for each condition matched
  788. } else {
  789. console.debug(parsed);
  790. }
  791. }
  792. };