PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/share/spice/geometry/geometry.js

http://github.com/duckduckgo/zeroclickinfo-spice
JavaScript | 496 lines | 453 code | 25 blank | 18 comment | 33 complexity | dbd9796b5c2b6f2cc6cd088171dc0bf7 MD5 | raw file
Possible License(s): Apache-2.0
  1. (function(env){
  2. "use strict";
  3. function getParameter(query, trigger, count){
  4. var regex, result;
  5. if(!count){
  6. regex = new RegExp("(?:" + trigger + ")\\s*[=:]?\\s*(\\d+(?:[.,]\\d+)?)");
  7. //(?:trigger)\s?[=:]?\s? => matches the name of the formula, following by optional whitespace, = or :, whitespace
  8. //(\d+(?:[.,]\d+)?) => matches the number, as integer or float
  9. result = query.match(regex);
  10. if(result === null) return null; //no match
  11. result = result[1].replace(",", "."); //stores the result as string and replace commas with points
  12. result = parseFloat(result);
  13. return result;
  14. } else {
  15. regex = new RegExp("(?:" + trigger + ")\\s*[=:]?\\s*((?:\\d+(?:[.,]\\d+)?)(?:[,;]?\\s+\\d+(?:[.,]\\d+)?){" + count + "})");
  16. //(?:trigger)\s?[=:]?\s? => matches the name of the formula, following by optional whitespace, = or :, whitespace
  17. //(\d+(?:[.,]\d+)?) => matches the first number, as integer or float
  18. //(?:[,;]?\s+\d+(?:[.,]\d+)?){count} => matches all other numbers, as integer or float
  19. result = query.match(regex);
  20. if(result === null) return null; //no match
  21. result = result[1].split(/\s+/); //split up the numbers
  22. for(var i = 0, l = result.length; i < l; ++i){
  23. result[i].replace(",", ".");
  24. result[i] = parseFloat(result[i]);
  25. }
  26. return result;
  27. }
  28. }
  29. function format(number){
  30. //maximal three decimal places
  31. return parseFloat(number.toFixed(3));
  32. }
  33. function bindHoverPair(formulaNode, svgNode, color){
  34. function enter(){
  35. formulaNode.addClass("hover");
  36. //set the fill/ stroke color for svg nodes
  37. svgNode.each(function(){
  38. this.classList.add("hover");
  39. });
  40. if(svgNode.is(".fill"))
  41. svgNode.css("fill", color);
  42. if(svgNode.is(".stroke"))
  43. svgNode.css("stroke", color);
  44. }
  45. function leave(){
  46. formulaNode.removeClass("hover");
  47. svgNode.each(function(){
  48. this.classList.remove("hover");
  49. });
  50. if(svgNode.is(".fill"))
  51. svgNode.css("fill", "transparent");
  52. if(svgNode.is(".stroke"))
  53. svgNode.css("stroke", "#000");
  54. }
  55. formulaNode = $(formulaNode);
  56. svgNode = $(svgNode);
  57. formulaNode.hover(enter, leave);
  58. svgNode.hover(enter, leave);
  59. }
  60. var formulas = {
  61. volume: {
  62. symbol: "V",
  63. color: "#DE5833"
  64. },
  65. area: {
  66. symbol: "A",
  67. color: "#F1A031"
  68. },
  69. surface: {
  70. symbol: "A",
  71. color: "#F1A031"
  72. },
  73. perimeter: {
  74. symbol: "u",
  75. color: "#5B9E4D"
  76. },
  77. circumference: {
  78. symbol: "u",
  79. color: "#5B9E4D"
  80. },
  81. diagonal: {
  82. symbol: "e",
  83. color: "#4495D4"
  84. }
  85. };
  86. var shapes = {
  87. square: {
  88. formulas: [{
  89. name: "area",
  90. html: "a<sup>2</sup>",
  91. calc: function(a){
  92. return a * a;
  93. }
  94. }, {
  95. name: "perimeter",
  96. html: "4a",
  97. calc: function(a){
  98. return 4 * a;
  99. }
  100. }, {
  101. name: "diagonal",
  102. html: "a&radic;2",
  103. calc: function(a){
  104. return a * Math.SQRT2;
  105. }
  106. }],
  107. svg: [{
  108. path: "M 0,0 h 120 v 120 h -120 z",
  109. class: "fill"
  110. }, {
  111. path: "M 0,0 h 120 m 0,120 h -120 m 120,0 v -120 m -120,0 v 120",
  112. class: "stroke"
  113. }, {
  114. path: "M 0,0 l 120,120",
  115. class: "stroke special"
  116. }],
  117. pairs: {
  118. area: [0, 0],
  119. perimeter: [1, 1],
  120. diagonal: [2, 2]
  121. },
  122. getParameter: function(query){
  123. return getParameter(query, "a|length|size");
  124. },
  125. parameterNames: ["a"]
  126. },
  127. rect: {
  128. formulas: [{
  129. name: "area",
  130. html: "ab",
  131. calc: function(a, b){
  132. return a * b;
  133. }
  134. }, {
  135. name: "perimeter",
  136. html: "2(a+b)",
  137. calc: function(a, b){
  138. return 2 * (a + b);
  139. }
  140. }, {
  141. name: "diagonal",
  142. html: "&radic;(a<sup>2</sup>+b<sup>2</sup>)",
  143. calc: function(a, b){
  144. return Math.sqrt(a * a + b * b);
  145. }
  146. }],
  147. svg: [{
  148. path: "M 0,0 h 160 v 120 h -160 z",
  149. class: "fill"
  150. }, {
  151. path: "M 0,0 h 160 m 0,120 h -160 m 160,0 v -120 m -160,0 v 120",
  152. class: "stroke"
  153. }, {
  154. path: "M 0,0 l 160,120",
  155. class: "stroke special"
  156. }],
  157. pairs: {
  158. area: [0, 0],
  159. perimeter: [1, 1],
  160. diagonal: [2, 2]
  161. },
  162. getParameter: function(query){
  163. var p = getParameter(query, "length|size", 1);
  164. if(p !== null)
  165. return p;
  166. p = [
  167. getParameter(query, "a"),
  168. getParameter(query, "b")
  169. ];
  170. if(p[0] !== null && p[1] !== null)
  171. return p;
  172. return null;
  173. },
  174. parameterNames: ["a", "b"]
  175. },
  176. "equilateral triangle": {
  177. formulas: [{
  178. name: "area",
  179. html: "(a<sup>2</sup>*&radic;3)/4",
  180. calc: function(a){
  181. return a / 4 * Math.sqrt(3);
  182. }
  183. }, {
  184. name: "perimeter",
  185. html: "3a",
  186. calc: function(a){
  187. return a * 3;
  188. }
  189. }],
  190. svg: [{
  191. path: "M 70,0 l 70,120 h -140 z",
  192. class: "fill"
  193. }, {
  194. path: "M 70,0 l 70,120 m -140,0 l 70,-120 m 70,120 h -140",
  195. class: "stroke"
  196. }],
  197. pairs: {
  198. area: [0, 0],
  199. perimeter: [1, 1]
  200. },
  201. getParameter: function(query){
  202. return getParameter(query, "a|length|size");
  203. },
  204. parameterNames: ["a"]
  205. },
  206. circle: {
  207. formulas: [{
  208. name: "area",
  209. html: "&pi;r<sup>2</sup>",
  210. calc: function(r){
  211. return Math.PI * r * r;
  212. }
  213. }, {
  214. name: "circumference",
  215. html: "2&pi;r",
  216. calc: function(r){
  217. return 2 * Math.PI * r;
  218. }
  219. }],
  220. svg: [{
  221. path: "M 0,60 a 25 25 0 0 0 120,0 a 25 25 0 0 0 -120,0",
  222. class: "fill"
  223. }, {
  224. path: "M 0,60 a 25 25 0 0 0 120,0 a 25 25 0 0 0 -120,0",
  225. class: "stroke"
  226. }],
  227. pairs: {
  228. area: [0, 0],
  229. circumference: [1, 1]
  230. },
  231. getParameter: function(query){
  232. var r = getParameter(query, "radius|r");
  233. if(r !== null) return r;
  234. r = getParameter(query, "diameter|d");
  235. if(r !== null) return r / 2;
  236. return null;
  237. },
  238. parameterNames: ["r"]
  239. },
  240. cube: {
  241. formulas: [{
  242. name: "volume",
  243. html: "a<sup>3</sup>",
  244. calc: function(a){
  245. return a * a * a;
  246. }
  247. }, {
  248. name: "surface",
  249. html: "6a<sup>2</sup>",
  250. calc: function(a){
  251. return 6 * a * a;
  252. }
  253. }, {
  254. name: "diagonal",
  255. html: "a&radic;3",
  256. calc: function(a){
  257. return a * Math.sqrt(3);
  258. }
  259. }],
  260. svg: [{
  261. path: "M 0,120 v -80 l 40,-40 h 80 v 80 l -40 40 z",
  262. class: "fill"
  263. }, {
  264. path: "M 0,120 l 40,-40 v -80 v 80 h 80",
  265. class: "stroke backface"
  266. }, {
  267. path: "M 0,40 l 120,40",
  268. class: "stroke special"
  269. }, {
  270. path: "M 0,120 v -80 l 40,-40 h 80 v 80 l -40 40 z",
  271. class: "fill"
  272. }, {
  273. path: "M 0,40 h 80 v 80 h -80 v -80 l 40,-40 h 80 v 80 l -40,40 v -80 l 40,-40",
  274. class: "stroke"
  275. }],
  276. pairs: {
  277. surface: [1, 0],
  278. diagonal: [2, 2],
  279. volume: [0, 3]
  280. },
  281. getParameter: function(query){
  282. return getParameter(query, "a|length|size");
  283. },
  284. parameterNames: ["a"]
  285. },
  286. cuboid: {
  287. formulas: [{
  288. name: "volume",
  289. html: "abc",
  290. calc: function(a, b, c){
  291. return a * b * c;
  292. }
  293. }, {
  294. name: "surface",
  295. html: "2(ab + ac + bc)",
  296. calc: function(a, b, c){
  297. return 2 * (a * b + a * c + b * c);
  298. }
  299. }, {
  300. name: "diagonal",
  301. html: "&radic;(a<sup>2</sup> + b<sup>2</sup> + c<sup>2</sup>)",
  302. calc: function(a, b, c){
  303. return Math.sqrt(a * a + b * b + c * c);
  304. }
  305. }],
  306. svg: [{
  307. path: "M 0,120 v -80 l 40,-40 h 120 v 80 l -40 40 z",
  308. class: "fill"
  309. }, {
  310. path: "M 0,120 l 40,-40 v -80 v 80 h 120",
  311. class: "stroke backface"
  312. }, {
  313. path: "M 0,40 l 160,40",
  314. class: "stroke special"
  315. }, {
  316. path: "M 0,120 v -80 l 40,-40 h 120 v 80 l -40 40 z",
  317. class: "fill"
  318. }, {
  319. path: "M 0,40 h 120 v 80 h -120 v -80 l 40,-40 h 120 v 80 l -40,40 v -80 l 40,-40",
  320. class: "stroke"
  321. }],
  322. pairs: {
  323. surface: [1, 0],
  324. diagonal: [2, 2],
  325. volume: [0, 3]
  326. },
  327. getParameter: function(query){
  328. var p = getParameter(query, "length|size", 2);
  329. if(p !== null) return p;
  330. p = [
  331. getParameter(query, "a"),
  332. getParameter(query, "b"),
  333. getParameter(query, "c")
  334. ];
  335. if(p[0] !== null && p[1] !== null && p[2] !== null)
  336. return p;
  337. return null;
  338. },
  339. parameterNames: ["a", "b", "c"]
  340. },
  341. sphere : {
  342. formulas: [{
  343. name: "volume",
  344. html: "4/3&pi;r<sup>3</sup>",
  345. calc: function(r){
  346. return 4 / 3 * Math.PI * r * r * r;
  347. }
  348. }, {
  349. name: "surface",
  350. html: "4&pi;r<sup>2</sup>",
  351. calc: function(r){
  352. return 4 * Math.PI * r * r;
  353. }
  354. }],
  355. svg: [{
  356. path: "M 0,60 a 25 25 0 0 0 120,0 a 25 25 0 0 0 -120,0",
  357. class: "fill"
  358. }, {
  359. path: "M 0,60 a 30 10 0 0 1 120,0",
  360. class: "stroke backface"
  361. }, {
  362. path: "M 0,60 a 25 25 0 0 0 120,0 a 25 25 0 0 0 -120,0",
  363. class: "fill"
  364. }, {
  365. path: "M 0,60 a 30 10 0 1 0 120,0 a 25 25 0 0 0 -120,0 a 25 25 0 0 0 120,0",
  366. class: "stroke"
  367. }],
  368. pairs: {
  369. volume: [0, 2],
  370. surface: [1, 0]
  371. },
  372. getParameter: function(query){
  373. var r = getParameter(query, "radius|r");
  374. if(r !== null) return r;
  375. r = getParameter(query, "diameter|d");
  376. if(r !== null) return r / 2;
  377. return null;
  378. },
  379. parameterNames: ["r"]
  380. }
  381. };
  382. env.ddg_spice_geometry = function(){
  383. var query = DDG.get_query(),
  384. shape,
  385. data = {},
  386. pairs,
  387. parameter,
  388. i, l,
  389. j, k,
  390. got = false;
  391. for(shape in shapes){
  392. if(query.match(shape)){
  393. got = true;
  394. break;
  395. }
  396. }
  397. if(!got){ //failed, no shape was matched
  398. Spice.failed("geometry");
  399. return;
  400. }
  401. shape = shapes[shape];
  402. data.formulas = shape.formulas;
  403. data.svg = shape.svg;
  404. pairs = shape.pairs;
  405. parameter = shape.getParameter(query);
  406. if(isNumber(parameter)) //force array
  407. parameter = [parameter];
  408. //loop through all formulas of this shape
  409. for(i = 0, l = data.formulas.length; i < l; ++i){
  410. //get the formula symbol, color and format the name
  411. data.formulas[i].symbol = formulas[data.formulas[i].name].symbol;
  412. data.formulas[i].color = formulas[data.formulas[i].name].color;
  413. data.formulas[i].nameCaps = data.formulas[i].name.toUpperCase();
  414. //calc the formula if parameter(s) is / are defined
  415. if(data.parameter !== null)
  416. data.formulas[i].result = format(data.formulas[i].calc.apply(0, parameter));
  417. //if the formula is in the search string, print only this formula
  418. if(query.match(data.formulas[i].name, "i")){
  419. //cleanup formulas
  420. data.formulas = [data.formulas[i]];
  421. //cleanup pairs
  422. for(j = 0, k = pairs.length; j < k; ++j){
  423. //find the right pair
  424. if(pairs[j][0] === i){
  425. pairs = [[0, pairs[j][1]]];
  426. break;
  427. }
  428. }
  429. break;
  430. }
  431. }
  432. //if parameter(s) is / are defined print their values
  433. if(parameter !== null){
  434. data.parameter = [];
  435. for(i = 0, l = parameter.length; i < l; ++i){
  436. data.parameter[i] = {
  437. symbol: shape.parameterNames[i],
  438. value: parameter[i]
  439. };
  440. }
  441. }
  442. // Display the plugin.
  443. Spice.add({
  444. data: data,
  445. id: "geometry",
  446. name: "Geometry",
  447. templates: {
  448. group: "base",
  449. options: {
  450. content: Spice.geometry.content
  451. }
  452. },
  453. onShow: function(){
  454. if(!Modernizr.svg) return;
  455. var formulaNodes = $("#zci--geometry-formulas").children(),
  456. svg = $("#zci--geometry-svg"),
  457. svgNodes = svg.children(),
  458. content = svg.parent();
  459. for(var i in pairs)
  460. bindHoverPair(formulaNodes[pairs[i][0]], svgNodes[pairs[i][1]], formulas[i].color);
  461. //wait for stylesheet
  462. $(window).load(function(){
  463. //set the height of the svg to the same like the content, but 150 as maximal value
  464. svg.height(Math.min(content.height(), 150));
  465. svg.show();
  466. });
  467. }
  468. });
  469. };
  470. }(this));
  471. ddg_spice_geometry();