dvf-2.x-dev/js/jquery.chart_export.js

js/jquery.chart_export.js
(function ($) {

  "use strict";

  /*
   * ChartExport jQuery plugin.
   * ---------------------------
   * This plugin handles exporting a chart to a svg/png format.
   *
   * Usage.
   * ------
   * Bind this to the button you wish to trigger the download of the chart.
   * Eg. $('button).chartExport({svg: 'svg.my-chart', format: 'svg'});
   *
   * Options/settings:
   * - svg: (required) This is either a jQuery object or jQuery selector for the chart.
   * - format: (string) The save as format (svg or png). Defaults to svg
   * - filename: (string) The filename to use when saving, Defaults to 'chart'
   * - includeExportStyles: (bool) Should we inject some styles that fix output bugs. Defaults to true
   * - exportStylesheet: (string) Optional stylesheet to embed in the svg. Requires includeExportStyles = true.
   * - errorMsg: (string) The error message to display if no browser support for downloading.
   */

  /**
   * chartExport class.
   *
   * @param dom
   *   The dom for the triggering element (eg. the button/link).
   * @param settings
   *   Settings for the class. @see defaults.
   * @returns {chartExport}
   *   Return self for chaining.
   */
  var ChartExport = function (dom, settings) {
    var self = this;

    // Defaults.
    self.defaults = {
      // The button this is getting bound to.
      $button: $(dom),
      // Chart SVG selector or jQuery object.
      svg: 'svg',
      // Format to save as.
      format: 'svg',
      // Filename (without extension).
      filename: 'chart',
      // Export as png dimensions. When values are empty, will automatically size.
      width: '',
      height: '',
      // Embed additional styles in svg (fixes display bugs with billboard.js charts)
      includeExportStyles: true,
      exportStylesheets: [],
      // If an export stylesheet is provided, these styles get replaced with the stylesheet content.
      exportStyles: 'svg{font:10px sans-serif}line,path{fill:none;stroke:#000}.bb-bar{stroke:none!important}',
      // Passed Validation requirements.
      valid: true,
      // Error message.
      errorMsg: 'Sorry, your browser does not support this function.',
      // Manual download message.
      manualDownloadMessage: 'Your browser requires manual saving of this file, when redirected to the image, ' +
        'right click and "Save {saveType} As..." to save it to your computer with the filename "{filename}"',
      ieDownloadMessage: 'Right click the graph and select "Save picture as" then in the save dialog, set the ' +
        '"Save as type" to '
    };

    // Update defaults with passed settings.
    self.settings = $.extend(self.defaults, settings);

    /*
     * Validate requirements.
     */
    self.validateRequirements = function () {
      // TODO: Check html5 support.
      try {
        var isFileSaverSupported = !!new Blob;
      } catch (e) {
        self.settings.valid = false;
      }

      // Return self for chaining.
      return self;
    };

    /*
     * Parse the svg object.
     */
    self.parseSvg = function () {
      // Processed class.
      var processedClass = 'chart-export-processed';

      // If svg is not a jQuery object, make it one.
      if (self.settings.svg.jquery === undefined) {
        self.settings.svg = $(self.settings.svg);
      }

      // Ensure our svg is actually an svg (not a wrapper element).
      self.settings.svg = self.settings.svg.is('svg') ? self.settings.svg : self.settings.svg.find('> svg');

      // If no svg, validation failed.
      if (self.settings.svg.length === 0) {
        self.settings.valid = false;
        return self;
      }

      // Don't process the svg more than once.
      if (self.settings.svg.data('processed') === true) {
        return self;
      }

      // Add some XML attributes.
      self.settings.svg
        .attr('version', 1.1)
        .attr('xmlns', 'http://www.w3.org/2000/svg')
        .attr('class', 'chart-export ' + self.settings.svg.attr('class'))
        .data('processed', true);

      // Remove clip path from groups.
      self.settings.svg.find('g').removeAttr('clip-path');

      // Add font family to texts.
      self.settings.svg.find('text').attr('font-family', '\'arial\'');

      // Include billboard.js styles.
      self.includeExportStyles();

      // Return self for chaining.
      return self;
    };

    /*
     * Inject billboard.js styles if required.
     */
    self.includeExportStyles = function () {
      // Check if styles need to be added first.
      if (!self.settings.includeExportStyles) {
        return self;
      }

      // jQuery 1.5+ is required to use exported styles as deferreds are used.
      // See: https://api.jquery.com/jquery.when/
      if (typeof $.when !== 'function') {
        return self;
      }

      // If export stylesheets are provided, we load their content.
      if (self.settings.exportStylesheets.length > 0) {
        var deferreds = [], cssUrl, i;

        // Ensure we don't have any duplicate stylesheets before fetching (a possible result
        // of drupal_add_js running multiple times).
        var cssSheets = self.settings.exportStylesheets.filter(function(element, index, array){
          return array.indexOf(element) >= index;
        });

        // Fetch each stylesheet and append to exportStyles.
        for (i in cssSheets) {
          cssUrl = cssSheets[i];
          deferreds.push(
            $.get(cssUrl, self.appendStyles)
          );
        }

        // After all the sheets are fetched, add them.
        $.when.apply($, deferreds).then(function () {
          self.embedStyles();
        });

      } else {
        // No export stylesheet, use default styles.
        self.embedStyles();
      }

      // Return self for chaining.
      return self;
    };

    /*
     * Callback for joining all external styles together.
     */
    self.appendStyles = function(css) {
      self.settings.exportStyles += css;
    };

    // Embed current export styles.
    self.embedStyles = function () {
      // Create a style element.
      self.$chartStyles = $('<style>')
        .attr('type', 'text/css')
        .html("<![CDATA[\n" + self.settings.exportStyles + "\n]]>")
        .appendTo($('defs', self.settings.svg));
    };

    /*
     * Save as SVG.
     */
    self.saveSVG = function () {
      // Save using blob and filesaver.js.
      var blob = new Blob([self.svgHtml], {type: "image/svg+xml"});
      saveAs(blob, self.getFilename());
    };

    /*
     * Save as PNG.
     */
    self.savePNG = function () {
      // Create a new image and add the svg as a src.
      var image = new Image();

      // Unescape/encodeURIComponent solves issues with non utf-8 strings.
      image.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(self.svgHtml)));

      // On image load, trigger its download with html5 download attr.
      image.onload = function () {
        // Once loaded, turn the image object into a 2d canvas.
        var canvas = document.createElement('canvas'), context, dimensions;
        dimensions = self.getDimensions(image);
        canvas.width = dimensions.width;
        canvas.height = dimensions.height;
        context = canvas.getContext('2d');
        context.drawImage(image, 0, 0, dimensions.width, dimensions.height);

        // Save using canvas-toBlob.js.
        canvas.toBlob(function (blob) {
          saveAs(blob, self.getFilename());
        });
      };
    };

    /*
     * Returns the width and height for the canvas based on settings and AR.
     *
     * @param image
     *   The image with the src populated as the chart.
     */
    self.getDimensions = function (image) {
      // Default to the image width/height.
      var d = {width: image.width, height: image.height}, ar;

      // If overridden we do some additional processing with the settings.
      if (self.settings.width !== '') {
        // Width set, height optional.
        d.width = parseInt(self.settings.width);

        // If no height, we calculate with the aspect ratio.
        if (self.settings.height === '') {
          ar = (image.height / image.width);
          d.height = Math.round((d.width * ar));
        } else {
          // Defined width and height: May result in cropping or poor AR.
          d.height = parseInt(self.settings.height);
        }

      } else if (self.settings.height !== '') {
        // No width, only height defined, calculate width with AR.
        d.height = parseInt(self.settings.height);
        ar = (image.width / image.height);
        d.width = Math.round((d.height * ar));
      }

      // Return the dimensions object.
      return d;
    };

    /*
     * Click action.
     */
    self.bindClick = function (e) {
      e.preventDefault();

      // If not passed validation, clicking the button returns an error msg.
      if (self.settings.valid === false) {
        return alert(self.settings.errorMsg);
      }

      // Safari currently doesn't support filesaver
      // @see https://github.com/eligrey/FileSaver.js/issues/12
      // So as a workaround we instruct the user on how to download.
      if (self.isSafari()) {
        var saveType = 'png' === self.settings.format ? 'Image' : 'Page';
        alert(self.settings.manualDownloadMessage.replace('{filename}', self.getFilename()).replace('{saveType}', saveType));
      }

      // IE doesn't have very good blob support causing save to fail, so show a
      // message instead. IE also has issues with duplicating xmlns and outerHTML.
      // Works fine in EDGE.
      if (self.isIE()) {
        alert(self.settings.ieDownloadMessage + '"' + self.settings.format.toUpperCase() + '"');
        return;
      }

      // Get the html for the svg (emulates cross browser outerHtml).
      self.svgHtml = self.settings.svg[0].outerHTML;

      // Call the save method based on format.
      switch (self.settings.format.toLowerCase()) {
        case 'svg':
          self.saveSVG();
          break;
        case 'png':
          self.savePNG();
          break;
      }
    };

    /*
     * Safari check due to issue with filesaver.
     */
    self.isSafari = function () {
      return navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS');
    };

    /*
     * Internet Explorer check due to issue with blobs (and other things).
     */
    self.isIE = function () {
      var ua = window.navigator.userAgent;
      var msie = ua.indexOf("MSIE ");
      return (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./));
    };

    /*
     * Return a clean filename with extension.
     */
    self.getFilename = function () {
      var cleanFilename = self.settings.filename.replace(/[^a-z0-9_\-]/gi, '_').toLowerCase();
      return cleanFilename + '.' + self.settings.format;
    };

    /*
     * Init the class.
     */
    self.init = function () {
      // Only one export per button.
      if (self.settings.$button.hasClass('chart-export-processed')) {
        return;
      }

      // Prepare requirements and svg.
      self
        .validateRequirements()
        .parseSvg();

      // Bind click and mark as processed.
      self.settings.$button
        .click(self.bindClick)
        .addClass('chart-export-processed');
    };

    // Kick it off on construct.
    self.init();

    // Return self for chaining.
    return self;
  };


  /**
   * jQuery plugin/function.
   */
  $.fn.chartExport = function (settings) {
    return this.each(function (i, dom) {
      new ChartExport(dom, settings)
    });
  };

})(jQuery);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc