not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
10 KiB

  1. //This is the CalculateApp object, providing a full context for the app
  2. var CalculateApp = {
  3. /* UI Elements */
  4. elements: {
  5. resultsZoneDiv: undefined,
  6. calcInputDiv: undefined,
  7. calcInput: undefined,
  8. labelInput: undefined,
  9. modalDiv: undefined
  10. },
  11. /* libraries loaded by require */
  12. libs: {
  13. mustache: undefined,
  14. functionPlot: undefined
  15. },
  16. /* Application data */
  17. data: {
  18. buddyColor: {
  19. stroke: "#1500A7",
  20. fill: "#ff0000"
  21. },
  22. calculations: [],
  23. windowWidth: undefined,
  24. windowHeight: undefined,
  25. isMobile: /iphone|ipod|ipad|android|blackberry|opera mini|opera mobi|skyfire|maemo|windows phone|palm|iemobile|symbian|symbianos|fennec/i.test(navigator.userAgent.toLowerCase()),
  26. isIos: (navigator.userAgent.match(/iPad|iPhone|iPod/g) ? true : false ),
  27. outputBase: 10,
  28. outputDigits: 6,
  29. isRadian: false,
  30. pi: 3.141592653589793
  31. },
  32. /* Return if Chrome WebApp env. Chrome WebApp does not allow eval. */
  33. isFullMode: function fullModeEval() {
  34. if (typeof chrome != "undefined" && chrome.app && chrome.app.runtime) {
  35. return false;
  36. }
  37. return true;
  38. }(),
  39. /* Evaluate a calculation without using eval() */
  40. evalParser: function(input) {
  41. var result = Parser.parse(input).evaluate();
  42. return result;
  43. },
  44. /* Evaluate a calculation using eval() - provides advanced features */
  45. evalMathJS: function(input) {
  46. var result = mathjs.eval(input);
  47. return result;
  48. },
  49. /* Auto eval using light or full mode */
  50. eval: function(input) {
  51. var result;
  52. var resultBase10;
  53. if (CalculateApp.isFullMode) {
  54. result = CalculateApp.evalMathJS(input);
  55. resultBase10 = CalculateApp.baseConv(CalculateApp.evalMathJS(input), CalculateApp.data.outputBase, 10);
  56. } else {
  57. result = CalculateApp.evalParser(input);
  58. resultBase10 = CalculateApp.baseConv(CalculateApp.evalParser(input), CalculateApp.data.outputBase, 10);
  59. }
  60. return {
  61. resultBase10: CalculateApp.toFixed(result, CalculateApp.data.outputDigits),
  62. result: CalculateApp.toFixed(resultBase10, CalculateApp.data.outputDigits)
  63. };
  64. },
  65. /* Calcul f(x) with x = 0 using light or full mode*/
  66. evalFunctionAtZero: function(input) {
  67. var result;
  68. if (CalculateApp.isFullMode) {
  69. try {
  70. result = mathjs.eval(input)(0);
  71. if (isNaN(result) || !isFinite(result)) {
  72. return 0;
  73. }
  74. return result;
  75. } catch (error) {
  76. return 0;
  77. }
  78. } else {
  79. try {
  80. result = Parser.parse(input).evaluate({
  81. x: 0
  82. });
  83. if (isNaN(result) || !isFinite(result)) {
  84. return 0;
  85. }
  86. return result;
  87. } catch (error) {
  88. return 0;
  89. }
  90. }
  91. },
  92. /* Check if simple calculation or function */
  93. isGraph: function(input) {
  94. if (input.indexOf("f(x)") == -1) {
  95. return false;
  96. }
  97. return true;
  98. },
  99. /* Display a graph */
  100. _graph: function(equation, valueForZero, graphFn) {
  101. nanoModal(CalculateApp.elements.modalDiv, {
  102. buttons: [],
  103. }).onShow(function(m) {
  104. CalculateApp.elements.modalDiv.style.display = "block";
  105. CalculateApp.elements.modalDiv.style.marginLeft = "auto";
  106. CalculateApp.elements.modalDiv.style.marginRight = "auto";
  107. CalculateApp.elements.modalDiv.style.height = parseInt(CalculateApp.data.windowHeight * 80 / 100) + "px";
  108. CalculateApp.elements.modalDiv.style.width = parseInt(CalculateApp.data.windowWidth * 80 / 100) + "px";
  109. try {
  110. var openedModal = CalculateApp.libs.functionPlot({
  111. target: '#plot',
  112. yDomain: [valueForZero - 10, valueForZero + 10],
  113. xDomain: [-10, 10],
  114. width: parseInt(CalculateApp.data.windowWidth * 80 / 100),
  115. height: parseInt(CalculateApp.data.windowHeight * 80 / 100),
  116. data: [{
  117. fn: graphFn
  118. }]
  119. });
  120. for (var i = 0; i < 10; i++) {
  121. window.scroll(0, 0);
  122. }
  123. } catch (err) {
  124. m.hide();
  125. }
  126. }).onHide(function(m) {
  127. m.remove();
  128. setTimeout(function() {
  129. CalculateApp.focus();
  130. }, 300);
  131. }).show();
  132. },
  133. /* Display a graph using light mode */
  134. graphParser: function(equation) {
  135. var valueForZero = CalculateApp.evalFunctionAtZero(equation);
  136. var graphFn = function(x) {
  137. return Parser.parse(equation).evaluate({
  138. "x": x
  139. });
  140. };
  141. CalculateApp._graph(equation, valueForZero, graphFn);
  142. },
  143. /* Display a graph using full mode */
  144. graphMathJS: function(equation) {
  145. var valueForZero = CalculateApp.evalFunctionAtZero(equation);
  146. var graphFn = mathjs.eval(equation);
  147. CalculateApp._graph(equation, valueForZero, graphFn);
  148. },
  149. /* Display a graph using light or full mode */
  150. graph: function(input) {
  151. input = CalculateApp.tryPatchGraphEquation(input);
  152. if (CalculateApp.isFullMode) {
  153. return CalculateApp.graphMathJS(input);
  154. } else {
  155. return CalculateApp.graphParser(input);
  156. }
  157. },
  158. /* Display a calculation */
  159. displayCalculation: function(calculation) {
  160. var node = document.createElement("div");
  161. if (calculation.error) {
  162. node.innerHTML = CalculateApp.libs.mustache.render(getErrorTemplate(), calculation);
  163. } else {
  164. if (calculation.graph) {
  165. node.innerHTML = CalculateApp.libs.mustache.render(getGraphTemplate(), calculation);
  166. var childrens = node.childNodes[0].childNodes;
  167. var functionGraph = function(input) {
  168. CalculateApp.graph(input.target.value);
  169. };
  170. for (var i = 0; i < childrens.length; i++) {
  171. if (childrens[i].tagName === "BUTTON") {
  172. childrens[i].addEventListener('click', functionGraph);
  173. }
  174. }
  175. } else {
  176. node.innerHTML = CalculateApp.libs.mustache.render(getResultTemplate(), calculation);
  177. }
  178. }
  179. CalculateApp.elements.resultsZoneDiv.insertBefore(node, CalculateApp.elements.resultsZoneDiv.childNodes[0]);
  180. },
  181. /* Add calculation to list */
  182. storeCalculation: function(calculation) {
  183. if(CalculateApp.data.calculations === null){
  184. CalculateApp.data.calculations = [];
  185. }
  186. CalculateApp.data.calculations.push(calculation);
  187. },
  188. /* Persist calculation lists */
  189. persistCalculations: function() {
  190. var calculationsToSave = [];
  191. for (var i = CalculateApp.data.calculations.length - 1; i >= 0; i--) {
  192. if (calculationsToSave.length >= 20) {
  193. break;
  194. }
  195. calculationsToSave.unshift(CalculateApp.data.calculations[i]);
  196. }
  197. var json = JSON.stringify(calculationsToSave);
  198. CalculateApp.libs.activity.getDatastoreObject().setDataAsText(json);
  199. CalculateApp.libs.activity.getDatastoreObject().save(function() {});
  200. },
  201. /* Base convert */
  202. baseConv: function(number, to, from) {
  203. if (to == from) {
  204. return number;
  205. }
  206. return parseInt(number, from || 10).toString(to);
  207. },
  208. /* Handle window resize */
  209. onResize: function() {
  210. CalculateApp.data.windowHeight = getWindowHeight();
  211. CalculateApp.data.windowWidth = getWindowWidth();
  212. setTimeout(function() {
  213. CalculateApp.elements.resultsZoneDiv.style.height = CalculateApp.elements.calcInputDiv.clientHeight + "px";
  214. CalculateApp.elements.resultsZoneDiv.style.display = "block";
  215. }, 300);
  216. },
  217. /* Focus only if computer */
  218. focus: function() {
  219. if (!CalculateApp.data.isMobile) {
  220. CalculateApp.elements.calcInput.focus();
  221. }
  222. },
  223. /* Translation of the Gui */
  224. transateGui: function() {
  225. if (CalculateApp.libs.webL10n.get("calcul") !== undefined && CalculateApp.libs.webL10n.get("calcul").length > 0) {
  226. CalculateApp.elements.calcInput.placeholder = CalculateApp.libs.webL10n.get("calcul");
  227. }
  228. if (CalculateApp.libs.webL10n.get("label") !== undefined && CalculateApp.libs.webL10n.get("label").length > 0) {
  229. CalculateApp.elements.labelInput.placeholder = CalculateApp.libs.webL10n.get("label");
  230. }
  231. if (CalculateApp.libs.webL10n.get("clear") !== undefined && CalculateApp.libs.webL10n.get("clear").length > 0) {
  232. CalculateApp.elements.calcButtonClear.innerHTML = CalculateApp.libs.webL10n.get("clear");
  233. }
  234. },
  235. /* We clear the result box and display all previous calculations */
  236. displayAllCalculations: function() {
  237. while (CalculateApp.elements.resultsZoneDiv.firstChild) {
  238. CalculateApp.elements.resultsZoneDiv.removeChild(CalculateApp.elements.resultsZoneDiv.firstChild);
  239. }
  240. if(CalculateApp.data.calculations !== null){
  241. for (var i = 0; i < CalculateApp.data.calculations.length; i++) {
  242. CalculateApp.displayCalculation(CalculateApp.data.calculations[i]);
  243. }
  244. }
  245. },
  246. /* We try to patch the equation to handle things like degree to radians conversion */
  247. tryPatchEquation: function(equation) {
  248. if (CalculateApp.data.isRadian) {
  249. return equation;
  250. }
  251. var matchingFunctions = [
  252. "cos",
  253. "sin",
  254. "tan",
  255. "acos",
  256. "asin",
  257. "atan",
  258. "cosh",
  259. "sinh",
  260. "tanh"
  261. ];
  262. for (var i = 0; i < matchingFunctions.length; i++) {
  263. var regex = new RegExp(matchingFunctions[i] + "\\((.+?)\\)", "gi");
  264. var results = equation.match(regex);
  265. if (results) {
  266. for (var j = 0; j < results.length; j++) {
  267. var parenthesisRegex = new RegExp("\\(([^\\)]+)\\)", "gi");
  268. var arrayToReplace = parenthesisRegex.exec(results[j]);
  269. if (arrayToReplace && arrayToReplace.length >= 2) {
  270. equation = equation.replace(arrayToReplace[1], arrayToReplace[1] + " * " + CalculateApp.data.pi + " / 180");
  271. }
  272. }
  273. }
  274. }
  275. return equation;
  276. },
  277. /* We try to patch the equation to make the graph working on both full mode and light mode */
  278. tryPatchGraphEquation: function(question) {
  279. if (!CalculateApp.isFullMode) {
  280. question = question.replace("f(x)=", "");
  281. }
  282. return question;
  283. },
  284. /* Simple toFixed method to format the output with max digits */
  285. toFixed: function(value, precision) {
  286. if (isNaN(value) || (Number(value) === value && value % 1 === 0)) {
  287. return value;
  288. }
  289. var power = Math.pow(10, precision || 0);
  290. return String(Math.round(value * power) / power);
  291. }
  292. };