rsel-exprparser-basic

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

目前为 2016-03-15 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/17641/113461/rsel-exprparser-basic.js

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