API Docs for: 0.0.1
Show:

File: lib/graph.js

/*
 * niViz -- snow profiles visualization
 * Copyright (C) 2015 WSL/SLF - Fluelastrasse 11 - 7260 Davos Dorf - Switzerland.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

(function (niviz) {
  'use strict';

  // --- Module Dependencies ---
  var EventEmitter   = niviz.EventEmitter;
  var synchronizable = niviz.synchronizable;

  var util         = niviz.util;
  var inherits     = util.inherits;
  var fonts        = niviz.SVGFonts;

  var emitter = new EventEmitter();

  /** @module niviz */

  /**
   * Snowpack profile graph.
   *
   * @class Graph
   * @constructor
   *
   * @extends EventEmitter
   * @extends Synchronizable
   */
  function Graph() {
    // EventEmitter.call(emitter);
    synchronizable(this);
    this.emitter = emitter;
  }

  inherits(Graph, EventEmitter);

  /**
   * Registry of graph implementations.
   *
   * @property
   * @type Object
   * @static
   */
  Graph.type = {};

  /**
   * Makes the passed-in constructor inherit from
   * Graph and registers it for the given type
   *
   * @method implement
   * @static
   *
   * @param {Function} C The constructor.
   * @param {String} name The name of the graph (unique).
   * @param {String} [type] The type, e. g. 'profile' or 'timeline'.
   * @param {String} [base] The graph from which to inherit.
   *
   * @returns {Function} The extended constructor.
   */
  Graph.implement = function (C, name, type, base) {
    inherits(C, base && Graph.type[base] || Graph);
    Graph.type[name] = C;
    Graph.type[name].genus = type;

    return C;
  };

  /**
   * Request to draw a graph of a certain type. Check whether type is
   * registered and call the constructor upon the graph.
   *
   * @method draw
   * @static
   *
   * @param {String} name The name of the graph (unique).
   * @param {Station} station
   * @param {Object} canvas A svg element that will be used as SnapSVG paper
   * @param {Object} properties Properties to be passed to the graph
   *
   * @returns {Graph} An instance of the graph type
   */
  Graph.draw = function (type, station, canvas, properties) {
    var G = Graph.type[type];

    if (!G) throw new Error('no graph of type ' + type + ' found');

    if ((station.profiles && station.profiles[0]) || station.data) {
      return (new G(station, canvas, properties));
    }
  };

  /**
   * @method draw
   * @abstract
   */
  Graph.prototype.draw = function () {
    throw new Error('not implemented');
  };

  /**
   * Convert a graph into JSON or SVG format.
   *
   * @method convert
   * @static
   *
   * @param {String} type SVG or JSON
   * @param {Graph} object
   *
   * @returns {Object}
   */
  Graph.convert = function (type, object, callback) {
    if (type === 'SVG') {

      if (typeof object.paper.toString === 'function') { //Snap.svg

        var paper = object.paper.clone();
        var removableElements = paper.selectAll('[class=removable]');
        removableElements.items && removableElements.items.forEach(function (n) {
          n.remove();
        });

        paper.attr({viewBox: '0 0 '
          + Math.round(object.canvas.width()) + ' '
          + Math.round(object.canvas.height())});

        var str = paper.toString();
        str = str.replace('<defs>', fonts);
        str = str.replace('Created with Snap', 'Created with niViz on '
                          + moment().format('YYYY-MM-DDThh:mm:ss'));
        str = str.replace(' spinner="processing"', '');
        str = str.replace(' class="body"', '');
        str = str.replace(/(<svg [^>]*)(style="[^"]*" )([^>]*)/, '$1$3');
        str = str.replace(/(<svg [^>]*)(width="[^"]*" )([^>]*)/, '$1$3');
        str = str.replace(/(<svg [^>]*)(height="[^"]*" )([^>]*)/, '$1$3');
        str = str.replace(/(<svg [^>]*)(id="[^"]*" )([^>]*)/, '$1$3');
        str = str.replace(/<svg /, '<svg height="100%" width="100%" ');
        str = str.replace('xmlns="http://www.w3.org/2000/svg"',
          'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"');

        callback(str);
        paper.remove();

      } else {
        throw new Error('graph.js SVG rendering error: is the graph using SnapSVG?');
      }

    } else if (type === 'PNG') {
      if (typeof object.paper.toDataURL === 'function') { //Snap.svg
        var c = window.document.createElement('canvas');
        var att1 = document.createAttribute('id');
        att1.value = 'expcanvas';

        var paper = object.paper.clone();
        var removableElements = paper.selectAll('[class=removable]');
        removableElements.items && removableElements.items.forEach(function (n) {
          n.remove();
        });

        var svg = paper.toString();

        // To make PNG export work in Firefox:
        svg = svg.replace('xmlns="http://www.w3.org/2000/svg"',
          'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"');

        canvg(c, svg, {renderCallback: function (result) {

          /* Draw PNG on a white background if desired */
          var ctx = c.getContext('2d');

          if (niviz.Common.defaults.PNG_background === 'white') {
            ctx.globalCompositeOperation = 'destination-over';
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, c.width, c.height);
          }

          var dataURL = c.toDataURL('image/png');
          var bin = window.atob(dataURL.replace('data:image/png;base64,', ''));

          var ia = new Uint8Array(bin.length);
          for (var i = 0; i < bin.length; i++) {
            ia[i] = bin.charCodeAt(i);
          }
          callback(ia);
          paper.remove();
        }});
      }

    } else if (type === 'JSON') {
      if (object.profile) { // Download current profile only
        var station = new niviz.Station();
        station.name = object.station.name;
        station.id = object.station.id;
        station.position = object.station.position;
        station.observer = object.station.observer;
        station.push(object.profile);

        callback(JSON.stringify(station));
        return;
      }

      callback(JSON.stringify(object.data));

    } else if (type === 'CSV') {
      if (niviz.Meteograph.prototype.isPrototypeOf(object))
        callback(niviz.CSV.serialize(object));
      else
        throw new Error('CSV export only defined for Meteograph objects');
    } else if (type === 'CAAML5') {
      if (object.station && object.station.length && object.profile)
        callback(niviz.CAAML5.serialize(object));
      else
        throw new Error('CAAML5 export only defined for graphs of profiles');
    } else if (type === 'CAAML') {
      if (object.station && object.station.length && object.profile)
        callback(niviz.CAAML.serialize(object));
      else
        throw new Error('CAAML export only defined for graphs of profiles');
    }
  };

  // --- Module Exports ---
  niviz.Graph = Graph;
  niviz.draw = Graph.draw;
  niviz.convert = Graph.convert;

}(niviz));