<commit_before>'use strict';

/**
 * Plots the supplied GraphData to the screen.
 */
class GraphPlotter {
  /** @param {DataGraph} graph */
  constructor(graph) {
    if (!(graph instanceof GraphData)) {
      throw new TypeError(
          `A GraphData object must be supplied. Received: ${typeof graph}`);
    }
    /** @private @const {GraphData} */
    this.graph_ = graph;
    /** @private @const {Object} margins Creates room for labels and axis. */
    this.margins_ = {
      top: 15,
      right: 200,
      left: 80,
      bottom: 50
    };
    /** @private {number} */
    this.canvasHeight_ = 400;
    /** @private {number} */
    this.canvasWidth_ = 750;
    /** @private {number} */
    this.chartWidth_ =
      this.canvasWidth_ - this.margins_.left - this.margins_.right;
    /** @private {number} */
    this.chartHeight_ =
      this.canvasHeight_ - this.margins_.top - this.margins_.bottom;
    /** @private {Object} */
    this.background_ = d3.select('#canvas').append('svg:svg')
        .attr('width', this.canvasWidth_)
        .attr('height', this.canvasHeight_);
    /** @private {Object} */
    this.chart_ = this.background_.append('g')
        .attr('transform', `translate(${this.margins_.left},
                  ${this.margins_.top})`);
    /** @private {Object} */
    this.legend_ = this.createLegend_();
    /** @private {Object} */
    this.scaleForXAxis_ = this.setXAxisScale_();
    /** @private {Object} */
    this.scaleForYAxis_ = this.setYAxisScale_();
    /** @private {Object} */
    this.createAxis_();
    /** @private {Object} */
    this.labelAxis_();
  }

  setXAxisScale_() {
    return d3.scaleLinear()
        .domain([0, this.graph_.xAxisMax()])
        .range([0, this.chartWidth_]);
  }

  setYAxisScale_() {
    return d3.scaleLinear()
        .domain([this.graph_.yAxisMax(), 0])
        .range([0, this.chartHeight_]);
  }

  createLegend_() {
    return this.chart_.append('g')
        .attr('class', 'legend')
        .attr('transform',
            `translate(${this.chartWidth_}, ${this.margins_.top})`);
  }

  createAxis_() {
    this.chart_.append('g')
        .call(d3.axisBottom(this.scaleForXAxis_))
        .attr('transform', `translate(0, ${this.chartHeight_})`);

    this.chart_.append('g')
        .call(d3.axisLeft(this.scaleForYAxis_));
  }

  labelAxis_() {
    this.chart_.append('text')
        .attr('transform', `translate(${this.chartWidth_ / 2},
            ${this.canvasHeight_ - this.margins_.bottom / 2})`)
        .attr('text-anchor', 'middle')
        .text(this.graph_.xAxis());

    this.chart_.append('text')
        .attr('transform', 'rotate(-90)')
        .attr('y', 0 - (this.margins_.left / 2))
        .attr('x', 0 - (this.chartHeight_ / 2))
        .attr('text-anchor', 'middle')
        .text(this.graph_.yAxis());
  }

  /**
   * Draws a line plot to the canvas. If there are multiple dataSources it will
   * plot them both and label their colors in the legend.
   */
  linePlot() {
    const lineF = d3.line()
        .x(datum => this.scaleForXAxis_(datum.x))
        .y(datum => this.scaleForYAxis_(datum.y))
        .curve(d3.curveMonotoneX);

    this.graph_.dataSources.forEach(({ data, color, key }, index) => {
      this.chart_.selectAll('.dot')
          .data(data)
          .enter()
          .append('circle')
          .attr('cx', datum => this.scaleForXAxis_(datum.x))
          .attr('cy', datum => this.scaleForYAxis_(datum.y))
          .attr('r', 3)
          .attr('fill', color);
      this.chart_.append('path')
          .datum(data)
          .attr('d', lineF)
          .attr('stroke', color)
          .attr('fill', 'none')
          .attr('stroke-width', 2)
          .attr('data-legend', key);
      this.legend_.append('text')
          .text(key)
          .attr('y', index + 'em')
          .attr('fill', color);
    });
  }
}
<commit_msg>Create ability to zoom on line charts.<commit_after>'use strict';

/**
 * Plots the supplied GraphData to the screen.
 */
class GraphPlotter {
  /** @param {DataGraph} graph */
  constructor(graph) {
    if (!(graph instanceof GraphData)) {
      throw new TypeError(
          `A GraphData object must be supplied. Received: ${typeof graph}`);
    }
    /** @private @const {GraphData} */
    this.graph_ = graph;
    /** @private @const {Object} margins Creates room for labels and axis. */
    this.margins_ = {
      top: 15,
      right: 200,
      left: 80,
      bottom: 50,
    };
    /** @private {number} */
    this.canvasHeight_ = 720;
    /** @private {number} */
    this.canvasWidth_ = 1280;
    /** @private {number} */
    this.chartWidth_ =
      this.canvasWidth_ - this.margins_.left - this.margins_.right;
    /** @private {number} */
    this.chartHeight_ =
      this.canvasHeight_ - this.margins_.top - this.margins_.bottom;
    /** @private {Object} */
    this.background_ = d3.select('#canvas').append('svg:svg')
        .attr('width', this.canvasWidth_)
        .attr('height', this.canvasHeight_);
    /** @private {Object} */
    this.chart_ = this.background_.append('g')
        .attr('transform', `translate(${this.margins_.left},
                  ${this.margins_.top})`);
    // Appending a clip path rectangle prevents any drawings with the
    // clip-path: url(#plot-clip) attribute from being displayed outside
    // of the chart area.
    this.chart_.append('defs')
        .append('clipPath')
        .attr('id', 'plot-clip')
        .append('rect')
        .attr('width', this.chartWidth_)
        .attr('height', this.chartHeight_);
    /** @private {Object} */
    this.legend_ = this.createLegend_();
    /** @private {Object} */
    this.scaleForXAxis_ = this.createXAxisScale_();
    /** @private {Object} */
    this.scaleForYAxis_ = this.createYAxisScale_();
    /** @private {Object} */
    this.xAxisGenerator_ = d3.axisBottom(this.scaleForXAxis_);
    /** @private {Object} */
    this.yAxisGenerator_ = d3.axisLeft(this.scaleForYAxis_);
    // Draw the x-axis.
    this.chart_.append('g')
        .call(this.xAxisGenerator_)
        .attr('transform', `translate(0, ${this.chartHeight_})`);
    /** @private {Object} Keep a reference to y-axis for updating on zoom */
    this.yAxisDrawing_ = this.chart_.append('g')
        .call(this.yAxisGenerator_);
    this.labelAxis_();
  }

  createXAxisScale_() {
    return d3.scaleLinear()
        .domain([0, this.graph_.xAxisMax()])
        .range([0, this.chartWidth_]);
  }

  createYAxisScale_() {
    return d3.scaleLinear()
        .domain([this.graph_.yAxisMax(), 0])
        .range([0, this.chartHeight_]);
  }

  createLegend_() {
    return this.chart_.append('g')
        .attr('class', 'legend')
        .attr('transform',
            `translate(${this.chartWidth_}, ${this.margins_.top})`);
  }

  labelAxis_() {
    this.chart_.append('text')
        .attr('transform', `translate(${this.chartWidth_ / 2},
            ${this.canvasHeight_ - this.margins_.bottom / 2})`)
        .attr('text-anchor', 'middle')
        .text(this.graph_.xAxis());

    this.chart_.append('text')
        .attr('transform', 'rotate(-90)')
        .attr('y', 0 - (this.margins_.left / 2))
        .attr('x', 0 - (this.chartHeight_ / 2))
        .attr('text-anchor', 'middle')
        .text(this.graph_.yAxis());
  }

  /**
   * Draws a line plot to the canvas. If there are multiple dataSources it will
   * plot them both and label their colors in the legend.
   */
  linePlot() {
    const pathGenerator = d3.line()
        .x(datum => this.scaleForXAxis_(datum.x))
        .y(datum => this.scaleForYAxis_(datum.y))
        .curve(d3.curveMonotoneX);

    this.graph_.dataSources.forEach(({ data, color, key }, index) => {
      this.chart_.selectAll('.dot')
          .data(data)
          .enter()
          .append('circle')
          .attr('cx', datum => this.scaleForXAxis_(datum.x))
          .attr('cy', datum => this.scaleForYAxis_(datum.y))
          .attr('r', 3)
          .attr('fill', color)
          .attr('class', 'line-dot')
          .attr('clip-path', 'url(#plot-clip)');
      this.chart_.append('path')
          .datum(data)
          .attr('d', pathGenerator)
          .attr('stroke', color)
          .attr('fill', 'none')
          .attr('stroke-width', 2)
          .attr('data-legend', key)
          .attr('class', 'line-plot')
          .attr('clip-path', 'url(#plot-clip)');
      this.legend_.append('text')
          .text(key)
          .attr('y', index + 'em')
          .attr('fill', color);
    });
    const zoom = d3.zoom();
    this.setUpZoom_(zoom);
    this.setUpZoomReset_(zoom);
  }

  transformLinePlot_(yAxisScale) {
    const pathGenerator = d3.line()
        .x(d => this.scaleForXAxis_(d.x))
        .y(d => yAxisScale(d.y))
        .curve(d3.curveMonotoneX);
    this.yAxisGenerator_.scale(yAxisScale);
    this.yAxisDrawing_.call(this.yAxisGenerator_);
    this.chart_.selectAll('.line-dot')
        .attr('cx', datum => this.scaleForXAxis_(datum.x))
        .attr('cy', datum => yAxisScale(datum.y));
    this.chart_.selectAll('.line-plot')
        .attr('d', pathGenerator);
  }

  setUpZoom_(zoom) {
    const onZoom = () => {
      const transform = d3.event.transform;
      const transformedScaleForYAxis = transform.rescaleY(this.scaleForYAxis_);
      this.transformLinePlot_(transformedScaleForYAxis);
    };
    zoom.on('zoom', onZoom).scaleExtent([1, Infinity]);
    // The following invisible rectangle is there just to catch
    // mouse events for zooming. It's not possible to listen on
    // the chart itself because it is a g element (which does not
    // capture mouse events).
    this.chart_.append('rect')
        .attr('width', this.chartWidth_)
        .attr('height', this.chartHeight_)
        .attr('class', 'zoom-listener')
        .style('opacity', 0)
        .call(zoom);
  }

  setUpZoomReset_(zoom) {
    // Gives some padding between the x-axis and the reset button.
    const padding = 10;
    const resetButton = this.chart_.append('svg')
        .attr('width', '10em')
        .attr('height', '2em')
        .attr('x', `${this.chartWidth_ + padding}px`)
        .attr('y', `${this.chartHeight_}px`)
        .attr('cursor', 'pointer');
    // Styling for the button.
    resetButton.append('rect')
        .attr('rx', '5px')
        .attr('ry', '5px')
        .attr('width', '100%')
        .attr('height', '100%')
        .attr('fill', '#1b39a8');
    resetButton.append('text')
        .text('RESET CHART')
        .attr('x', '50%')
        .attr('y', '50%')
        .attr('fill', 'white')
        .attr('text-anchor', 'middle')
        .attr('dominant-baseline', 'middle');
    resetButton
        .on('mouseover', () => {
          resetButton.attr('opacity', '0.5');
        })
        .on('mouseout', () => {
          resetButton.attr('opacity', '1');
        })
        .on('click', () => {
          resetButton.attr('opacity', '1');
          this.chart_.select('.zoom-listener')
              .call(zoom.transform, d3.zoomIdentity);
          this.transformLinePlot_(this.scaleForYAxis_);
        });
  }
}
<|endoftext|>


<commit_before>import { Colors } from "@blueprintjs/core";

export default {
    dark: {
        backgroundColor: Colors.DARK_GRAY4,
        backgroundFrameColor: Colors.DARK_GRAY2,
        listItemHover: Colors.DARK_GRAY2,
        codeBlock: Colors.DARK_GRAY2,
        codeAccent: Colors.GREEN5
    },
    light: {
        backgroundColor: Colors.WHITE,
        backgroundFrameColor: Colors.GRAY5,
        listItemHover: Colors.LIGHT_GRAY3,
        codeBlock: Colors.LIGHT_GRAY1,
        codeAccent: Colors.GREEN1
    }
};
<commit_msg>Add UI themes<commit_after>import { Colors } from "@blueprintjs/core";

export default {
    dark: {
        backgroundColor: Colors.DARK_GRAY4,
        backgroundFrameColor: Colors.DARK_GRAY2,
        listItemHover: Colors.DARK_GRAY2,
        codeBlock: Colors.DARK_GRAY2,
        codeAccent: Colors.GREEN5,
        vault: {
            list: {
                focusedBackgroundColor: Colors.DARK_GRAY5,
                selectedBackgroundColor: Colors.TURQUOISE3,
                selectedTextColor: "#fff"
            },
            colors: {
                divider: Colors.DARK_GRAY5,
                paneDivider: Colors.GRAY3,
                uiBackground: Colors.DARK_GRAY4,
                mainPaneBackground: Colors.DARK_GRAY3
            },
            tree: {
                selectedBackgroundColor: Colors.DARK_GRAY5,
                hoverBackgroundColor: "transparent",
                selectedTextColor: Colors.LIGHT_GRAY5,
                selectedIconColor: Colors.LIGHT_GRAY5
            },
            entry: {
                primaryContainer: Colors.DARK_GRAY3,
                separatorTextColor: Colors.GRAY3,
                separatorBorder: Colors.GRAY1,
                fieldHoverBorder: Colors.GRAY1
            }
        }
    },
    light: {
        backgroundColor: Colors.WHITE,
        backgroundFrameColor: Colors.GRAY5,
        listItemHover: Colors.LIGHT_GRAY3,
        codeBlock: Colors.LIGHT_GRAY1,
        codeAccent: Colors.GREEN1,
        vault: {
            list: {
                focusedBackgroundColor: Colors.LIGHT_GRAY5,
                selectedBackgroundColor: Colors.TURQUOISE3,
                selectedTextColor: "#fff"
            },
            colors: {
                divider: Colors.LIGHT_GRAY4,
                paneDivider: Colors.GRAY3,
                uiBackground: "#fff",
                mainPaneBackground: Colors.LIGHT_GRAY5
            },
            tree: {
                selectedBackgroundColor: Colors.LIGHT_GRAY2,
                hoverBackgroundColor: "transparent",
                selectedTextColor: Colors.DARK_GRAY1,
                selectedIconColor: Colors.DARK_GRAY5
            },
            entry: {
                primaryContainer: Colors.LIGHT_GRAY5,
                separatorTextColor: Colors.GRAY3,
                separatorBorder: Colors.LIGHT_GRAY2,
                fieldHoverBorder: Colors.LIGHT_GRAY1
            }
        }
    }
};
<|endoftext|>


<commit_before>/*!
 * Socialite v2.0 - Plain Twitter Extension
 * Copyright (c) 2013 Dan Drinkard
 * Dual-licensed under the BSD or MIT licenses: http://socialitejs.com/license.txt
 *
 */
(function(window, document, Socialite, undefined)
{
    // https://dev.twitter.com/docs/intents/events/
    var twitterActivate = function(instance)
    {
        if (window.twttr && typeof window.twttr.widgets === 'object' && typeof window.twttr.widgets.load === 'function') {
            window.twttr.widgets.load();
        }

        if (getParams(instance)['show-counts']) {
            appendStyleSheet()
            twitterGetCount(instance);
        };
    },

    twitterGetCount = function(instance, options) {
        var params = getParams(instance),
            counturl = params['counturl'] || params['url'],
            callback = 'socialiteTwitter' + instance.uid,
            endpoint = "https://cdn.api.twitter.com/1/urls/count.json?url=" + counturl + "&callback=" + callback,
            options = options || {},
            scriptTag = document.createElement('script');
            scriptTag.src = endpoint;
            document.getElementsByTagName('script')[0].insertBefore(scriptTag);
            window[callback] = function(data){
                instance.el.querySelectorAll('.counter')[0].innerHTML = data.count;
                delete window[callback];
                scriptTag.parentNode.removeChild(scriptTag);
            }
    },

    getParams = function(instance) {
        return parseQS(Socialite.getDataAttributes(instance.el, true));
    },

    parseQS = function(qs) {
        var hsh = {},
            parts = qs.split(/[&=]/);
        for(var i in parts) {

            if  (i%2) {
                hsh[parts[i-1]] = decodeURIComponent(parts[i]);
            };
        };
        return hsh;
    },

    appendStyleSheet = function() {
        (document.querySelectorAll('style.custom-count-widgets').length || (function(){
            var styleTag = document.createElement('style');
            styleTag.className = 'custom-count-widgets';
            styleTag.innerHTML = '.socialite .counter {position:relative; margin-left: 5px; vertical-align: middle; display:inline-block; min-width:6px; border:1px solid #e6e5e3; padding:6px 7px; font-family:"helvetica neue", helvetica, arial, sans-serif; font-size:13px; line-height:1.2; color:#222; text-decoration: none; /*border-radius: 2px;*/ }' +
                                 '.socialite .counter:before { content:""; position:absolute; left: -5px; top: 10px; background:#fff; border:1px solid #e6e5e3; border-right:none; border-top:none; display:block; width:7px; height:6px; -webkit-transform: rotate(61deg) skewX(35deg); -moz-transform: rotate(61deg) skewX(35deg); -o-transform: rotate(61deg) skewX(35deg); -ms-transform: rotate(61deg) skewX(35deg); transform: rotate(61deg) skewX(35deg); }' +
                                 '.socialite img { vertical-align: middle; }';
            document.getElementsByTagName('head')[0].appendChild(styleTag);
        })());
    }

    Socialite.widget('twitter', 'simple',   {
        init: function(instance){
            var el = document.createElement('a'),
                href = "//twitter.com/intent/tweet?";
            el.className = instance.widget.name;
            Socialite.copyDataAttributes(instance.el, el);
            href += Socialite.getDataAttributes(el, true);
            el.setAttribute('href', href);
            el.setAttribute('data-lang', instance.el.getAttribute('data-lang') || Socialite.settings.twitter.lang);
            if (instance.el.getAttribute('data-image')) {
                var imgTag = document.createElement('img');
                imgTag.src = instance.el.getAttribute('data-image');
                el.appendChild(imgTag);
            }
            if (instance.el.getAttribute('data-show-counts')) {
                var counterTag = document.createElement('a')
                counterTag.className = 'counter';
                counterTag.innerHTML = '…';
                counterTag.href="https://twitter.com/search?q=" + instance.el.getAttribute('data-url');
                el.appendChild(counterTag);
            }
            instance.el.appendChild(el);
        },
        activate: twitterActivate
    });

})(window, window.document, window.Socialite);

<commit_msg>probably should mind my manners wrt callbacks<commit_after>/*!
 * Socialite v2.0 - Plain Twitter Extension
 * Copyright (c) 2013 Dan Drinkard
 * Dual-licensed under the BSD or MIT licenses: http://socialitejs.com/license.txt
 *
 */
(function(window, document, Socialite, undefined)
{
    // https://dev.twitter.com/docs/intents/events/
    var twitterActivate = function(instance)
    {
        if (window.twttr && typeof window.twttr.widgets === 'object' && typeof window.twttr.widgets.load === 'function') {
            window.twttr.widgets.load();
        }

        if (getParams(instance)['show-counts']) {
            appendStyleSheet()
            twitterGetCount(instance);
        };
    },

    twitterGetCount = function(instance, options) {
        var params = getParams(instance),
            counturl = params['counturl'] || params['url'],
            callback = 'socialiteTwitterSimple' + instance.uid,
            endpoint = "https://cdn.api.twitter.com/1/urls/count.json?url=" + counturl + "&callback=" + callback,
            options = options || {},
            scriptTag = document.createElement('script');
            scriptTag.src = endpoint;
            document.getElementsByTagName('script')[0].insertBefore(scriptTag);
            window[callback] = function(data){
                instance.el.querySelectorAll('.counter')[0].innerHTML = data.count;
                delete window[callback];
                scriptTag.parentNode.removeChild(scriptTag);
            }
    },

    getParams = function(instance) {
        return parseQS(Socialite.getDataAttributes(instance.el, true));
    },

    parseQS = function(qs) {
        var hsh = {},
            parts = qs.split(/[&=]/);
        for(var i in parts) {

            if  (i%2) {
                hsh[parts[i-1]] = decodeURIComponent(parts[i]);
            };
        };
        return hsh;
    },

    appendStyleSheet = function() {
        (document.querySelectorAll('style.custom-count-widgets').length || (function(){
            var styleTag = document.createElement('style');
            styleTag.className = 'custom-count-widgets';
            styleTag.innerHTML = '.socialite .counter {position:relative; margin-left: 5px; vertical-align: middle; display:inline-block; min-width:6px; border:1px solid #e6e5e3; padding:6px 7px; font-family:"helvetica neue", helvetica, arial, sans-serif; font-size:13px; line-height:1.2; color:#222; text-decoration: none; /*border-radius: 2px;*/ }' +
                                 '.socialite .counter:before { content:""; position:absolute; left: -5px; top: 10px; background:#fff; border:1px solid #e6e5e3; border-right:none; border-top:none; display:block; width:7px; height:6px; -webkit-transform: rotate(61deg) skewX(35deg); -moz-transform: rotate(61deg) skewX(35deg); -o-transform: rotate(61deg) skewX(35deg); -ms-transform: rotate(61deg) skewX(35deg); transform: rotate(61deg) skewX(35deg); }' +
                                 '.socialite img { vertical-align: middle; }';
            document.getElementsByTagName('head')[0].appendChild(styleTag);
        })());
    }

    Socialite.widget('twitter', 'simple',   {
        init: function(instance){
            var el = document.createElement('a'),
                href = "//twitter.com/intent/tweet?";
            el.className = instance.widget.name;
            Socialite.copyDataAttributes(instance.el, el);
            href += Socialite.getDataAttributes(el, true);
            el.setAttribute('href', href);
            el.setAttribute('data-lang', instance.el.getAttribute('data-lang') || Socialite.settings.twitter.lang);
            if (instance.el.getAttribute('data-image')) {
                var imgTag = document.createElement('img');
                imgTag.src = instance.el.getAttribute('data-image');
                el.appendChild(imgTag);
            }
            if (instance.el.getAttribute('data-show-counts')) {
                var counterTag = document.createElement('a')
                counterTag.className = 'counter';
                counterTag.innerHTML = '…';
                counterTag.href="https://twitter.com/search?q=" + instance.el.getAttribute('data-url');
                el.appendChild(counterTag);
            }
            instance.el.appendChild(el);
        },
        activate: twitterActivate
    });

})(window, window.document, window.Socialite);

<|endoftext|>


<commit_before>// @cliDescription  Generate a new React Native project with Ignite CLI.
// @cliAlias n
// ----------------------------------------------------------------------------
const isIgniteDirectory = require('../lib/isIgniteDirectory')
const exitCodes = require('../lib/exitCodes')
const path = require('path')
const header = require('../brand/header')
const addEmptyBoilerplate = require('../lib/addEmptyBoilerplate')
const { forEach, keys, reduce, concat, trim, isEmpty, match, not } = require('ramda')

/**
 * Creates a new ignite project based on an optional boilerplate.
 *
 * @param {any} context - The gluegun context.
 */
async function command (context) {
  const { parameters, strings, print, system, filesystem, ignite } = context
  const { isBlank, upperFirst, camelCase } = strings
  const { log } = ignite

  // grab the project name
  const projectName = parameters.second

  // check for kebabs
  const isKebabCase = not(isEmpty(match(/.-/g, projectName || '')))

  // camelCase the project name for user example
  const projectNameCamel = upperFirst(camelCase(projectName))

  // Guard against `ignite new ignite`
  if (projectNameCamel === 'ignite') {
    context.print.error('Crossing the streams is bad! Please use another name for your project.')
    process.exit(exitCodes.PROJECT_NAME)
  }

  // ensure we're in a supported directory
  if (isIgniteDirectory(process.cwd())) {
    context.print.error('The `ignite new` command cannot be run within an already ignited project.')
    process.exit(exitCodes.NOT_IGNITE_PROJECT)
  }

  // prevent installing when node_modules/react-native exists
  if (filesystem.exists('node_modules/react-native')) {
    context.print.error('The `ignite new` command cannot be run within a directory with `node_modules/react-native` installed.')
    context.print.error('Try installing from a directory without a `node_modules` directory.')
    process.exit(exitCodes.EXISTING_REACT_NATIVE)
  }

  // verify the project name is a thing
  if (isBlank(projectName)) {
    print.info(`${context.runtime.brand} new <projectName>\n`)
    print.error('Project name is required')
    process.exit(exitCodes.PROJECT_NAME)
  }

  // verify the project name isn't kebab cased
  if (isKebabCase) {
    print.error(`Please use camel case for your project name. Ex: ${projectNameCamel}`)
    process.exit(exitCodes.PROJECT_NAME)
  }

  // verify the directory doesn't exist already
  if (filesystem.exists(projectName) === 'dir') {
    print.error(`Directory ${projectName} already exists.`)
    process.exit(exitCodes.DIRECTORY_EXISTS)
  }

  // print a header
  header()
  print.newline()
  print.info(`🔥 igniting app ${print.colors.yellow(projectName)}`)

  // skip the boilerplate?
  // NOTE(steve): this expression is intentionally evaluating against false because of
  // --no-boilerplate and how minimist works.
  if (parameters.options.boilerplate === false) {
    addEmptyBoilerplate(context)
    return
  }

  // make & jump into the project directory
  log(`making directory ${projectName}`)
  filesystem.dir(projectName)
  process.chdir(projectName)
  log(`switched directory to ${process.cwd()}`)

  // make a temporary package.json file so node stops walking up the diretories
  // NOTE(steve): a lot of pain went into this 1 function call
  filesystem.write('package.json', {
    name: 'ignite-shim',
    description: 'A temporary package.json created to prevent node from wandering too far.',
    repository: 'infinitered/ignite',
    license: 'MIT'
  })

  // grab the right boilerplate
  const cliBoilerplate = parameters.options.boilerplate || parameters.options.b
  const boilerplateName = strings.isBlank(cliBoilerplate) ? 'ir-boilerplate-2016' : cliBoilerplate

  // pick the inbound cli options
  const cliOpts = parameters.options

  // turn this back into a string
  const forwardingOptions = trim(reduce((src, k) => {
    const v = cliOpts[k]
    return concat(v === true ? `--${k} ` : `--${k} ${v} `, src)
  })('', keys(cliOpts)))

  // let's kick off the template
  let ok = false
  try {
    const command = trim(`ignite boilerplate-install ${boilerplateName} ${projectName} ${forwardingOptions}`)
    log(`running boilerplate: ${command}`)
    await system.exec(command, { stdio: 'inherit' })
    log('finished boilerplate')
    ok = true
  } catch (e) {
    log('error running boilerplate')
    log(e)
  }

  // always clean up the app-template stuff
  log('cleaning temporary files')
  filesystem.remove('node_modules')
  filesystem.remove('ignite')
  filesystem.remove('package.json')

  // did we install everything successfully?
  if (ok) {
    log(`moving contents of ${projectName} into place`)
    // move everything that's 1 deep back up to here
    forEach(
      filename => filesystem.move(path.join(projectName, filename), filename)
      , filesystem.list(projectName) || []
    )
    log(`removing unused sub directory ${projectName}`)
    filesystem.remove(projectName)
  }
  log('finished running new')
}

module.exports = command
<commit_msg>Lowercase project name and check for ignite naming<commit_after>// @cliDescription  Generate a new React Native project with Ignite CLI.
// @cliAlias n
// ----------------------------------------------------------------------------
const isIgniteDirectory = require('../lib/isIgniteDirectory')
const exitCodes = require('../lib/exitCodes')
const path = require('path')
const header = require('../brand/header')
const addEmptyBoilerplate = require('../lib/addEmptyBoilerplate')
const { forEach, keys, reduce, concat, trim, isEmpty, match, not, toLower } = require('ramda')

/**
 * Creates a new ignite project based on an optional boilerplate.
 *
 * @param {any} context - The gluegun context.
 */
async function command (context) {
  const { parameters, strings, print, system, filesystem, ignite } = context
  const { isBlank, upperFirst, camelCase } = strings
  const { log } = ignite

  // grab the project name
  const projectName = parameters.second

  // check for kebabs
  const isKebabCase = not(isEmpty(match(/.-/g, projectName || '')))

  // camelCase the project name for user example
  const projectNameCamel = upperFirst(camelCase(projectName))

  // Guard against `ignite new ignite`
  if (toLower(projectName) === 'ignite') {
    context.print.error('Crossing the streams is bad! Please use another name for your project.')
    process.exit(exitCodes.PROJECT_NAME)
  }

  // ensure we're in a supported directory
  if (isIgniteDirectory(process.cwd())) {
    context.print.error('The `ignite new` command cannot be run within an already ignited project.')
    process.exit(exitCodes.NOT_IGNITE_PROJECT)
  }

  // prevent installing when node_modules/react-native exists
  if (filesystem.exists('node_modules/react-native')) {
    context.print.error('The `ignite new` command cannot be run within a directory with `node_modules/react-native` installed.')
    context.print.error('Try installing from a directory without a `node_modules` directory.')
    process.exit(exitCodes.EXISTING_REACT_NATIVE)
  }

  // verify the project name is a thing
  if (isBlank(projectName)) {
    print.info(`${context.runtime.brand} new <projectName>\n`)
    print.error('Project name is required')
    process.exit(exitCodes.PROJECT_NAME)
  }

  // verify the project name isn't kebab cased
  if (isKebabCase) {
    print.error(`Please use camel case for your project name. Ex: ${projectNameCamel}`)
    process.exit(exitCodes.PROJECT_NAME)
  }

  // verify the directory doesn't exist already
  if (filesystem.exists(projectName) === 'dir') {
    print.error(`Directory ${projectName} already exists.`)
    process.exit(exitCodes.DIRECTORY_EXISTS)
  }

  // print a header
  header()
  print.newline()
  print.info(`🔥 igniting app ${print.colors.yellow(projectName)}`)

  // skip the boilerplate?
  // NOTE(steve): this expression is intentionally evaluating against false because of
  // --no-boilerplate and how minimist works.
  if (parameters.options.boilerplate === false) {
    addEmptyBoilerplate(context)
    return
  }

  // make & jump into the project directory
  log(`making directory ${projectName}`)
  filesystem.dir(projectName)
  process.chdir(projectName)
  log(`switched directory to ${process.cwd()}`)

  // make a temporary package.json file so node stops walking up the diretories
  // NOTE(steve): a lot of pain went into this 1 function call
  filesystem.write('package.json', {
    name: 'ignite-shim',
    description: 'A temporary package.json created to prevent node from wandering too far.',
    repository: 'infinitered/ignite',
    license: 'MIT'
  })

  // grab the right boilerplate
  const cliBoilerplate = parameters.options.boilerplate || parameters.options.b
  const boilerplateName = strings.isBlank(cliBoilerplate) ? 'ir-boilerplate-2016' : cliBoilerplate

  // pick the inbound cli options
  const cliOpts = parameters.options

  // turn this back into a string
  const forwardingOptions = trim(reduce((src, k) => {
    const v = cliOpts[k]
    return concat(v === true ? `--${k} ` : `--${k} ${v} `, src)
  })('', keys(cliOpts)))

  // let's kick off the template
  let ok = false
  try {
    const command = trim(`ignite boilerplate-install ${boilerplateName} ${projectName} ${forwardingOptions}`)
    log(`running boilerplate: ${command}`)
    await system.exec(command, { stdio: 'inherit' })
    log('finished boilerplate')
    ok = true
  } catch (e) {
    log('error running boilerplate')
    log(e)
  }

  // always clean up the app-template stuff
  log('cleaning temporary files')
  filesystem.remove('node_modules')
  filesystem.remove('ignite')
  filesystem.remove('package.json')

  // did we install everything successfully?
  if (ok) {
    log(`moving contents of ${projectName} into place`)
    // move everything that's 1 deep back up to here
    forEach(
      filename => filesystem.move(path.join(projectName, filename), filename)
      , filesystem.list(projectName) || []
    )
    log(`removing unused sub directory ${projectName}`)
    filesystem.remove(projectName)
  }
  log('finished running new')
}

module.exports = command
<|endoftext|>


<commit_before>import {TimeSeriesButton} from './timeSeriesButton.js';

export class TimeSeriesMarker extends TimeSeriesButton {
    constructor(latLon) {
        super();
        const roundLatLon = (num) => Math.round(num*100)/100;
        const timeSeriesButton = this.querySelector('#timeseries-button');
        timeSeriesButton.innerHTML += `

No layer with colorbar to show values

lat: ${roundLatLon(latLon.lat)} lon: ${roundLatLon(latLon.lng)}

        `;
        this.rgb = null;
        this.clrbarLocation = null;
    }

    connectedCallback() {
        super.connectedCallback;
        this.updateTimestamps();
    }

    getRGB() {
        return this.rgb;
    }

    setRGBValues(rgb, clrbarLocation) {
        this.rgb = rgb;
        this.clrbarLocation = clrbarLocation;
        const clrbarP = this.querySelector('#colorbar-location');
        const rgbP = this.querySelector('#rgb-value');
        const button = this.getButton();
        clrbarP.style.display = "none";
        rgbP.innerHTML = "No layer with colorbar to show values of";
        rgbP.style.color = "black";
        button.disabled = true;
        if (clrbarLocation) {
            clrbarP.style.display = "block";
            clrbarP.innerHTML = "colorbar location: " + clrbarLocation;
            rgbP.style.color = `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`;
            rgbP.innerHTML = `R: ${rgb[0]} G: ${rgb[1]} B: ${rgb[2]}`;
            button.disabled = false;
        }
    }
}

window.customElements.define('time-series-marker', TimeSeriesMarker);
<commit_msg>If value of pixel was zero, would show there was no value in marker<commit_after>import {TimeSeriesButton} from './timeSeriesButton.js';

export class TimeSeriesMarker extends TimeSeriesButton {
    constructor(latLon) {
        super();
        const roundLatLon = (num) => Math.round(num*100)/100;
        const timeSeriesButton = this.querySelector('#timeseries-button');
        timeSeriesButton.innerHTML += `

No layer with colorbar to show values

lat: ${roundLatLon(latLon.lat)} lon: ${roundLatLon(latLon.lng)}

        `;
        this.rgb = null;
        this.clrbarLocation = null;
    }

    connectedCallback() {
        super.connectedCallback;
        this.updateTimestamps();
    }

    getRGB() {
        return this.rgb;
    }

    setRGBValues(rgb, clrbarLocation) {
        this.rgb = rgb;
        this.clrbarLocation = clrbarLocation;
        const clrbarP = this.querySelector('#colorbar-location');
        const rgbP = this.querySelector('#rgb-value');
        const button = this.getButton();
        clrbarP.style.display = "none";
        rgbP.innerHTML = "No layer with colorbar to show values of";
        rgbP.style.color = "black";
        button.disabled = true;
        if (clrbarLocation != null) {
            clrbarP.style.display = "block";
            clrbarP.innerHTML = "colorbar location: " + clrbarLocation;
            rgbP.style.color = `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`;
            rgbP.innerHTML = `R: ${rgb[0]} G: ${rgb[1]} B: ${rgb[2]}`;
            button.disabled = false;
        }
    }
}

window.customElements.define('time-series-marker', TimeSeriesMarker);
<|endoftext|>


<commit_before>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { TextInput } from 'react-native';
import { FormattedMessage } from 'react-intl';

import type { Context, Style } from '../types';
import { HALF_COLOR, BORDER_COLOR } from '../styles';

export type Props = {|
  ...$PropertyType<TextInput, 'props'>,
  style?: Style,
  placeholder: string, // could become LocalizableText if need be
  onChangeText?: (text: string) => void,
  textInputRef?: (component: ?TextInput) => void,
|};

type State = {
  isFocused: boolean,
};

/**
 * A light abstraction over the standard TextInput component
 * that allows us to seamlessly provide internationalization
 * capabilities and also style the component depending on
 * the platform the app is running on.
 *
 * @prop [style] - Can override our default style for inputs.
 * @prop placeholder - Translated before passing to TextInput as
 *   a prop of the same name.
 * @prop [textInputRef] - Passed to TextInput in `ref`.  See upstream docs
 *   on refs: https://reactjs.org/docs/refs-and-the-dom.html
 * @prop ...all other TextInput props - Passed through verbatim to TextInput.
 *   See upstream: https://facebook.github.io/react-native/docs/textinput
 */
export default class Input extends PureComponent<Props, State> {
  context: Context;
  state = {
    isFocused: false,
  };
  textInput: ?TextInput;

  static contextTypes = {
    styles: () => null,
  };

  handleClear = () => {
    const { onChangeText } = this.props;
    if (onChangeText) {
      onChangeText('');
    }
    if (this.textInput) {
      this.textInput.clear();
    }
  };

  handleFocus = () => {
    this.setState({
      isFocused: true,
    });
  };

  handleBlur = () => {
    this.setState({
      isFocused: false,
    });
  };

  render() {
    const { styles } = this.context;
    const { style, placeholder, textInputRef, ...restProps } = this.props;
    const { isFocused } = this.state;

    return (
      <FormattedMessage id={placeholder} defaultMessage={placeholder}>
        {(text: string) => (
          <TextInput
            style={[styles.input, style]}
            placeholder={text}
            placeholderTextColor={HALF_COLOR}
            underlineColorAndroid={isFocused ? BORDER_COLOR : HALF_COLOR}
            onFocus={this.handleFocus}
            onBlur={this.handleBlur}
            ref={(component: ?TextInput) => {
              this.textInput = component;
              if (textInputRef) {
                textInputRef(component);
              }
            }}
            {...restProps}
          />
        )}
      </FormattedMessage>
    );
  }
}
<commit_msg>input: Handle full text/values localization after all.<commit_after>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { TextInput } from 'react-native';
import { FormattedMessage } from 'react-intl';

import type { Context, Style } from '../types';
import { HALF_COLOR, BORDER_COLOR } from '../styles';

export type Props = {|
  ...$PropertyType<TextInput, 'props'>,
  style?: Style,
  placeholder: string | { text: string, values?: { [string]: string } },
  onChangeText?: (text: string) => void,
  textInputRef?: (component: ?TextInput) => void,
|};

type State = {
  isFocused: boolean,
};

/**
 * A light abstraction over the standard TextInput component
 * that allows us to seamlessly provide internationalization
 * capabilities and also style the component depending on
 * the platform the app is running on.
 *
 * @prop [style] - Can override our default style for inputs.
 * @prop placeholder - Translated before passing to TextInput as
 *   a prop of the same name.
 * @prop [textInputRef] - Passed to TextInput in `ref`.  See upstream docs
 *   on refs: https://reactjs.org/docs/refs-and-the-dom.html
 * @prop ...all other TextInput props - Passed through verbatim to TextInput.
 *   See upstream: https://facebook.github.io/react-native/docs/textinput
 */
export default class Input extends PureComponent<Props, State> {
  context: Context;
  state = {
    isFocused: false,
  };
  textInput: ?TextInput;

  static contextTypes = {
    styles: () => null,
  };

  handleClear = () => {
    const { onChangeText } = this.props;
    if (onChangeText) {
      onChangeText('');
    }
    if (this.textInput) {
      this.textInput.clear();
    }
  };

  handleFocus = () => {
    this.setState({
      isFocused: true,
    });
  };

  handleBlur = () => {
    this.setState({
      isFocused: false,
    });
  };

  render() {
    const { styles } = this.context;
    const { style, placeholder, textInputRef, ...restProps } = this.props;
    const { isFocused } = this.state;
    const fullPlaceholder =
      typeof placeholder === 'object' /* force linebreak */
        ? placeholder
        : { text: placeholder, values: undefined };

    return (
      <FormattedMessage
        id={fullPlaceholder.text}
        defaultMessage={fullPlaceholder.text}
        values={fullPlaceholder.values}
      >
        {(text: string) => (
          <TextInput
            style={[styles.input, style]}
            placeholder={text}
            placeholderTextColor={HALF_COLOR}
            underlineColorAndroid={isFocused ? BORDER_COLOR : HALF_COLOR}
            onFocus={this.handleFocus}
            onBlur={this.handleBlur}
            ref={(component: ?TextInput) => {
              this.textInput = component;
              if (textInputRef) {
                textInputRef(component);
              }
            }}
            {...restProps}
          />
        )}
      </FormattedMessage>
    );
  }
}
<|endoftext|>


<commit_before><commit_msg>Update project validator to 5.0.0<commit_after><|endoftext|>


<commit_before>/**
 * Scarlett @ DevTeam
 * Includes global utility functions that can be called from any context
 */

import _ from "lodash";

export {
  isObjectAssigned,
  isString,
  isNumber,
  isFunction,
  inheritsFrom,
  generateUID,
  splitCamelCase,
  capitalize,
  getType,
  isEqual
};

/**
 * Returns true if there is something assigned to the given object
 * @param obj
 * @returns {boolean}
 */
function isObjectAssigned(obj) {
  return typeof obj !== "undefined" && obj !== null;
}

/**
 * Validates if the given object is a string
 * @param obj
 * @returns {boolean}
 */
function isString(obj) {
  return _.isString(obj);
}

/**
 * Validates if the given object is a number
 * @param obj
 * @returns {boolean}
 */
function isNumber(obj) {
  return _.isNumber(obj);
}

/**
 * Validates if the given object is a function
 * @param obj
 * @returns {boolean}
 */
function isFunction(obj) {
  return _.isFunction(obj);
}

/**
 * Creates inheritance between classes by cloning the prototype
 * @param child
 * @param parent
 */
function inheritsFrom(child, parent) {
  child.prototype = Object.create(parent.prototype);
}

/**
 * Generates a unique natural number
 * @type {number}
 * @private
 */
let _SS_UID = 0;
function generateUID() {
  return ++_SS_UID;
}

/**
 * Capitalizes a string
 * @param string
 * @returns {*}
 */
function capitalize(string) {
  return _.capitalize(string);
}

/**
 * Split camel case
 * TODO: change to lodash startCase?
 * @param string
 * @returns {string}
 */
function splitCamelCase(string) {
  return _.startCase(string);
}

/**
 * Gets the type of the object
 * @param object
 * @returns {*}
 */
function getType(object) {
  if (object === null) return "[object Null]"; // special case
  if (object.getType) return object.getType();
  return object.constructor.name || Object.prototype.toString.call(object);
}

/**
 * Compares by applying the 'equal' function if it exists in the first object
 * @param a
 * @param b
 */
function isEqual(a, b) {
  return _.isEqual(a, b);
}
<commit_msg>fix(utils): Revert isEqual function<commit_after>/**
 * Scarlett @ DevTeam
 * Includes global utility functions that can be called from any context
 */

import _ from "lodash";

export {
  isObjectAssigned,
  isString,
  isNumber,
  isFunction,
  inheritsFrom,
  generateUID,
  splitCamelCase,
  capitalize,
  getType,
  isEqual
};

/**
 * Returns true if there is something assigned to the given object
 * @param obj
 * @returns {boolean}
 */
function isObjectAssigned(obj) {
  return typeof obj !== "undefined" && obj !== null;
}

/**
 * Validates if the given object is a string
 * @param obj
 * @returns {boolean}
 */
function isString(obj) {
  return _.isString(obj);
}

/**
 * Validates if the given object is a number
 * @param obj
 * @returns {boolean}
 */
function isNumber(obj) {
  return _.isNumber(obj);
}

/**
 * Validates if the given object is a function
 * @param obj
 * @returns {boolean}
 */
function isFunction(obj) {
  return typeof obj === "function";
}

/**
 * Creates inheritance between classes by cloning the prototype
 * @param child
 * @param parent
 */
function inheritsFrom(child, parent) {
  child.prototype = Object.create(parent.prototype);
}

/**
 * Generates a unique natural number
 * @type {number}
 * @private
 */
let _SS_UID = 0;
function generateUID() {
  return ++_SS_UID;
}

/**
 * Capitalizes a string
 * @param string
 * @returns {*}
 */
function capitalize(string) {
  return _.capitalize(string);
}

/**
 * Split camel case
 * TODO: change to lodash startCase?
 * @param string
 * @returns {string}
 */
function splitCamelCase(string) {
  return _.startCase(string);
}

/**
 * Gets the type of the object
 * @param object
 * @returns {*}
 */
function getType(object) {
  if (object === null) return "[object Null]"; // special case
  if (object.getType) return object.getType();
  return object.constructor.name || Object.prototype.toString.call(object);
}

/**
 * Compares by applying the 'equal' function if it exists in the first object
 * @param a
 * @param b
 */
function isEqual(a, b) {
  if (isFunction(a.equals)) {
    return a.equals(b);
  }

  return a === b;
}
<|endoftext|>


<commit_before>import * as actions from 'store/actions'
import assign from 'lodash/assign'
import forEach from 'lodash/forEach'
import humanFormat from 'human-format'
import includes from 'lodash/includes'
import isArray from 'lodash/isArray'
import isFunction from 'lodash/isFunction'
import isPlainObject from 'lodash/isPlainObject'
import isString from 'lodash/isString'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import pick from 'lodash/fp/pick'
import React, { cloneElement, PropTypes } from 'react'
import { connect } from 'react-redux'

import invoke from './invoke'

// ===================================================================

export const EMPTY_OBJECT = Object.freeze({ })

export const ensureArray = (value) => {
  if (value === undefined) {
    return []
  }

  return Array.isArray(value) ? value : [ value ]
}

export const propsEqual = (o1, o2, props) => {
  props = ensureArray(props)

  for (const prop of props) {
    if (o1[prop] !== o2[prop]) {
      return false
    }
  }

  return true
}

// ===================================================================

const _bind = (fn, thisArg) => function bound () {
  return fn.apply(thisArg, arguments)
}
const _defineProperty = Object.defineProperty

export const autobind = (target, key, {
  configurable,
  enumerable,
  value: fn,
  writable
}) => ({
  configurable,
  enumerable,

  get () {
    if (this === target) {
      return fn
    }

    const bound = _bind(fn, this)

    _defineProperty(this, key, {
      configurable: true,
      enumerable: false,
      value: bound,
      writable: true
    })

    return bound
  },
  set (newValue) {
    // Cannot use assignment because it will call the setter on
    // the prototype.
    _defineProperty(this, key, {
      configurable: true,
      enumerable: true,
      value: newValue,
      writable: true
    })
  }
})

// -------------------------------------------------------------------

export class BlockLink extends React.Component {
  static contextTypes = {
    router: React.PropTypes.object
  }

  render () {
    const { children } = this.props
    return (

 {
          const { currentTarget } = event
          let element = event.target
          while (element !== currentTarget) {
            if (includes(['A', 'INPUT', 'BUTTON'], element.tagName)) {
              return
            }
            element = element.parentNode
          }
          event.stopPropagation()
          this.context.router.push(this.props.to)
        }}
      >
        {children}

    )
  }
}

// -------------------------------------------------------------------

const _normalizeMapStateToProps = mapper => {
  if (isFunction(mapper)) {
    return mapper
  }

  if (isArray(mapper)) {
    return pick(mapper)
  }

  mapper = mapValues(mapper, _normalizeMapStateToProps)
  return (state, props) => mapValues(mapper, fn => fn(state, props))
}

export const connectStore = (mapStateToProps, opts) => connect(
  _normalizeMapStateToProps(mapStateToProps),
  actions,
  undefined,
  opts
)

// -------------------------------------------------------------------

// Simple matcher to use in object filtering.
export const createSimpleMatcher = (pattern, valueGetter) => {
  if (!pattern) {
    return
  }

  pattern = pattern.toLowerCase()
  return item => valueGetter(item).toLowerCase().indexOf(pattern) !== -1
}

// -------------------------------------------------------------------

// Returns the first defined (non-null, non-undefined) value.
export const firstDefined = function () {
  const n = arguments.length
  for (let i = 0; i < n; ++i) {
    const arg = arguments[i]
    if (arg != null) {
      return arg
    }
  }
}

// -------------------------------------------------------------------

export const mapPlus = (collection, cb) => {
  const result = []
  const push = ::result.push
  forEach(collection, value => cb(value, push))
  return result
}

// -------------------------------------------------------------------

export const noop = () => {}

// -------------------------------------------------------------------

export const osFamily = invoke({
  linux: [
    'coreos'
  ],
  centos: [
    'centos'
  ],
  debian: [
    'debian'
  ],
  fedora: [
    'fedora'
  ],
  gentoo: [
    'gentoo'
  ],
  oracle: [
    'oracle'
  ],
  redhat: [
    'redhat',
    'rhel'
  ],
  ubuntu: [
    'ubuntu'
  ],
  solaris: [
    'solaris'
  ],
  freebsd: [
    'freebsd'
  ],
  netbsd: [
    'netbsd'
  ],
  osx: [
    'osx'
  ],
  'linux-mint': [
    'linux-mint'
  ],
  suse: [
    'sles',
    'suse'
  ],
  windows: [
    'windows'
  ]
}, osByFamily => {
  const osToFamily = Object.create(null)
  forEach(osByFamily, (list, family) => {
    forEach(list, os => {
      osToFamily[os] = family
    })
  })

  return osName => osToFamily[osName.toLowerCase()] || 'other'
})

// -------------------------------------------------------------------

// Experimental!
//
// ```js
// <If cond={user}>
//   <p>user.name</p>
//   <p>user.email</p>
// </If>
// ```
export const If = ({ cond, children }) => cond && children
  ? map(children, (child, key) => cloneElement(child, { key }))
  : null

// -------------------------------------------------------------------

export { invoke }

// -------------------------------------------------------------------

// Decorators to help declaring on React components without using the
// tedious static properties syntax.
//
// ```js
// @propTypes({
//   children: propTypes.node.isRequired
// })
// class MyComponent extends React.Component {}
// ```
export const propTypes = types => target => {
  if (target.propTypes) {
    throw new Error('refusing to override propTypes property')
  }

  target.propTypes = types

  return target
}
assign(propTypes, PropTypes)

// -------------------------------------------------------------------

export const formatSize = bytes => humanFormat(bytes, { scale: 'binary', unit: 'B' })

export const parseSize = size => {
  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

export const normalizeXenToolsStatus = status => {
  if (status === false) {
    return 'not-installed'
  }
  if (status === undefined) {
    return 'unknown'
  }
  if (status === 'up to date') {
    return 'up-to-date'
  }
  return 'out-of-date'
}

// -------------------------------------------------------------------

export const Debug = ({ value }) =>

  {JSON.stringify(value, null, 2)}

// -------------------------------------------------------------------

const _NotFound = () =>
Page not found

// Decorator to declare routes on a component.
//
// TODO: add support for function childRoutes (getChildRoutes).
export const routes = (indexRoute, childRoutes) => target => {
  if (isArray(indexRoute)) {
    childRoutes = indexRoute
    indexRoute = undefined
  } else if (isFunction(indexRoute)) {
    indexRoute = {
      component: indexRoute
    }
  } else if (isString(indexRoute)) {
    indexRoute = {
      onEnter: invoke(indexRoute, pathname => (state, replace) => {
        const current = state.location.pathname
        replace((current === '/' ? '' : current) + '/' + pathname)
      })
    }
  }

  if (isPlainObject(childRoutes)) {
    childRoutes = map(childRoutes, (component, path) => {
      // The logic can be bypassed by passing a plain object.
      if (isPlainObject(component)) {
        return { ...component, path }
      }

      return { ...component.route, component, path }
    })
  }

  if (childRoutes) {
    childRoutes.push({ component: _NotFound, path: '*' })
  }

  target.route = {
    indexRoute,
    childRoutes
  }

  return target
}

// -------------------------------------------------------------------

// Creates a new function which throws an error.
//
// ```js
// promise.catch(throwFn('an error has occured'))
//
// function foo (param = throwFn('param is required')) {}
// ```
export const throwFn = error => () => {
  throw (
    isString(error)
      ? new Error(error)
      : error
  )
}
<commit_msg>feat(utils/@checkPropsState): create an optimized shouldComponentUpdate()<commit_after>import * as actions from 'store/actions'
import assign from 'lodash/assign'
import forEach from 'lodash/forEach'
import humanFormat from 'human-format'
import includes from 'lodash/includes'
import isArray from 'lodash/isArray'
import isFunction from 'lodash/isFunction'
import isPlainObject from 'lodash/isPlainObject'
import isString from 'lodash/isString'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import pick from 'lodash/fp/pick'
import React, { cloneElement, PropTypes } from 'react'
import { connect } from 'react-redux'

import invoke from './invoke'

// ===================================================================

export const EMPTY_OBJECT = Object.freeze({ })

export const ensureArray = (value) => {
  if (value === undefined) {
    return []
  }

  return Array.isArray(value) ? value : [ value ]
}

export const propsEqual = (o1, o2, props) => {
  props = ensureArray(props)

  for (const prop of props) {
    if (o1[prop] !== o2[prop]) {
      return false
    }
  }

  return true
}

// ===================================================================

const _bind = (fn, thisArg) => function bound () {
  return fn.apply(thisArg, arguments)
}
const _defineProperty = Object.defineProperty

export const autobind = (target, key, {
  configurable,
  enumerable,
  value: fn,
  writable
}) => ({
  configurable,
  enumerable,

  get () {
    if (this === target) {
      return fn
    }

    const bound = _bind(fn, this)

    _defineProperty(this, key, {
      configurable: true,
      enumerable: false,
      value: bound,
      writable: true
    })

    return bound
  },
  set (newValue) {
    // Cannot use assignment because it will call the setter on
    // the prototype.
    _defineProperty(this, key, {
      configurable: true,
      enumerable: true,
      value: newValue,
      writable: true
    })
  }
})

// -------------------------------------------------------------------

export class BlockLink extends React.Component {
  static contextTypes = {
    router: React.PropTypes.object
  }

  render () {
    const { children } = this.props
    return (

 {
          const { currentTarget } = event
          let element = event.target
          while (element !== currentTarget) {
            if (includes(['A', 'INPUT', 'BUTTON'], element.tagName)) {
              return
            }
            element = element.parentNode
          }
          event.stopPropagation()
          this.context.router.push(this.props.to)
        }}
      >
        {children}

    )
  }
}

// -------------------------------------------------------------------

export const checkPropsState = (propsNames, stateNames) => Component => {
  const nProps = propsNames && propsNames.length
  const nState = stateNames && stateNames.length

  Component.prototype.shouldComponentUpdate = (newProps, newState) => {
    const { props, state } = this

    for (let i = 0; i < nProps; ++i) {
      const name = propsNames[i]
      if (newProps[name] !== props[name]) {
        return true
      }
    }

    for (let i = 0; i < nState; ++i) {
      const name = stateNames[i]
      if (newState[name] !== state[name]) {
        return true
      }
    }
  }

  return Component
}

// -------------------------------------------------------------------

const _normalizeMapStateToProps = mapper => {
  if (isFunction(mapper)) {
    return mapper
  }

  if (isArray(mapper)) {
    return pick(mapper)
  }

  mapper = mapValues(mapper, _normalizeMapStateToProps)
  return (state, props) => mapValues(mapper, fn => fn(state, props))
}

export const connectStore = (mapStateToProps, opts) => connect(
  _normalizeMapStateToProps(mapStateToProps),
  actions,
  undefined,
  opts
)

// -------------------------------------------------------------------

// Simple matcher to use in object filtering.
export const createSimpleMatcher = (pattern, valueGetter) => {
  if (!pattern) {
    return
  }

  pattern = pattern.toLowerCase()
  return item => valueGetter(item).toLowerCase().indexOf(pattern) !== -1
}

// -------------------------------------------------------------------

// Returns the first defined (non-null, non-undefined) value.
export const firstDefined = function () {
  const n = arguments.length
  for (let i = 0; i < n; ++i) {
    const arg = arguments[i]
    if (arg != null) {
      return arg
    }
  }
}

// -------------------------------------------------------------------

export const mapPlus = (collection, cb) => {
  const result = []
  const push = ::result.push
  forEach(collection, value => cb(value, push))
  return result
}

// -------------------------------------------------------------------

export const noop = () => {}

// -------------------------------------------------------------------

export const osFamily = invoke({
  linux: [
    'coreos'
  ],
  centos: [
    'centos'
  ],
  debian: [
    'debian'
  ],
  fedora: [
    'fedora'
  ],
  gentoo: [
    'gentoo'
  ],
  oracle: [
    'oracle'
  ],
  redhat: [
    'redhat',
    'rhel'
  ],
  ubuntu: [
    'ubuntu'
  ],
  solaris: [
    'solaris'
  ],
  freebsd: [
    'freebsd'
  ],
  netbsd: [
    'netbsd'
  ],
  osx: [
    'osx'
  ],
  'linux-mint': [
    'linux-mint'
  ],
  suse: [
    'sles',
    'suse'
  ],
  windows: [
    'windows'
  ]
}, osByFamily => {
  const osToFamily = Object.create(null)
  forEach(osByFamily, (list, family) => {
    forEach(list, os => {
      osToFamily[os] = family
    })
  })

  return osName => osToFamily[osName.toLowerCase()] || 'other'
})

// -------------------------------------------------------------------

// Experimental!
//
// ```js
// <If cond={user}>
//   <p>user.name</p>
//   <p>user.email</p>
// </If>
// ```
export const If = ({ cond, children }) => cond && children
  ? map(children, (child, key) => cloneElement(child, { key }))
  : null

// -------------------------------------------------------------------

export { invoke }

// -------------------------------------------------------------------

// Decorators to help declaring on React components without using the
// tedious static properties syntax.
//
// ```js
// @propTypes({
//   children: propTypes.node.isRequired
// })
// class MyComponent extends React.Component {}
// ```
export const propTypes = types => target => {
  if (target.propTypes) {
    throw new Error('refusing to override propTypes property')
  }

  target.propTypes = types

  return target
}
assign(propTypes, PropTypes)

// -------------------------------------------------------------------

export const formatSize = bytes => humanFormat(bytes, { scale: 'binary', unit: 'B' })

export const parseSize = size => {
  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

export const normalizeXenToolsStatus = status => {
  if (status === false) {
    return 'not-installed'
  }
  if (status === undefined) {
    return 'unknown'
  }
  if (status === 'up to date') {
    return 'up-to-date'
  }
  return 'out-of-date'
}

// -------------------------------------------------------------------

export const Debug = ({ value }) =>

  {JSON.stringify(value, null, 2)}

// -------------------------------------------------------------------

const _NotFound = () =>
Page not found

// Decorator to declare routes on a component.
//
// TODO: add support for function childRoutes (getChildRoutes).
export const routes = (indexRoute, childRoutes) => target => {
  if (isArray(indexRoute)) {
    childRoutes = indexRoute
    indexRoute = undefined
  } else if (isFunction(indexRoute)) {
    indexRoute = {
      component: indexRoute
    }
  } else if (isString(indexRoute)) {
    indexRoute = {
      onEnter: invoke(indexRoute, pathname => (state, replace) => {
        const current = state.location.pathname
        replace((current === '/' ? '' : current) + '/' + pathname)
      })
    }
  }

  if (isPlainObject(childRoutes)) {
    childRoutes = map(childRoutes, (component, path) => {
      // The logic can be bypassed by passing a plain object.
      if (isPlainObject(component)) {
        return { ...component, path }
      }

      return { ...component.route, component, path }
    })
  }

  if (childRoutes) {
    childRoutes.push({ component: _NotFound, path: '*' })
  }

  target.route = {
    indexRoute,
    childRoutes
  }

  return target
}

// -------------------------------------------------------------------

// Creates a new function which throws an error.
//
// ```js
// promise.catch(throwFn('an error has occured'))
//
// function foo (param = throwFn('param is required')) {}
// ```
export const throwFn = error => () => {
  throw (
    isString(error)
      ? new Error(error)
      : error
  )
}
<|endoftext|>


<commit_before>var MARKDOWN_TUTORIAL = "\
# Markdown tutorial by example\
\n\n\
Read this if you need to check the Markdown syntax. \n\n\
\
> **Disable the live tutorial to go back to your article**.\n\
\
\n\n\n\
# Headers \
\n\n\
## Header's Subsection \
\n\n\
### Header's Subsection \
\n\n\
#### Header's Subsection \
\n\n\
##### Header's Subsection \
\n\n\n\
# Text Format \
\n\n\
normal, *italic*, **bold**, __bold__, _emphasis_, ~~strikethrough~~, ùníçõd&, `code`, \*escaping special chars\*, © \
\n\n\
## Bloquote \
\n\n\
> You can put some warning or important messages in blockquotes. \n\
Check that a blockquote can have multiple lines. \
\n\n\n\
# Code \
\n\n\
```\n\
print('test')\n\
```\
\n\n\
```javascript\n\
$(function(){\n\
  $('div').html('I am a div.');\n\
});\n\
```\
\n\n\n\
# Lists\
\n\n\
## Unordered list\
\n\n\
- item 1\n\
- item 2\n\
\n\
or\n\
\n\
* item 1\n\
* item 2\
\n\n\
## Ordered list\
\n\n\
1. item 1\n\
1. item 2\n\
\n\
or\n\
\n\
1. item 1\n\
2. item 2\n\
\n\
## Nesting\
\n\n\
1. item 1\n\
  1. item 1.1\n\
  2. item 1.2\n\
    - subitem 1\n\
    - subitem 2\n\
2. item 2\n\
\n\
## Task Listing\
\n\n\
- [ ] item 1\n\
- [x] item 2\n\
\n\n\
# Tables\
\n\n\
First Column Header | Second Column Header | Third Column\n\
------------------- | -------------------- | ------------\n\
Content from cell 1 | | Content from cell 3\n\
Another cell 1 | Another cell 2\n\
\n\n\
# Links\
\n\n\
* [text of the link](http://hackguides.org)\n\
* http://hackguides.org\
\n\n\n\
# Images and Files\
\n\n\
![alt text](http://tutorials.pluralsight.com/static/img/dark-logo.png 'Logo Title')\
\n\n\n\
# Horizontal rules\
\n\n\
------------------------\
\n\n\
or\
\n\n\
* * *\
\n\n\
or\
\n\n\
*****\
\n\n\
";

// Article data
var editor;
var author_name;
var author_real_name;
// Auto-save
var current_local_filename;
var autosaveEnabled = true;
// Preview
var preview = null;
var editor_wrapper = null;
// Markdown tutorial
var liveTutorialEnabled = false;
// Scroll Sync
var scrollSyncEnabled = false;
// Virtual DOM
var vdom = window.virtualDom
var html2vtree = window.vdomParser;
var currentVTree = null;
var previewRootDomNode = null;

var help_sections;
var isHelpEnabled = false;

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

var updatePreview = function() {
    console.clear()
    var start, end, time = 0
    start = new Date().getTime();
    var newHtml = markdown2html(editor.getSession().getValue())
    end = new Date().getTime();
    time = end - start;
    console.log('markdown to html: ' + time + 'ms')

    start = new Date().getTime();
    newVTree = html2vtree('
' + newHtml + '
', 'key')
    end = new Date().getTime();
    time = end - start;
    console.log('html to vdom: ' + time + 'ms')
    console.log('vdom: ' + newVTree.children.length + ' nodes')

    if (! currentVTree) {
        currentVTree = newVTree
        previewRootDomNode = vdom.create(currentVTree)
        preview.appendChild(previewRootDomNode);
    }

    start = new Date().getTime();
    var patches = vdom.diff(currentVTree, newVTree);
    end = new Date().getTime();
    time = end - start;
    console.log('diff: ' + Object.keys(patches).length + ' nodes')
    console.log('diff: ' + time + 'ms')

    start = new Date().getTime();
    previewRootDomNode = vdom.patch(previewRootDomNode, patches);
    end = new Date().getTime();
    time = end - start;
    console.log('patch: ' + time + 'ms')

    start = new Date().getTime();
    currentVTree = newVTree;
    // $(preview).find('pre code').each(function(i, e) {hljs.highlightBlock(e)});
    scrollPreviewAccordingToEditor()
    end = new Date().getTime();
    time = end - start;
    console.log('fixed variable and scroll: ' + time + 'ms')
    console.log('<< Preview updated')
};

var loadAutoSave = function(local_filename) {
    var obj = localStorage.getItem('hack.guides');
    if (obj) {
        obj = JSON.parse(obj);
        return obj[local_filename]; // markdown content or undefined
    }
    return undefined;
}

var autoSave = function(local_filename) {
    var content_as_markdown = editor.getSession().getValue();
    var obj = localStorage.getItem('hack.guides') || '{}';
    obj = JSON.parse(obj);
    obj[local_filename] = content_as_markdown;
    localStorage.setItem('hack.guides', JSON.stringify(obj));
};

var clearLocalSave = function(local_filename) {
    var obj = localStorage.getItem('hack.guides');
    if (obj) {
        obj = JSON.parse(obj);
        delete obj[local_filename];
        localStorage.setItem('hack.guides', JSON.stringify(obj));
    }
    return undefined;
}

function initialize_editor(local_filename, content, name, real_name, img_upload_url) {
    author_name = name;
    author_real_name = real_name;
    current_local_filename = local_filename;

    preview = document.getElementById('preview');
    editor_wrapper = document.getElementById('editor-wrapper');
    toggleScrollSync(); // enable auto sync

    editor = ace.edit("editor");
    editor.setTheme("ace/theme/github");
    editor.getSession().setMode("ace/mode/markdown");
    editor.getSession().setUseWrapMode(true);
    editor.getSession().setNewLineMode("unix");
    // Manage editor size
    editor.setOption('minLines', 1);
    $(window).resize(resizeEditor);
    resizeEditor()
    editor.$blockScrolling = Infinity;
    // Editor layout features
    editor.setShowPrintMargin(false);
    editor.renderer.setShowGutter(true);
    editor.renderer.setOption('showLineNumbers', true);

    var placeholder = '# Start writing your guide here.\n\nOr load the live markdown tutorial to check the syntax.';
    var local_content = loadAutoSave(local_filename);
    // local content should always be the same or the most updated version.
    editor.setValue(local_content || content || placeholder);
    editor.gotoLine(1);
    updatePreview();

    if (content && ! local_content) {
        autoSave(local_filename);
    }

    editor.getSession().on('change', debounce(updatePreview, 200));
    editor.getSession().on('change', debounce(function() {
        if (autosaveEnabled) {
            autoSave(local_filename);
        }
    }, 1000));

    editor.getSession().on('changeScrollTop', function(scrollTop) {
        scrollPreviewAccordingToEditor(scrollTop)
    })

    configure_dropzone_area(img_upload_url);

    return editor;
}

function getAceEditorScrollHeight() {
    var r = editor.renderer
    return r.layerConfig.maxHeight - r.$size.scrollerHeight + r.scrollMargin.bottom
}

function configure_dropzone_area(img_upload_url) {
    Dropzone.autoDiscover = false;
    var dropZoneOptions = {
        url: img_upload_url,
        paramName: 'file',
        maxFilesize: 3, // MB
        uploadMultiple: false,
        disablePreview: false,
        createImageThumbnails: false,
        addRemoveLinks: false,
        previewTemplate: document.querySelector('#preview-template').innerHTML,
        clickable: '.btn-dropzone',
        accept: function(file, done) {
            if (file.name.endsWith('.exe') || file.name.endsWith('.bin') || file.name.endsWith('.bat')) {
                done("File not supported");
            }
            else {
                done();
            }
        }
    };
    var myDropzone = new Dropzone("div#droppable-area", dropZoneOptions);
    myDropzone.on('success', function(file, path) {
        // Add Markdown reference into the editor
        var fileMarkdown = '\n![description](' + path + ')\n';
        editor.insert(fileMarkdown);
    });

    myDropzone.on("complete", function(file) {
        myDropzone.removeFile(file);
    });

    return myDropzone;
}

function openLiveMarkdownTutorial() {
    autosaveEnabled = false;
    editor.getSession().setValue(MARKDOWN_TUTORIAL);
    $('.btn-save').prop('disabled', true);
}

function closeLiveMarkdownTutorial() {
    editor.setValue(loadAutoSave(current_local_filename) || '');
    autosaveEnabled = true;
    $('.btn-save').prop('disabled', false);
}

function toggleLiveTutorial() {
    if (liveTutorialEnabled) {
        closeLiveMarkdownTutorial();
    } else {
        openLiveMarkdownTutorial();
    }
    liveTutorialEnabled = ! liveTutorialEnabled;
}

function scrollPreviewAccordingToEditor(scrollTop) {
    if (scrollSyncEnabled) {
        scrollTop = scrollTop || editor.session.getScrollTop()
        var editorHeight = getAceEditorScrollHeight()
        var percentage = scrollTop / editorHeight

        preview.scrollTop = Math.round(percentage * (preview.scrollHeight - preview.offsetHeight))
    }
}

function scrollEditorAccordingToPreview() {
    $(preview).off('scroll')

    var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight)

    var editorHeight = getAceEditorScrollHeight()
    var position = Math.round(percentage * editorHeight)
    editor.getSession().setScrollTop(position)

    setTimeout(function() { $(preview).on('scroll', scrollEditorAccordingToPreview); }, 10);
}

function toggleScrollSync() {
    if (scrollSyncEnabled) {
        $(preview).off('scroll', scrollEditorAccordingToPreview);
    } else {
        $(preview).on('scroll', scrollEditorAccordingToPreview);
    }
    scrollSyncEnabled = ! scrollSyncEnabled;
}

function openFullscreen() {
    $('html, body').addClass('body-fs');
    resizeEditor()
}

function resizeEditor() {
    var lineHeight = editor.renderer.lineHeight
    var maxLines = document.getElementById('editor-wrapper').offsetHeight / lineHeight
    editor.setOption('maxLines', Math.floor(maxLines) - 1);
    editor.resize()
};

var clearFlashMessages = function(message, clazz) {
    $('.bg-info, .bg-warning, .bg-danger').remove();
};

/* This requires Twitter bootstraps Modal.js! */
var addModalMessage = function(message) {
    $('#modal-content').html('
' + message + '
');
    $('#modal-error').modal()
};

function save(sha, path, secondary_repo) {
    clearFlashMessages();
    var data = {
        'title': $('input[name=title]').val(),
        'original_stack': $('input[name=original_stack]').val(),
        'stacks': $('#stacks').val(),
        'content': editor.getSession().getValue(),
        'sha': sha,
        'path': path
    }
    if (secondary_repo) {
        data['secondary_repo'] = 1;
    }
    $.ajax({
        type: 'POST',
        url: '/api/save/',
        contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
        data: data,
        dataType: 'json',
        cache: false,
        beforeSend: function(xhr) {
            $('.btn-save').prop('disabled', true);
            $('html, body').css("cursor", "wait");
            return true;
        },
        complete: function(xhr, txt_status) {
            $('html, body').css("cursor", "auto");
            $('.btn-save').prop('disabled', false);
        },
        success: function(data) {
            console.log(data);
            console.log(data.msg);
            clearLocalSave(current_local_filename);
            setTimeout(function(){ window.location.href = data.redirect; }, 1000);
        },
        error: function(response) {
            var status = response.status;
            var data = response.responseJSON;
            console.log(status, data);
            if (data && data.error) {
                addModalMessage(data.error);
            }
            if (data && data.redirect) {
                setTimeout(function(){ window.location.href = data.redirect; }, 1000);
            }
        },
    });
}

function visible_section_idx() {
    for (var ii=0; ii < help_sections.length; ii++) {
        if ($(help_sections[ii]).css('display') == 'block') {
            return ii;
        }
    }

    return -1;
}

function goto_section(next_section) {
    for (var ii=0; ii < help_sections.length; ii++) {
        if (next_section == ii ) {
            $(help_sections[ii]).css('display', 'block');
        } else {
            $(help_sections[ii]).css('display', 'none');
        }
    }
}

function show_all_help_sections(should_show) {
    var display = 'none';
    if (should_show) {
        display = 'block';
    }

    for (var ii=0; ii < help_sections.length; ii++) {
        $(help_sections[ii]).css('display', display);
    }
}

function toggleHelp() {
    if (isHelpEnabled) {
        hideHelp();
    } else {
        showHelp();
    }
}

function showHelp() {
    $('#editor-help').fadeIn('fast');
    $('#editor-help').show();
    isHelpEnabled = true;
}

function hideHelp() {
    $('#editor-help').fadeOut('fast');
    $('#editor-help').hide();
    isHelpEnabled = false;
}

/* Show each section 1 at a time in help. This only works for full-screen mode
 * because these buttons are visible otherwise. */
function init_editor_help() {
    help_sections = $('#editor-help').find('.section');
    goto_section(0);

    $('#editor-help #next').click(function() {
        var curr_section = visible_section_idx();
        var next_section = curr_section + 1;

        if (next_section >= help_sections.length) {
            next_section = 0;
        }

        goto_section(next_section);
    });

    $('#editor-help #prev').click(function() {
        var curr_section = visible_section_idx();
        var next_section = curr_section - 1;

        if (next_section < 0) {
            next_section = help_sections.length - 1;
        }

        goto_section(next_section);
    });
}
<commit_msg>Update preview with dynamic delay time, according to the cursor position.<commit_after>var MARKDOWN_TUTORIAL = "\
# Markdown tutorial by example\
\n\n\
Read this if you need to check the Markdown syntax. \n\n\
\
> **Disable the live tutorial to go back to your article**.\n\
\
\n\n\n\
# Headers \
\n\n\
## Header's Subsection \
\n\n\
### Header's Subsection \
\n\n\
#### Header's Subsection \
\n\n\
##### Header's Subsection \
\n\n\n\
# Text Format \
\n\n\
normal, *italic*, **bold**, __bold__, _emphasis_, ~~strikethrough~~, ùníçõd&, `code`, \*escaping special chars\*, © \
\n\n\
## Bloquote \
\n\n\
> You can put some warning or important messages in blockquotes. \n\
Check that a blockquote can have multiple lines. \
\n\n\n\
# Code \
\n\n\
```\n\
print('test')\n\
```\
\n\n\
```javascript\n\
$(function(){\n\
  $('div').html('I am a div.');\n\
});\n\
```\
\n\n\n\
# Lists\
\n\n\
## Unordered list\
\n\n\
- item 1\n\
- item 2\n\
\n\
or\n\
\n\
* item 1\n\
* item 2\
\n\n\
## Ordered list\
\n\n\
1. item 1\n\
1. item 2\n\
\n\
or\n\
\n\
1. item 1\n\
2. item 2\n\
\n\
## Nesting\
\n\n\
1. item 1\n\
  1. item 1.1\n\
  2. item 1.2\n\
    - subitem 1\n\
    - subitem 2\n\
2. item 2\n\
\n\
## Task Listing\
\n\n\
- [ ] item 1\n\
- [x] item 2\n\
\n\n\
# Tables\
\n\n\
First Column Header | Second Column Header | Third Column\n\
------------------- | -------------------- | ------------\n\
Content from cell 1 | | Content from cell 3\n\
Another cell 1 | Another cell 2\n\
\n\n\
# Links\
\n\n\
* [text of the link](http://hackguides.org)\n\
* http://hackguides.org\
\n\n\n\
# Images and Files\
\n\n\
![alt text](http://tutorials.pluralsight.com/static/img/dark-logo.png 'Logo Title')\
\n\n\n\
# Horizontal rules\
\n\n\
------------------------\
\n\n\
or\
\n\n\
* * *\
\n\n\
or\
\n\n\
*****\
\n\n\
";

// Article data
var editor;
var author_name;
var author_real_name;
// Auto-save
var current_local_filename;
var autosaveEnabled = true;
// Preview
var preview = null;
var editor_wrapper = null;
var updatingPreview = false;
// Markdown tutorial
var liveTutorialEnabled = false;
// Scroll Sync
var scrollSyncEnabled = false;
// Virtual DOM
var vdom = window.virtualDom
var html2vtree = window.vdomParser;
var currentVTree = null;
var previewRootDomNode = null;

var help_sections;
var isHelpEnabled = false;

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        if (isFunction(wait)) {
            timeout = setTimeout(later, wait());
        } else {
            timeout = setTimeout(later, wait);
        }
        if (callNow) func.apply(context, args);
    };
};

function getUpdatePreviewDelay() {
    var delay = 0;
    var cursorLine = editor.selection.getCursor()['row'];
    var totalOfLines = editor.session.getLength();
    var linesAfterCursor = totalOfLines - cursorLine;
    if (linesAfterCursor > 2000) {
        delay = 2000 - 200;
    } else if (linesAfterCursor > 1000) {
        delay = 1000 - 200;
    } else if (linesAfterCursor > 500) {
        delay = 500 - 200;
    }
    return delay;
}

var updatePreview = function() {
    if (updatingPreview) {
        return;
    }
    updatingPreview = true;

    console.clear()
    var start, end, time = 0
    start = new Date().getTime();
    var newHtml = markdown2html(editor.getSession().getValue())
    end = new Date().getTime();
    time = end - start;
    console.log('markdown to html: ' + time + 'ms')

    start = new Date().getTime();
    newVTree = html2vtree('
' + newHtml + '
', 'key')
    end = new Date().getTime();
    time = end - start;
    console.log('html to vdom: ' + time + 'ms')
    console.log('vdom: ' + newVTree.children.length + ' nodes')

    if (! currentVTree) {
        currentVTree = newVTree
        previewRootDomNode = vdom.create(currentVTree)
        preview.appendChild(previewRootDomNode);
    }

    start = new Date().getTime();
    var patches = vdom.diff(currentVTree, newVTree);
    end = new Date().getTime();
    time = end - start;
    console.log('diff: ' + (Object.keys(patches).length-1) + ' nodes')
    console.log('diff: ' + time + 'ms')

    start = new Date().getTime();
    previewRootDomNode = vdom.patch(previewRootDomNode, patches);
    end = new Date().getTime();
    time = end - start;
    console.log('patch: ' + time + 'ms')

    start = new Date().getTime();
    currentVTree = newVTree;
    // $(preview).find('pre code').each(function(i, e) {hljs.highlightBlock(e)});
    scrollPreviewAccordingToEditor()
    end = new Date().getTime();
    time = end - start;
    console.log('fixed variable and scroll: ' + time + 'ms')
    console.log('<< Preview updated')
    updatingPreview = false;
};

var loadAutoSave = function(local_filename) {
    var obj = localStorage.getItem('hack.guides');
    if (obj) {
        obj = JSON.parse(obj);
        return obj[local_filename]; // markdown content or undefined
    }
    return undefined;
}

var autoSave = function(local_filename) {
    var content_as_markdown = editor.getSession().getValue();
    var obj = localStorage.getItem('hack.guides') || '{}';
    obj = JSON.parse(obj);
    obj[local_filename] = content_as_markdown;
    localStorage.setItem('hack.guides', JSON.stringify(obj));
};

var clearLocalSave = function(local_filename) {
    var obj = localStorage.getItem('hack.guides');
    if (obj) {
        obj = JSON.parse(obj);
        delete obj[local_filename];
        localStorage.setItem('hack.guides', JSON.stringify(obj));
    }
    return undefined;
}

function initialize_editor(local_filename, content, name, real_name, img_upload_url) {
    author_name = name;
    author_real_name = real_name;
    current_local_filename = local_filename;

    preview = document.getElementById('preview');
    editor_wrapper = document.getElementById('editor-wrapper');
    toggleScrollSync(); // enable auto sync

    editor = ace.edit("editor");
    editor.setTheme("ace/theme/github");
    editor.getSession().setMode("ace/mode/markdown");
    editor.getSession().setUseWrapMode(true);
    editor.getSession().setNewLineMode("unix");
    // Manage editor size
    editor.setOption('minLines', 1);
    $(window).resize(resizeEditor);
    resizeEditor()
    editor.$blockScrolling = Infinity;
    // Editor layout features
    editor.setShowPrintMargin(false);
    editor.renderer.setShowGutter(true);
    editor.renderer.setOption('showLineNumbers', true);

    var placeholder = '# Start writing your guide here.\n\nOr load the live markdown tutorial to check the syntax.';
    var local_content = loadAutoSave(local_filename);
    // local content should always be the same or the most updated version.
    editor.setValue(local_content || content || placeholder);
    editor.gotoLine(1);
    updatePreview();

    if (content && ! local_content) {
        autoSave(local_filename);
    }

    editor.getSession().on('change', debounce(updatePreview, getUpdatePreviewDelay));
    editor.getSession().on('change', debounce(function() {
        if (autosaveEnabled) {
            autoSave(local_filename);
        }
    }, 2000));

    editor.getSession().on('changeScrollTop', function(scrollTop) {
        scrollPreviewAccordingToEditor(scrollTop)
    })

    configure_dropzone_area(img_upload_url);

    return editor;
}

function getAceEditorScrollHeight() {
    var r = editor.renderer
    return r.layerConfig.maxHeight - r.$size.scrollerHeight + r.scrollMargin.bottom
}

function configure_dropzone_area(img_upload_url) {
    Dropzone.autoDiscover = false;
    var dropZoneOptions = {
        url: img_upload_url,
        paramName: 'file',
        maxFilesize: 3, // MB
        uploadMultiple: false,
        disablePreview: false,
        createImageThumbnails: false,
        addRemoveLinks: false,
        previewTemplate: document.querySelector('#preview-template').innerHTML,
        clickable: '.btn-dropzone',
        accept: function(file, done) {
            if (file.name.endsWith('.exe') || file.name.endsWith('.bin') || file.name.endsWith('.bat')) {
                done("File not supported");
            }
            else {
                done();
            }
        }
    };
    var myDropzone = new Dropzone("div#droppable-area", dropZoneOptions);
    myDropzone.on('success', function(file, path) {
        // Add Markdown reference into the editor
        var fileMarkdown = '\n![description](' + path + ')\n';
        editor.insert(fileMarkdown);
    });

    myDropzone.on("complete", function(file) {
        myDropzone.removeFile(file);
    });

    return myDropzone;
}

function openLiveMarkdownTutorial() {
    autosaveEnabled = false;
    editor.getSession().setValue(MARKDOWN_TUTORIAL);
    $('.btn-save').prop('disabled', true);
}

function closeLiveMarkdownTutorial() {
    editor.setValue(loadAutoSave(current_local_filename) || '');
    autosaveEnabled = true;
    $('.btn-save').prop('disabled', false);
}

function toggleLiveTutorial() {
    if (liveTutorialEnabled) {
        closeLiveMarkdownTutorial();
    } else {
        openLiveMarkdownTutorial();
    }
    liveTutorialEnabled = ! liveTutorialEnabled;
}

function scrollPreviewAccordingToEditor(scrollTop) {
    if (scrollSyncEnabled) {
        scrollTop = scrollTop || editor.session.getScrollTop()
        var editorHeight = getAceEditorScrollHeight()
        var percentage = scrollTop / editorHeight

        preview.scrollTop = Math.round(percentage * (preview.scrollHeight - preview.offsetHeight))
    }
}

function scrollEditorAccordingToPreview() {
    $(preview).off('scroll')

    var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight)

    var editorHeight = getAceEditorScrollHeight()
    var position = Math.round(percentage * editorHeight)
    editor.getSession().setScrollTop(position)

    setTimeout(function() { $(preview).on('scroll', scrollEditorAccordingToPreview); }, 10);
}

function toggleScrollSync() {
    if (scrollSyncEnabled) {
        $(preview).off('scroll', scrollEditorAccordingToPreview);
    } else {
        $(preview).on('scroll', scrollEditorAccordingToPreview);
    }
    scrollSyncEnabled = ! scrollSyncEnabled;
}

function openFullscreen() {
    $('html, body').addClass('body-fs');
    resizeEditor()
}

function resizeEditor() {
    var lineHeight = editor.renderer.lineHeight
    var maxLines = document.getElementById('editor-wrapper').offsetHeight / lineHeight
    editor.setOption('maxLines', Math.floor(maxLines) - 1);
    editor.resize()
};

var clearFlashMessages = function(message, clazz) {
    $('.bg-info, .bg-warning, .bg-danger').remove();
};

/* This requires Twitter bootstraps Modal.js! */
var addModalMessage = function(message) {
    $('#modal-content').html('
' + message + '
');
    $('#modal-error').modal()
};

function save(sha, path, secondary_repo) {
    clearFlashMessages();
    var data = {
        'title': $('input[name=title]').val(),
        'original_stack': $('input[name=original_stack]').val(),
        'stacks': $('#stacks').val(),
        'content': editor.getSession().getValue(),
        'sha': sha,
        'path': path
    }
    if (secondary_repo) {
        data['secondary_repo'] = 1;
    }
    $.ajax({
        type: 'POST',
        url: '/api/save/',
        contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
        data: data,
        dataType: 'json',
        cache: false,
        beforeSend: function(xhr) {
            $('.btn-save').prop('disabled', true);
            $('html, body').css("cursor", "wait");
            return true;
        },
        complete: function(xhr, txt_status) {
            $('html, body').css("cursor", "auto");
            $('.btn-save').prop('disabled', false);
        },
        success: function(data) {
            console.log(data);
            console.log(data.msg);
            clearLocalSave(current_local_filename);
            setTimeout(function(){ window.location.href = data.redirect; }, 1000);
        },
        error: function(response) {
            var status = response.status;
            var data = response.responseJSON;
            console.log(status, data);
            if (data && data.error) {
                addModalMessage(data.error);
            }
            if (data && data.redirect) {
                setTimeout(function(){ window.location.href = data.redirect; }, 1000);
            }
        },
    });
}

function visible_section_idx() {
    for (var ii=0; ii < help_sections.length; ii++) {
        if ($(help_sections[ii]).css('display') == 'block') {
            return ii;
        }
    }

    return -1;
}

function goto_section(next_section) {
    for (var ii=0; ii < help_sections.length; ii++) {
        if (next_section == ii ) {
            $(help_sections[ii]).css('display', 'block');
        } else {
            $(help_sections[ii]).css('display', 'none');
        }
    }
}

function show_all_help_sections(should_show) {
    var display = 'none';
    if (should_show) {
        display = 'block';
    }

    for (var ii=0; ii < help_sections.length; ii++) {
        $(help_sections[ii]).css('display', display);
    }
}

function toggleHelp() {
    if (isHelpEnabled) {
        hideHelp();
    } else {
        showHelp();
    }
}

function showHelp() {
    $('#editor-help').fadeIn('fast');
    $('#editor-help').show();
    isHelpEnabled = true;
}

function hideHelp() {
    $('#editor-help').fadeOut('fast');
    $('#editor-help').hide();
    isHelpEnabled = false;
}

/* Show each section 1 at a time in help. This only works for full-screen mode
 * because these buttons are visible otherwise. */
function init_editor_help() {
    help_sections = $('#editor-help').find('.section');
    goto_section(0);

    $('#editor-help #next').click(function() {
        var curr_section = visible_section_idx();
        var next_section = curr_section + 1;

        if (next_section >= help_sections.length) {
            next_section = 0;
        }

        goto_section(next_section);
    });

    $('#editor-help #prev').click(function() {
        var curr_section = visible_section_idx();
        var next_section = curr_section - 1;

        if (next_section < 0) {
            next_section = help_sections.length - 1;
        }

        goto_section(next_section);
    });
}
<|endoftext|>


<commit_before>(function(){"use strict";var e=typeof window!="undefined"?window:global;if(typeof e.require=="function")return;var t={},n={},r=function(e,t){return{}.hasOwnProperty.call(e,t)},i=function(e,t){var n=[],r,i;/^\.\.?(\/|$)/.test(t)?r=[e,t].join("/").split("/"):r=t.split("/");for(var s=0,o=r.length;s<o;s++)i=r[s],i===".."?n.pop():i!=="."&&i!==""&&n.push(i);return n.join("/")},s=function(e){return e.split("/").slice(0,-1).join("/")},o=function(t){return function(n){var r=s(t),o=i(r,n);return e.require(o,t)}},u=function(e,t){var r={id:e,exports:{}};return n[e]=r,t(r.exports,o(e),r),r.exports},a=function(e,s){var o=i(e,".");s==null&&(s="/");if(r(n,o))return n[o].exports;if(r(t,o))return u(o,t[o]);var a=i(o,"./index");if(r(n,a))return n[a].exports;if(r(t,a))return u(a,t[a]);throw new Error('Cannot find module "'+e+'" from '+'"'+s+'"')},f=function(e,n){if(typeof e=="object")for(var i in e)r(e,i)&&(t[i]=e[i]);else t[e]=n},l=function(){var e=[];for(var n in t)r(t,n)&&e.push(n);return e};e.require=a,e.require.define=f,e.require.register=f,e.require.list=l,e.require.brunch=!0})(),require.register("Application",function(e,t,n){var r={initialize:function(){var e=t("views/HomeView"),n=t("routers/ApplicationRouter");this.homeView=new e,this.applicationRouter=new n,typeof Object.freeze=="function"&&Object.freeze(this)}};n.exports=r}),require.register("config/ApplicationConfig",function(e,t,n){var r=function(){var e="/";return{BASE_URL:e}}.call();n.exports=r}),require.register("core/Collection",function(e,t,n){var r=Backbone.Collection.extend({model:Model});n.exports=r}),require.register("core/Model",function(e,t,n){var r=Backbone.Model.extend({defaults:{id:" ",children:"[]",related:"[]",childrenNumber:0,relatedNumber:0}});n.exports=r}),require.register("core/Router",function(e,t,n){var r=Backbone.Router.extend({routes:{},initialize:function(e){}});n.exports=r}),require.register("core/View",function(e,t,n){var r=Backbone.View.extend({template:function(){},initialize:function(){_.bindAll(this)},render:function(){return this.$el.html(this.template()),this}});n.exports=r}),require.register("events/Event",function(e,t,n){var r={APPLICATION_INITIALIZED:"onApplicationInitialized"};n.exports=r}),require.register("helpers/ViewHelper",function(e,t,n){Handlebars.registerHelper("link",function(e,t){e=Handlebars.Utils.escapeExpression(e),t=Handlebars.Utils.escapeExpression(t);var n=''+e+"";return new Handlebars.SafeString(n)})}),require.register("initialize",function(e,t,n){var r=t("Application");$(function(){r.initialize(),Backbone.history.start(),$("#infovis").length>0&&(init(),r.homeView.initSearchBox())})}),require.register("routers/ApplicationRouter",function(e,t,n){var r=t("core/Router"),i=t("Application"),s=r.extend({routes:{"":"home","/api/getnarrowerconcepts/:node":"getNarrowerConcepts"},home:function(){$("body").html(i.homeView.render().el)},getNarrowerConcepts:function(e){console.log(e)}});n.exports=s}),require.register("utils/BackboneView",function(e,t,n){var r=t("core/View"),i=t("templates/HomeViewTemplate"),s=r.extend({id:"view",template:i,initialize:function(){this.render=_.bind(this.render,this)},render:function(){return this.$el.html(this.template({content:"View Content"})),this}});n.exports=s}),require.register("views/HomeView",function(e,t,n){var r=t("core/View"),i=t("templates/homeViewTemplate"),s=r.extend({id:"home-view",template:i,initialize:function(){_.bindAll(this)},events:{},render:function(){return this.$el.html(this.template({})),this},initSearchBox:function(){sURL="/api",$("#selector").select2({width:"80%",placeholder:"Select user",minimumInputLength:1,ajax:{url:sURL,cache:!0,dataType:"json",data:function(e,t){return{q:e}},results:function(e,t){return{results:e}}},formatResult:function(e){return"aaa"},formatSelection:function(e){return"bbbb"},id:function(e){return"aaaa"},dropdownCssClass:"bigdrop"})}});n.exports=s})<commit_msg>created dummy test data from front<commit_after>(function(){"use strict";var e=typeof window!="undefined"?window:global;if(typeof e.require=="function")return;var t={},n={},r=function(e,t){return{}.hasOwnProperty.call(e,t)},i=function(e,t){var n=[],r,i;/^\.\.?(\/|$)/.test(t)?r=[e,t].join("/").split("/"):r=t.split("/");for(var s=0,o=r.length;s<o;s++)i=r[s],i===".."?n.pop():i!=="."&&i!==""&&n.push(i);return n.join("/")},s=function(e){return e.split("/").slice(0,-1).join("/")},o=function(t){return function(n){var r=s(t),o=i(r,n);return e.require(o,t)}},u=function(e,t){var r={id:e,exports:{}};return n[e]=r,t(r.exports,o(e),r),r.exports},a=function(e,s){var o=i(e,".");s==null&&(s="/");if(r(n,o))return n[o].exports;if(r(t,o))return u(o,t[o]);var a=i(o,"./index");if(r(n,a))return n[a].exports;if(r(t,a))return u(a,t[a]);throw new Error('Cannot find module "'+e+'" from '+'"'+s+'"')},f=function(e,n){if(typeof e=="object")for(var i in e)r(e,i)&&(t[i]=e[i]);else t[e]=n},l=function(){var e=[];for(var n in t)r(t,n)&&e.push(n);return e};e.require=a,e.require.define=f,e.require.register=f,e.require.list=l,e.require.brunch=!0})(),require.register("Application",function(e,t,n){var r={initialize:function(){var e=t("views/HomeView"),n=t("routers/ApplicationRouter");this.homeView=new e,this.applicationRouter=new n,typeof Object.freeze=="function"&&Object.freeze(this)}};n.exports=r}),require.register("config/ApplicationConfig",function(e,t,n){var r=function(){var e="/";return{BASE_URL:e}}.call();n.exports=r}),require.register("core/Collection",function(e,t,n){var r=Backbone.Collection.extend({model:Model});n.exports=r}),require.register("core/Model",function(e,t,n){var r=Backbone.Model.extend({defaults:{id:" ",name:" ",children:"[]",related:"[]",childrenNumber:0,relatedNumber:0}});n.exports=r}),require.register("core/Router",function(e,t,n){var r=Backbone.Router.extend({routes:{},initialize:function(e){}});n.exports=r}),require.register("core/View",function(e,t,n){var r=Backbone.View.extend({template:function(){},initialize:function(){_.bindAll(this)},render:function(){return this.$el.html(this.template()),this}});n.exports=r}),require.register("events/Event",function(e,t,n){var r={APPLICATION_INITIALIZED:"onApplicationInitialized"};n.exports=r}),require.register("helpers/ViewHelper",function(e,t,n){Handlebars.registerHelper("link",function(e,t){e=Handlebars.Utils.escapeExpression(e),t=Handlebars.Utils.escapeExpression(t);var n=''+e+"";return new Handlebars.SafeString(n)})}),require.register("initialize",function(e,t,n){var r=t("Application");$(function(){r.initialize(),Backbone.history.start(),$("#infovis").length>0&&(init(),r.homeView.initSearchBox())})}),require.register("routers/ApplicationRouter",function(e,t,n){var r=t("core/Router"),i=t("Application"),s=r.extend({routes:{"":"home","/api/getnarrowerconcepts/:node":"getNarrowerConcepts"},home:function(){$("body").html(i.homeView.render().el)},getNarrowerConcepts:function(e){console.log(e)}});n.exports=s}),require.register("utils/BackboneView",function(e,t,n){var r=t("core/View"),i=t("templates/HomeViewTemplate"),s=r.extend({id:"view",template:i,initialize:function(){this.render=_.bind(this.render,this)},render:function(){return this.$el.html(this.template({content:"View Content"})),this}});n.exports=s}),require.register("views/HomeView",function(e,t,n){var r=t("core/View"),i=t("templates/homeViewTemplate"),s=r.extend({id:"home-view",template:i,initialize:function(){_.bindAll(this)},events:{},render:function(){return this.$el.html(this.template({})),this},initSearchBox:function(){sURL="/api",$("#selector").select2({width:"80%",placeholder:"Select user",minimumInputLength:1,ajax:{url:sURL,cache:!0,dataType:"json",data:function(e,t){return{q:e}},results:function(e,t){return{results:e}}},formatResult:function(e){return"aaa"},formatSelection:function(e){return"bbbb"},id:function(e){return"aaaa"},dropdownCssClass:"bigdrop"})}});n.exports=s})<|endoftext|>