expr.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // ******* Expr MANAGER ******** //
  2. $axure.internal(function($ax) {
  3. var _expr = $ax.expr = {};
  4. var _binOpHandlers = {
  5. '&&': function(left, right) { return _binOpOverride(left, right, function(left) { return $ax.getBool(left) && $ax.getBool(right()); }); },
  6. '||': function(left, right) { return _binOpOverride(left, right, function(left) { return $ax.getBool(left) || $ax.getBool(right()); }); },
  7. '==': function(left, right) { return isEqual(left, right, true); },
  8. '!=': function(left, right) { return !isEqual(left, right, true); },
  9. '>': function(left, right) { return _binOpNum(left, right, function(left, right) { return left > right; }); },
  10. '<': function(left, right) { return _binOpNum(left, right, function(left, right) { return left < right; }); },
  11. '>=': function(left, right) { return _binOpNum(left, right, function(left, right) { return left >= right; }); },
  12. '<=': function(left, right) { return _binOpNum(left, right, function(left, right) { return left <= right; }); }
  13. };
  14. var checkOps = function(left, right) {
  15. return left == undefined || right == undefined;
  16. };
  17. var isEqual = function (left, right, isFunction) {
  18. if (isFunction) {
  19. //if left and right is function, then get the value
  20. //otherwise left and right should be already the value we want
  21. left = left();
  22. right = right();
  23. }
  24. if(checkOps(left, right)) return false;
  25. if(left instanceof Date && right instanceof Date) {
  26. if(left.getMilliseconds() != right.getMilliseconds()) return false;
  27. if(left.getSeconds() != right.getSeconds()) return false;
  28. if(left.getMinutes() != right.getMinutes()) return false;
  29. if(left.getHours() != right.getHours()) return false;
  30. if(left.getDate() != right.getDate()) return false;
  31. if(left.getMonth() != right.getMonth()) return false;
  32. if(left.getYear() != right.getYear()) return false;
  33. return true;
  34. }
  35. if(left instanceof Object && right instanceof Object) {
  36. var prop;
  37. // Go through all of lefts properties and compare them to rights.
  38. for(prop in left) {
  39. if(!left.hasOwnProperty(prop)) continue;
  40. // If left has a property that the right doesn't they are not equal.
  41. if(!right.hasOwnProperty(prop)) return false;
  42. // If any of their properties are not equal, they are not equal.
  43. if(!isEqual(left[prop], right[prop], false)) return false;
  44. }
  45. for(prop in right) {
  46. // final check to make sure right doesn't have some extra properties that make them not equal.
  47. if(left.hasOwnProperty(prop) != right.hasOwnProperty(prop)) return false;
  48. }
  49. return true;
  50. }
  51. return $ax.getBool(left) == $ax.getBool(right);
  52. };
  53. var _binOpOverride = function(left, right, func) {
  54. left = left();
  55. if(left == undefined) return false;
  56. var res = func(left, right);
  57. return res == undefined ? false : res;
  58. };
  59. var _binOpNum = function(left, right, func) {
  60. var left = left();
  61. var right = right();
  62. if(checkOps(left, right)) return false;
  63. return func(left, Number(right));
  64. };
  65. var _exprHandlers = {};
  66. _exprHandlers.array = function(expr, eventInfo) {
  67. var returnVal = [];
  68. for(var i = 0; i < expr.items.length; i++) {
  69. returnVal[returnVal.length] = _evaluateExpr(expr.items[i], eventInfo);
  70. }
  71. return returnVal;
  72. };
  73. _exprHandlers.binaryOp = function(expr, eventInfo) {
  74. var left = function() { return expr.leftExpr && _evaluateExpr(expr.leftExpr, eventInfo); };
  75. var right = function() { return expr.rightExpr && _evaluateExpr(expr.rightExpr, eventInfo); };
  76. if(left == undefined || right == undefined) return false;
  77. return _binOpHandlers[expr.op](left, right);
  78. };
  79. _exprHandlers.block = function(expr, eventInfo) {
  80. var subExprs = expr.subExprs;
  81. for(var i = 0; i < subExprs.length; i++) {
  82. _evaluateExpr(subExprs[i], eventInfo); //ignore the result
  83. }
  84. };
  85. _exprHandlers.booleanLiteral = function(expr) {
  86. return expr.value;
  87. };
  88. _exprHandlers.nullLiteral = function() { return null; };
  89. _exprHandlers.pathLiteral = function(expr, eventInfo) {
  90. if(expr.isThis) return [eventInfo.srcElement];
  91. if(expr.isFocused && window.lastFocusedControl) {
  92. $ax('#' + window.lastFocusedControl).focus();
  93. return [window.lastFocusedControl];
  94. }
  95. if(expr.isTarget) return [eventInfo.targetElement];
  96. return $ax.getElementIdsFromPath(expr.value, eventInfo);
  97. };
  98. _exprHandlers.panelDiagramLiteral = function(expr, eventInfo) {
  99. var elementIds = $ax.getElementIdsFromPath(expr.panelPath, eventInfo);
  100. var elementIdsWithSuffix = [];
  101. var suffix = '_state' + expr.panelIndex;
  102. for(var i = 0; i < elementIds.length; i++) {
  103. elementIdsWithSuffix[i] = $ax.repeater.applySuffixToElementId(elementIds[i], suffix);
  104. }
  105. return String($jobj(elementIdsWithSuffix).data('label'));
  106. };
  107. _exprHandlers.fcall = function(expr, eventInfo) {
  108. var oldTarget = eventInfo.targetElement;
  109. var targets = [];
  110. var fcallArgs = [];
  111. var exprArgs = expr.arguments;
  112. for(var i = 0; i < expr.arguments.length; i++) {
  113. var exprArg = exprArgs[i];
  114. var fcallArg = '';
  115. if(targets.length) {
  116. for(var j = 0; j < targets.length; j++) {
  117. if(exprArg == null) {
  118. fcallArgs[j][i] = null;
  119. continue;
  120. }
  121. eventInfo.targetElement = targets[j];
  122. fcallArg = _evaluateExpr(exprArg, eventInfo);
  123. if(typeof (fcallArg) == 'undefined') return '';
  124. fcallArgs[j][i] = fcallArg;
  125. }
  126. } else {
  127. if(exprArg == null) {
  128. fcallArgs[i] = null;
  129. continue;
  130. }
  131. fcallArg = _evaluateExpr(exprArg, eventInfo);
  132. if(typeof (fcallArg) == 'undefined') return '';
  133. fcallArgs[i] = fcallArg;
  134. }
  135. // We do support null exprArgs...
  136. // TODO: This makes 2 assumptions that may change in the future. 1. The pathLiteral is the always the first arg. 2. there is always only 1 pathLiteral
  137. if(exprArg && exprArg.exprType == 'pathLiteral') {
  138. targets = fcallArg;
  139. // fcallArgs is now an array of an array of args
  140. for(j = 0; j < targets.length; j++) fcallArgs[j] = [[fcallArg[j]]];
  141. }
  142. }
  143. // we want to preserve the target element from outside this function.
  144. eventInfo.targetElement = oldTarget;
  145. var retval = '';
  146. if(targets.length) {
  147. // Go backwards so retval is the first item.
  148. for(i = targets.length - 1; i >= 0; i--) {
  149. var args = fcallArgs[i];
  150. // Add event info to the end
  151. args[args.length] = eventInfo;
  152. retval = _exprFunctions[expr.functionName].apply(this, args);
  153. }
  154. } else fcallArgs[fcallArgs.length] = eventInfo;
  155. return targets.length ? retval : _exprFunctions[expr.functionName].apply(this, fcallArgs);
  156. };
  157. _exprHandlers.globalVariableLiteral = function(expr) {
  158. return expr.variableName;
  159. };
  160. _exprHandlers.keyPressLiteral = function(expr) {
  161. var keyInfo = {};
  162. keyInfo.keyCode = expr.keyCode;
  163. keyInfo.ctrl = expr.ctrl;
  164. keyInfo.alt = expr.alt;
  165. keyInfo.shift = expr.shift;
  166. return keyInfo;
  167. };
  168. _exprHandlers.adaptiveViewLiteral = function(expr) {
  169. return expr.id;
  170. };
  171. _exprHandlers.optionLiteral = function(expr) {
  172. return expr.value;
  173. }
  174. var _substituteSTOs = function(expr, eventInfo) {
  175. //first evaluate the local variables
  176. var scope = {};
  177. for(var varName in expr.localVariables) {
  178. scope[varName] = $ax.expr.evaluateExpr(expr.localVariables[varName], eventInfo);
  179. }
  180. // TODO: [ben] Date and data object (obj with info for url or image) both need to return non-strings.
  181. var i = 0;
  182. var retval;
  183. var retvalString = expr.value.replace(/\[\[(?!\[)(.*?)\]\](?=\]*)/g, function(match) {
  184. var sto = expr.stos[i++];
  185. if(sto.sto == 'error') return match;
  186. try {
  187. var result = $ax.evaluateSTO(sto, scope, eventInfo);
  188. } catch(e) {
  189. return match;
  190. }
  191. if((result instanceof Object) && i == 1 && expr.value.substring(0, 2) == '[[' &&
  192. expr.value.substring(expr.value.length - 2) == ']]') {
  193. // If the result was an object, this was the first result, and the whole thing was this expresion.
  194. retval = result;
  195. }
  196. return ((result instanceof Object) && (result.label || result.text)) || result;
  197. });
  198. // If more than one group returned, the object is not valid
  199. if(i != 1) retval = false;
  200. return retval || retvalString;
  201. };
  202. _exprHandlers.htmlLiteral = function (expr, eventInfo) {
  203. eventInfo.htmlLiteral = true;
  204. var html = _substituteSTOs(expr, eventInfo);
  205. eventInfo.htmlLiteral = false
  206. return html;
  207. };
  208. _exprHandlers.stringLiteral = function(expr, eventInfo) {
  209. return _substituteSTOs(expr, eventInfo);
  210. };
  211. var _exprFunctions = {};
  212. _exprFunctions.SetCheckState = function(elementIds, value) {
  213. var toggle = value == 'toggle';
  214. var boolValue = Boolean(value) && value != 'false';
  215. for(var i = 0; i < elementIds.length; i++) {
  216. var query = $ax('#' + elementIds[i]);
  217. query.selected(toggle ? !query.selected() : boolValue);
  218. }
  219. };
  220. _exprFunctions.SetSelectedOption = function(elementIds, value) {
  221. for(var i = 0; i < elementIds.length; i++) {
  222. var elementId = elementIds[i];
  223. var obj = $jobj($ax.INPUT(elementId));
  224. if(obj.val() == value) return;
  225. obj.val(value);
  226. if($ax.event.HasSelectionChanged($ax.getObjectFromElementId(elementId))) $ax.event.raiseSyntheticEvent(elementId, 'onSelectionChange');
  227. }
  228. };
  229. _exprFunctions.SetGlobalVariableValue = function(varName, value) {
  230. $ax.globalVariableProvider.setVariableValue(varName, value);
  231. };
  232. _exprFunctions.SetWidgetFormText = function(elementIds, value) {
  233. for(var i = 0; i < elementIds.length; i++) {
  234. var elementId = elementIds[i];
  235. var inputId = $ax.repeater.applySuffixToElementId(elementId, '_input');
  236. var obj = $jobj(inputId);
  237. if(obj.val() == value || (value == '' && $ax.placeholderManager.isActive(elementId))) return;
  238. obj.val(value);
  239. $ax.placeholderManager.updatePlaceholder(elementId, !value);
  240. if($ax.event.HasTextChanged($ax.getObjectFromElementId(elementId))) $ax.event.TryFireTextChanged(elementId);
  241. }
  242. };
  243. _exprFunctions.SetFocusedWidgetText = function(elementId, value) {
  244. if(window.lastFocusedControl) {
  245. var elementId = window.lastFocusedControl;
  246. var type = $obj(elementId).type;
  247. if ($ax.public.fn.IsTextBox(type) || $ax.public.fn.IsTextArea(type)) _exprFunctions.SetWidgetFormText([elementId], value);
  248. else _exprFunctions.SetWidgetRichText([elementId], value, true);
  249. }
  250. };
  251. _exprFunctions.GetRtfElementHeight = function(rtfElement) {
  252. if(rtfElement.innerHTML == '') rtfElement.innerHTML = '&nbsp;';
  253. return rtfElement.offsetHeight;
  254. };
  255. _exprFunctions.SetWidgetRichText = function(ids, value, plain) {
  256. // Converts dates, widgetinfo, and the like to strings.
  257. value = _exprFunctions.ToString(value);
  258. //Replace any newlines with line breaks
  259. var finalValue = value.replace(/\r\n/g, '<br>').replace(/\n/g, '<br>');
  260. for(var i = 0; i < ids.length; i++) {
  261. var id = ids[i];
  262. // If calling this on button shape, get the id of the rich text panel inside instead
  263. var type = $obj(id).type;
  264. if(type != 'richTextPanel' && type != 'hyperlink') {
  265. id = $jobj(id).find('.text')[0].id;
  266. }
  267. var element = window.document.getElementById(id);
  268. $ax.visibility.SetVisible(element, value != '');
  269. $ax.style.transformTextWithVerticalAlignment(id, function() {
  270. var spans = $jobj(id).find('span');
  271. if(plain) {
  272. // Wrap in span and p, style them accordingly.
  273. var span = $('<span></span>');
  274. if(spans.length > 0) {
  275. span.attr('style', $(spans[0]).attr('style'));
  276. span.attr('id', $(spans[0]).attr('id'));
  277. }
  278. // Can't set value as text because '<br/>' doesn't actually do a line break
  279. // Can't set vaule as html because it doesn't like '<' and ignores all after it
  280. // Create tags yourself
  281. var lines = value.split(/\r\n|\n/);
  282. if(lines.length == 1) span.text(value);
  283. else {
  284. for(var i = 0; i < lines.length; i++) {
  285. if (i != 0) span.append($('<br />'));
  286. var line = lines[i];
  287. if(line.length == 0) continue;
  288. var subSpan = $('<span />');
  289. subSpan.text(line);
  290. span.append(subSpan);
  291. }
  292. }
  293. var p = $('<p></p>');
  294. var ps = $jobj(id).find('p');
  295. if(ps.length > 0) {
  296. p.attr('style', $(ps[0]).attr('style'));
  297. p.attr('id', $(ps[0]).attr('id'));
  298. }
  299. p.append(span);
  300. finalValue = $('<div></div>').append(p).html();
  301. }
  302. element.innerHTML = finalValue;
  303. });
  304. if(!plain) $ax.style.CacheOriginalText(id, true);
  305. }
  306. };
  307. _exprFunctions.GetCheckState = function(ids) {
  308. return $ax('#' + ids[0]).selected();
  309. };
  310. _exprFunctions.GetSelectedOption = function (ids) {
  311. var inputs = $jobj($ax.INPUT(ids[0]));
  312. return inputs.length ? inputs[0].value : '';
  313. };
  314. _exprFunctions.GetNum = function(str) {
  315. //Setting a GlobalVariable to some blank text then setting a widget to the value of that variable would result in 0 not ""
  316. //I have fixed this another way so commenting this should be fine now
  317. //if (!str) return "";
  318. return isNaN(str) ? str : Number(str);
  319. };
  320. _exprFunctions.GetGlobalVariableValue = function(id) {
  321. return $ax.globalVariableProvider.getVariableValue(id);
  322. };
  323. _exprFunctions.GetGlobalVariableLength = function(id) {
  324. return _exprFunctions.GetGlobalVariableValue(id).length;
  325. };
  326. _exprFunctions.GetWidgetText = function(ids) {
  327. if($ax.placeholderManager.isActive(ids[0])) return '';
  328. var input = $ax.INPUT(ids[0]);
  329. return $ax('#' + ($jobj(input).length ? input : ids[0])).text();
  330. };
  331. _exprFunctions.GetFocusedWidgetText = function() {
  332. if(window.lastFocusedControl) {
  333. return $ax('#' + window.lastFocusedControl).text();
  334. } else {
  335. return "";
  336. }
  337. };
  338. _exprFunctions.GetWidgetValueLength = function(ids) {
  339. var id = ids[0];
  340. if(!id) return undefined;
  341. if($ax.placeholderManager.isActive(id)) return 0;
  342. var obj = $jobj($ax.INPUT(id));
  343. if(!obj.length) obj = $jobj(id);
  344. var val = obj[0].value || _exprFunctions.GetWidgetText([id]);
  345. return val.length;
  346. };
  347. _exprFunctions.GetPanelState = function(ids) {
  348. var id = ids[0];
  349. if(!id) return undefined;
  350. var stateId = $ax.visibility.GetPanelState(id);
  351. return stateId && String($jobj(stateId).data('label'));
  352. };
  353. _exprFunctions.GetWidgetVisibility = function(ids) {
  354. var id = ids[0];
  355. if(!id) return undefined;
  356. return $ax.visibility.IsIdVisible(id);
  357. };
  358. // ***************** Validation Functions ***************** //
  359. _exprFunctions.IsValueAlpha = function(val) {
  360. var isAlphaRegex = new RegExp("^[a-z\\s]+$", "gi");
  361. return isAlphaRegex.test(val);
  362. };
  363. _exprFunctions.IsValueNumeric = function(val) {
  364. var isNumericRegex = new RegExp("^[0-9,\\.\\s]+$", "gi");
  365. return isNumericRegex.test(val);
  366. };
  367. _exprFunctions.IsValueAlphaNumeric = function(val) {
  368. var isAlphaNumericRegex = new RegExp("^[0-9a-z\\s]+$", "gi");
  369. return isAlphaNumericRegex.test(val);
  370. };
  371. _exprFunctions.IsValueOneOf = function(val, values) {
  372. for(var i = 0; i < values.length; i++) {
  373. var option = values[i];
  374. if(val == option) return true;
  375. }
  376. //by default, return false
  377. return false;
  378. };
  379. _exprFunctions.IsValueNotAlpha = function(val) {
  380. return !_exprFunctions.IsValueAlpha(val);
  381. };
  382. _exprFunctions.IsValueNotNumeric = function(val) {
  383. return !_exprFunctions.IsValueNumeric(val);
  384. };
  385. _exprFunctions.IsValueNotAlphaNumeric = function(val) {
  386. return !_exprFunctions.IsValueAlphaNumeric(val);
  387. };
  388. _exprFunctions.IsValueNotOneOf = function(val, values) {
  389. return !_exprFunctions.IsValueOneOf(val, values);
  390. };
  391. _exprFunctions.GetKeyPressed = function(eventInfo) {
  392. return eventInfo.keyInfo;
  393. };
  394. _exprFunctions.GetCursorRectangles = function() {
  395. var rects = new Object();
  396. rects.lastRect = new $ax.drag.Rectangle($ax.lastMouseLocation.x, $ax.lastMouseLocation.y, 1, 1);
  397. rects.currentRect = new $ax.drag.Rectangle($ax.mouseLocation.x, $ax.mouseLocation.y, 1, 1);
  398. return rects;
  399. };
  400. _exprFunctions.GetWidgetRectangles = function (elementIds, eventInfo) {
  401. var elementId = elementIds[0];
  402. var rects = new Object();
  403. var jObj = $jobj(elementId);
  404. var axObj = $ax('#' + elementId);
  405. var invalid = jObj.length == 0;
  406. var parent = jObj;
  407. // Or are in valid if no obj can be found, or if it is not visible.
  408. while(parent.length != 0 && !parent.is('body')) {
  409. if(parent.css('display') == 'none') {
  410. invalid = true;
  411. break;
  412. }
  413. parent = parent.parent();
  414. }
  415. if(invalid) {
  416. rects.lastRect = rects.currentRect = new $ax.drag.Rectangle(-1, -1, -1, -1);
  417. return rects;
  418. }
  419. rects.lastRect = new $ax.drag.Rectangle(
  420. axObj.left(),
  421. axObj.top(),
  422. axObj.width(),
  423. axObj.height());
  424. rects.currentRect = rects.lastRect;
  425. return rects;
  426. };
  427. _exprFunctions.GetWidget = function(elementId) {
  428. return $ax.getWidgetInfo(elementId[0]);
  429. };
  430. _exprFunctions.GetAdaptiveView = function() {
  431. return $ax.adaptive.currentViewId || '';
  432. };
  433. _exprFunctions.IsEntering = function(movingRects, targetRects) {
  434. return !movingRects.lastRect.IntersectsWith(targetRects.currentRect) && movingRects.currentRect.IntersectsWith(targetRects.currentRect);
  435. };
  436. _exprFunctions.IsLeaving = function(movingRects, targetRects) {
  437. return movingRects.lastRect.IntersectsWith(targetRects.currentRect) && !movingRects.currentRect.IntersectsWith(targetRects.currentRect);
  438. };
  439. var _IsOver = _exprFunctions.IsOver = function(movingRects, targetRects) {
  440. return movingRects.currentRect.IntersectsWith(targetRects.currentRect);
  441. };
  442. _exprFunctions.IsNotOver = function(movingRects, targetRects) {
  443. return !_IsOver(movingRects, targetRects);
  444. };
  445. _exprFunctions.ValueContains = function(inputString, value) {
  446. return inputString.indexOf(value) > -1;
  447. };
  448. _exprFunctions.ValueNotContains = function(inputString, value) {
  449. return !_exprFunctions.ValueContains(inputString, value);
  450. };
  451. _exprFunctions.ToString = function(value) {
  452. if(value.isWidget) {
  453. return value.text;
  454. }
  455. return String(value);
  456. };
  457. var _evaluateExpr = $ax.expr.evaluateExpr = function(expr, eventInfo, toString) {
  458. if(expr === undefined || expr === null) return undefined;
  459. var result = _exprHandlers[expr.exprType](expr, eventInfo);
  460. return toString ? _exprFunctions.ToString(result) : result;
  461. };
  462. });