cra16/cake-core

View on GitHub
blockly/apps/graph/graph.js

Summary

Maintainability
B
6 hrs
Test Coverage
/**
 * Blockly Apps: Graphing Calculator
 *
 * Copyright 2012 Google Inc.
 * https://blockly.googlecode.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview JavaScript for Blockly's Graphing Calculator application.
 * @author q.neutron@gmail.com (Quynh Neutron)
 */
'use strict';

// Load the Google Chart Tools Visualization API and the chart package.
if (typeof google == 'object') {
  google.load('visualization', '1', {packages: ['corechart']});
} else {
  alert('Unable to load Google\'s chart API.\n' +
        'Are you connected to the Internet?');
}

// Supported languages.
BlocklyApps.LANGUAGES =
    ['ace', 'ar', 'az', 'ca', 'da', 'de', 'el', 'en', 'es', 'fa', 'fr', 'hi',
     'hrx', 'hu', 'is', 'it', 'ko', 'mg', 'ms', 'nl', 'pl', 'pms', 'pt-br',
     'ro', 'ru', 'sco', 'si', 'sr', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-hans',
     'zh-hant'];
BlocklyApps.LANG = BlocklyApps.getLang();

document.write('<script type="text/javascript" src="generated/' +
               BlocklyApps.LANG + '.js"></script>\n');

/**
 * Create a namespace for the application.
 */
var Graph = {};

/**
 * Initialize Blockly and the graph.  Called on page load.
 */
Graph.init = function() {
  BlocklyApps.init();

  var rtl = BlocklyApps.isRtl();
  var blocklyDiv = document.getElementById('blockly');
  var visualization = document.getElementById('visualization');
  var onresize = function(e) {
    var top = visualization.offsetTop;
    blocklyDiv.style.top = Math.max(10, top - window.pageYOffset) + 'px';
    blocklyDiv.style.left = rtl ? '10px' : '420px';
    blocklyDiv.style.width = (window.innerWidth - 440) + 'px';
  };
  window.addEventListener('scroll', function() {
      onresize();
      Blockly.fireUiEvent(window, 'resize');
    });
  window.addEventListener('resize', onresize);
  onresize();

  var toolbox = document.getElementById('toolbox');
  Blockly.inject(document.getElementById('blockly'),
      {path: '../../',
       rtl: rtl,
       toolbox: toolbox});

  var defaultXml =
      '<xml>' +
      '  <block type="graph_set_y" deletable="false" x="85" y="100">' +
      '    <value name="VALUE">' +
      '      <block type="graph_get_x"></block>' +
      '    </value>' +
      '  </block>' +
      '</xml>';
  BlocklyApps.loadBlocks(defaultXml);

  Blockly.mainWorkspace.getCanvas().addEventListener('blocklyWorkspaceChange',
      window.parent.Graph.drawVisualization, false);
};

window.addEventListener('load', Graph.init);

/**
 * Cached copy of the function string.
 * @type !string
 * @private
 */
Graph.oldFormula_ = null;

/**
 * Visualize the graph of y = f(x) using Google Chart Tools.
 * For more documentation on Google Chart Tools, see this linechart example:
 * google-developers.appspot.com/chart/interactive/docs/gallery/linechart
 */
Graph.drawVisualization = function() {
  var tuple = Graph.getFunction();
  var defs = tuple[0];
  var formula = tuple[1];
  if (formula === Graph.oldFormula_) {
    // No change in the formula, don't recompute.
    return;
  }
  Graph.oldFormula_ = formula;

  // Create and populate the data table.
  var data = google.visualization.arrayToDataTable(Graph.plot(defs + formula));

  var options = { //curveType: "function",
                  width: 400, height: 400,
                  chartArea: {left: '10%', width: '85%', height: '85%'}
                };

  // Create and draw the visualization, passing in the data and options.
  new google.visualization.LineChart(document.getElementById('visualization')).
      draw(data, options);
  var funcText = document.getElementById('funcText');
  if (funcText.lastChild) {
    funcText.removeChild(funcText.lastChild);
  }
  // Remove the ";" generally ending the JavaScript statement y = {code};.
  formula = formula.replace(/;$/, '');
  funcText.appendChild(document.createTextNode('y = ' + formula));
};

/**
 * Plot points on the function y = f(x).
 * @param {string} code JavaScript code.
 * @return {!Array.<!Array>} 2D Array of points on the graph.
 */
Graph.plot = function(code) {
  // Initialize a table with two column headings.
  var table = [];
  var y;
  // TODO: Improve range and scale of graph.
  for (var x = -10; x <= 10; x = Math.round((x + 0.1) * 10) / 10) {
    try {
      y = eval(code);
    } catch (e) {
      y = NaN;
    }
    if (!isNaN(y)) {
      // Prevent y from being displayed inconsistently, some in decimals, some
      // in scientific notation, often when y has accumulated rounding errors.
      y = Math.round(y * Math.pow(10, 14)) / Math.pow(10, 14);
      table.push([x, y]);
    }
  }
  // Add column heading to table.
  if (table.length) {
    table.unshift(['x', 'y']);
  } else {
    // If the table is empty, add a [0, 0] row to prevent graph error.
    table.unshift(['x', 'y'], [0, 0]);
  }
  return table;
};

/**
 * Get from blocks the right hand side content of the function y = f(x).
 * @return {!Array.<string>} Tuple of any function definitions and the formula
 *     in JavaScipt for f(x).
 */
Graph.getFunction = function() {
  var topBlocks = Blockly.mainWorkspace.getTopBlocks(false);
  var yBlock;
  // Set yBlock to only the code plugged into 'graph_set_y'.
  for (var j = 0; j < topBlocks.length; j++) {
    if (topBlocks[j].type == 'graph_set_y') {
      yBlock = topBlocks[j];
    }
  }
  if (!yBlock) {
    return NaN;
  }
  Blockly.JavaScript.init();
  var code = Blockly.JavaScript.blockToCode(yBlock);
  var defs = Blockly.JavaScript.finish('');
  return [defs, code];
};