<commit_before>const mongoose = require('mongoose')
const config = require('../../config.js')
const fs = require('fs')
const path = require('path')
const log = require('../../util/logger.js')
const packageVersion = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', '..', 'package.json'))).version

/**
 * @typedef {import('mongoose').Model<import('mongoose').Document, {}>} MongooseModel
 */

class Base {
  /**
   * Object data
   * @param {MongooseModel|Object<string, any>} data
   */
  constructor (data = {}) {
    this.data = data

    /**
     * MongoDB's generated ID if instantiated with a model
     * @type {string}
     */
    this._id = this.getField('_id')

    /**
     * The bot version this data model was created on
     * @type {string}
     */
    this.version = packageVersion

    // Run the get method and throw its associated error if unimplemented
    void this.constructor.Model
  }

  /**
   * Remove the internal version key when finding from database
   */
  static FIND_PROJECTION () {
    return { __v: 0 }
  }

  /**
   * Returns the Mongoose model specific to the class
   * @returns {MongooseModel}
   */
  static get Model () {
    throw new Error('Model static get method must be implemented by subclasses')
  }

  /**
   * Checks whether the app is databaseless
   * @returns {boolean}
   */
  static get isMongoDatabase () {
    return config.database.uri.startsWith('mongo')
  }

  /**
   * Get the folder paths of the intended fs location the data will
   * be written to.
   * @returns {string[]}
   */
  static getFolderPaths () {
    const folderPath = config.database.uri
    const subfolderPath = path.join(folderPath, this.Model.collection.collectionName)
    return [ folderPath, subfolderPath ]
  }

  /**
   * Check whether the data has been written to the database or file
   * @returns {boolean}
   */
  isSaved () {
    if (this.constructor.isMongoDatabase) {
      return !!this._id && this.data instanceof mongoose.Model
    } else {
      return !!this._id
    }
  }

  /**
   * Resolves data acquisition based on whether the app is databaseless
   * @param {string} field - The field to get
   * @param {*} def - The default value if there is no value
   * @returns {*} - The field value
   */
  getField (field, def) {
    const value = this.data instanceof mongoose.Model
      ? this.data.get(field) : this.data[field]
    return value === undefined || value === null ? def : value
  }

  /**
   * Convert data into a plain object
   * @returns {Object<string, any>} - The plain object
   */
  toObject () {
    throw new Error('Method must be implemented by subclasses')
  }

  /**
   * Get a document
   * @param {string} id - Guild ID
   * @returns {Base|null}
   */
  static async get (id) {
    if (!id) {
      throw new Error('Undefined id')
    }
    if (typeof id !== 'string') {
      throw new Error('id must be a string')
    }

    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.Model

    // Mongo
    if (this.isMongoDatabase) {
      const model = await DatabaseModel.findById(id).exec()
      return new this(model)
    }

    // Databaseless
    const filePath = path.join(config.database.uri, `${id}.json`)
    if (!fs.existsSync(filePath)) {
      return null
    }

    try {
      const readContent = fs.readFileSync(filePath)
      return new this(JSON.parse(readContent))
    } catch (err) {
      log.general.warning(`Could not parse ${DatabaseModel.collection.collectionName} JSON from file ${id}`, err)
      return null
    }
  }

  /**
   * Get multiple documents
   * @param {string[]} ids - Array of guild IDs
   * @returns {Base[]}
   */
  static async getMany (ids) {
    return Promise.all(ids.map(id => this.get(id)))
  }

  /**
   * Get all documents
   * @returns {Base[]}
   */
  static async getAll () {
    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.Model

    // Mongo
    if (this.isMongoDatabase) {
      const documents = await DatabaseModel.find({}, this.FIND_PROJECTION).exec()
      return documents.map(doc => new this(doc))
    }

    // Databaseless
    const folderPaths = this.getFolderPaths()
    const fullPath = folderPaths[folderPaths.length - 1]
    if (!fs.existsSync(fullPath)) {
      return []
    }
    const fileReads = fs.readdirSync(fullPath)
      .filter(id => /\.json$/.test(id))
      .map(fileName => this.get(fileName.replace(/\.json/i, '')))

    return Promise.all(fileReads)
  }

  /**
   * Deletes this from either the database from the file system
   * depending on whether the app is databaseless.
   */
  async delete () {
    if (!this.isSaved()) {
      throw new Error('Data has not been saved')
    }

    // Mongo
    if (this.constructor.isMongoDatabase) {
      await this.data.remove()
      return
    }

    const Model = this.constructor.Model

    // Databaseless
    const paths = this.constructor.getFolderPaths()
    const folderPath = paths[paths.length - 1]
    const filePath = path.join(folderPath, `${this._id}.json`)
    if (!fs.existsSync(filePath)) {
      log.general.warning(`Unable to delete ${Model.collection.collectionName} ${this._id} at ${filePath} since its nonexistent`)
    } else {
      fs.unlinkSync(filePath)
    }
  }

  /**
   * Save the data to either the database or a file depending on whether the
   * app is databaseless.
   * @returns {Base} - This instance
   */
  async save () {
    if (this.constructor.isMongoDatabase) {
      return this.saveToDatabase()
    } else {
      return this.saveToFile()
    }
  }

  /**
   * Save the data to the database
   * @returns {Base}
   */
  async saveToDatabase () {
    const toSave = this.toObject()

    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.constructor.Model

    const options = {
      ...this.constructor.FIND_PROJECTION,
      upsert: true,
      new: true
    }

    let document
    if (!this.isSaved()) {
      const model = new DatabaseModel(toSave)
      document = await model.save()
    } else {
      document = await DatabaseModel.findByIdAndUpdate(this._id, toSave, options).exec()
    }
    this.data = document
    this._id = document._id
    return this
  }

  /**
   * Saves the data to a file
   * @returns {Base}
   */
  async saveToFile () {
    const toSave = JSON.stringify(this.toObject(), null, 2)
    const folderPaths = this.constructor.getFolderPaths()
    for (const p of folderPaths) {
      if (!fs.existsSync(p)) {
        fs.mkdirSync(p)
      }
    }
    const folderPath = folderPaths[folderPaths.length - 1]
    if (!this.isSaved()) {
      const newId = new mongoose.Types.ObjectId().toHexString()
      await fs.writeFileSync(path.join(folderPath, `${newId}.json`), toSave)
      this._id = newId
    } else {
      await fs.writeFileSync(path.join(folderPath, `${this._id}.json`), toSave)
    }
    return this
  }
}

module.exports = Base
<commit_msg>Don't set default from Base<commit_after>const mongoose = require('mongoose')
const config = require('../../config.js')
const fs = require('fs')
const path = require('path')
const log = require('../../util/logger.js')

/**
 * @typedef {import('mongoose').Model<import('mongoose').Document, {}>} MongooseModel
 */

class Base {
  /**
   * Object data
   * @param {MongooseModel|Object<string, any>} data
   */
  constructor (data = {}) {
    this.data = data

    /**
     * MongoDB's generated ID if instantiated with a model
     * @type {string}
     */
    this._id = this.getField('_id')

    /**
     * The bot version this data model was created on
     * @type {string}
     */
    this.version = this.getField('version')

    // Run the get method and throw its associated error if unimplemented
    void this.constructor.Model
  }

  /**
   * Remove the internal version key when finding from database
   */
  static FIND_PROJECTION () {
    return { __v: 0 }
  }

  /**
   * Returns the Mongoose model specific to the class
   * @returns {MongooseModel}
   */
  static get Model () {
    throw new Error('Model static get method must be implemented by subclasses')
  }

  /**
   * Checks whether the app is databaseless
   * @returns {boolean}
   */
  static get isMongoDatabase () {
    return config.database.uri.startsWith('mongo')
  }

  /**
   * Get the folder paths of the intended fs location the data will
   * be written to.
   * @returns {string[]}
   */
  static getFolderPaths () {
    const folderPath = config.database.uri
    const subfolderPath = path.join(folderPath, this.Model.collection.collectionName)
    return [ folderPath, subfolderPath ]
  }

  /**
   * Check whether the data has been written to the database or file
   * @returns {boolean}
   */
  isSaved () {
    if (this.constructor.isMongoDatabase) {
      return !!this._id && this.data instanceof mongoose.Model
    } else {
      return !!this._id
    }
  }

  /**
   * Resolves data acquisition based on whether the app is databaseless
   * @param {string} field - The field to get
   * @param {*} def - The default value if there is no value
   * @returns {*} - The field value
   */
  getField (field, def) {
    const value = this.data instanceof mongoose.Model
      ? this.data.get(field) : this.data[field]
    return value === undefined || value === null ? def : value
  }

  /**
   * Convert data into a plain object
   * @returns {Object<string, any>} - The plain object
   */
  toObject () {
    throw new Error('Method must be implemented by subclasses')
  }

  /**
   * Get a document
   * @param {string} id - Guild ID
   * @returns {Base|null}
   */
  static async get (id) {
    if (!id) {
      throw new Error('Undefined id')
    }
    if (typeof id !== 'string') {
      throw new Error('id must be a string')
    }

    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.Model

    // Mongo
    if (this.isMongoDatabase) {
      const model = await DatabaseModel.findById(id).exec()
      return new this(model)
    }

    // Databaseless
    const filePath = path.join(config.database.uri, `${id}.json`)
    if (!fs.existsSync(filePath)) {
      return null
    }

    try {
      const readContent = fs.readFileSync(filePath)
      return new this(JSON.parse(readContent))
    } catch (err) {
      log.general.warning(`Could not parse ${DatabaseModel.collection.collectionName} JSON from file ${id}`, err)
      return null
    }
  }

  /**
   * Get multiple documents
   * @param {string[]} ids - Array of guild IDs
   * @returns {Base[]}
   */
  static async getMany (ids) {
    return Promise.all(ids.map(id => this.get(id)))
  }

  /**
   * Get all documents
   * @returns {Base[]}
   */
  static async getAll () {
    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.Model

    // Mongo
    if (this.isMongoDatabase) {
      const documents = await DatabaseModel.find({}, this.FIND_PROJECTION).exec()
      return documents.map(doc => new this(doc))
    }

    // Databaseless
    const folderPaths = this.getFolderPaths()
    const fullPath = folderPaths[folderPaths.length - 1]
    if (!fs.existsSync(fullPath)) {
      return []
    }
    const fileReads = fs.readdirSync(fullPath)
      .filter(id => /\.json$/.test(id))
      .map(fileName => this.get(fileName.replace(/\.json/i, '')))

    return Promise.all(fileReads)
  }

  /**
   * Deletes this from either the database from the file system
   * depending on whether the app is databaseless.
   */
  async delete () {
    if (!this.isSaved()) {
      throw new Error('Data has not been saved')
    }

    // Mongo
    if (this.constructor.isMongoDatabase) {
      await this.data.remove()
      return
    }

    const Model = this.constructor.Model

    // Databaseless
    const paths = this.constructor.getFolderPaths()
    const folderPath = paths[paths.length - 1]
    const filePath = path.join(folderPath, `${this._id}.json`)
    if (!fs.existsSync(filePath)) {
      log.general.warning(`Unable to delete ${Model.collection.collectionName} ${this._id} at ${filePath} since its nonexistent`)
    } else {
      fs.unlinkSync(filePath)
    }
  }

  /**
   * Save the data to either the database or a file depending on whether the
   * app is databaseless.
   * @returns {Base} - This instance
   */
  async save () {
    if (this.constructor.isMongoDatabase) {
      return this.saveToDatabase()
    } else {
      return this.saveToFile()
    }
  }

  /**
   * Save the data to the database
   * @returns {Base}
   */
  async saveToDatabase () {
    const toSave = this.toObject()

    /**
     * @type {MongooseModel}
     */
    const DatabaseModel = this.constructor.Model

    const options = {
      ...this.constructor.FIND_PROJECTION,
      upsert: true,
      new: true
    }

    let document
    if (!this.isSaved()) {
      const model = new DatabaseModel(toSave)
      document = await model.save()
    } else {
      document = await DatabaseModel.findByIdAndUpdate(this._id, toSave, options).exec()
    }
    this.data = document
    this._id = document._id
    return this
  }

  /**
   * Saves the data to a file
   * @returns {Base}
   */
  async saveToFile () {
    const toSave = JSON.stringify(this.toObject(), null, 2)
    const folderPaths = this.constructor.getFolderPaths()
    for (const p of folderPaths) {
      if (!fs.existsSync(p)) {
        fs.mkdirSync(p)
      }
    }
    const folderPath = folderPaths[folderPaths.length - 1]
    if (!this.isSaved()) {
      const newId = new mongoose.Types.ObjectId().toHexString()
      await fs.writeFileSync(path.join(folderPath, `${newId}.json`), toSave)
      this._id = newId
    } else {
      await fs.writeFileSync(path.join(folderPath, `${this._id}.json`), toSave)
    }
    return this
  }
}

module.exports = Base
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 */

/**
 * @name SvgExporter
 *
 * @class The SvgExporter object holds all the functionality to convert a
 * Paper.js DOM to a SVG DOM.
 */

var SvgExporter = this.SvgExporter = new function() {

	// Shortcut to Base.formatNumber
	var formatNumber = Base.formatNumber;

	function formatPoint(point) {
		return formatNumber(point.x) + ',' + formatNumber(point.y);
	}

	function setAttributes(svg, attrs) {
		for (var key in attrs) {
			var val = attrs[key];
			if (typeof val === 'number')
				val = formatNumber(val);
			svg.setAttribute(key, val);
		}
		return svg;
	}

	function createElement(tag, attrs) {
		return setAttributes(
			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
	}

	function getDistance(segments, index1, index2) {
		return segments[index1]._point.getDistance(segments[index2]._point);
	}

	function exportGroup(group) {
		var svg = createElement('g'),
			children = group._children;
		for (var i = 0, l = children.length; i < l; i++)
			svg.appendChild(SvgExporter.exportItem(children[i]));
		// Override default SVG style on groups, then apply style.
		return setAttributes(svg, {
			fill: 'none'
		});
	}

	function exportText(item) {
		var point = item.getPoint(),
			style = item._style,
			attrs = {
				x: point._x,
				y: point._y
			};
		if (style._font != null)
			attrs['font-family'] = style._font;
		if (style._fontSize != null)
			attrs['font-size'] = style._fontSize;
		var svg = createElement('text', attrs);
		svg.textContent = item.getContent();
		svg.setAttribute('transform','rotate(' + item.matrix.getRotation() + ',' + item.getPoint()._x + ',' + item.getPoint()._y +')');
		return svg;
	}

	function exportPath(path) {
		var segments = path._segments,
			type = determineType(path, segments),
			angle = determineAngle(path, segments, type),
			attrs;
		switch (type) {
		case 'path':
			attrs = {
				d: drawPath(path, segments)
			};
			break;
		case 'polyline':
		case 'polygon':
			var parts = [];
			for(i = 0; i < segments.length; i++) {
				var point = segments[i]._point;
				parts.push(formatPoint(point));
			}
			attrs = {
				points: parts.join(' ')
			};
			break;
		case 'rect':
			var width = getDistance(segments, 0, 3),
				height = getDistance(segments, 0, 1),
				// Counter-compensate the rotation angle
				point = segments[1]._point.rotate(-angle, path.getPosition());
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height
			};
			break;
		case 'roundrect':
			type = 'rect';
			// d-variables and point are used to determine the rounded corners
			// for the rounded rectangle
			var width = getDistance(segments, 1, 6),
				height = getDistance(segments, 0, 3),
				point = path.getBounds().getTopLeft(),
				dx2 = getDistance(segments, 0, 7),
				dy2 = getDistance(segments, 1, 2),
				dx3 = (width - dx2) / 2,
				dy3 = (height - dy2) / 2,
				point = new Point((segments[3]._point._x - dx3), (segments[2]._point._y - dy3)),
				rx = segments[3]._point._x - point.x,
				ry = segments[2]._point._y - point.y;
			attrs = {
				x: point._x,
				y: point._y,
				width: width,
				height: height,
				rx: rx,
				ry: ry
			};
			break;
		case'line':
			var first = segments[0]._point,
				last = segments[segments.length - 1]._point;
			attrs = {
				x1: first._x,
				y1: first._y,
				x2: last._x,
				y2: last._y
			};
			break;
		case 'circle':
			var radius = getDistance(segments, 0, 2) / 2,
				center = path.getPosition();
			attrs = {
				cx: center._x,
				cy: center._y,
				r: radius
			};
			break;
		case 'ellipse':
			var rx = getDistance(segments, 2, 0) / 2,
				ry = getDistance(segments, 3, 1) / 2,
				center = path.getPosition();
			attrs = {
				cx: center._x,
				cy: center._y,
				rx: rx,
				ry: ry
			};
			break;
		}
		var svg = createElement(type, attrs);
		if (angle) {
			var center = path.getPosition();
			svg.setAttribute('transform', 'rotate(' + formatNumber(angle) + ','
						+ formatPoint(center) + ')');
		}
		return svg;
	}

	function drawPath(path, segments) {
		var parts = [],
			style = path._style;
		parts.push('M' + formatPoint(segments[0]._point));
		for (i = 0; i < segments.length - 1; i++)
			drawCurve(parts, segments[i], segments[i + 1], false);
		// We only need to draw the connecting curve if it is not a line, and if
		// the path is cosed and has a stroke color, or if it is filled.
		if (path._closed && style._strokeColor || style._fillColor)
			drawCurve(parts, segments[segments.length - 1], segments[0], true);
		if (path._closed)
			parts.push('z');
		return parts.join(' ');
	}

	function drawCurve(parts, seg1, seg2, skipLine) {
		var point1 = seg1._point,
			point2 = seg2._point,
			handle1 = seg1._handleOut,
			handle2 = seg2._handleIn;
		if (handle1.isZero() && handle2.isZero()) {
			if (!skipLine) {
				// L = lineto: moving to a point with drawing
				parts.push('L' + formatPoint(point2));
			}
		} else {
			// c = relative curveto: handle1, handle2 + end - start, end - start
			var end = point2.subtract(point1);
			parts.push('c' + formatPoint(handle1),
				formatPoint(end.add(handle2)),
				formatPoint(end));
		}
	}

	function determineAngle(path, segments, type) {
		// If the object is a circle, ellipse, rectangle, or rounded rectangle,
		// see if they are placed at an angle.
		var topCenter = type === 'rect'
				? segments[1]._point.add(segments[2]._point).divide(2)
				: type === 'roundrect'
				? segments[3]._point.add(segments[4]._point).divide(2)
				: type === 'circle' || type === 'ellipse'
				? segments[1]._point
				: null;
		if (topCenter) {
			var angle = topCenter.subtract(path.getPosition()).getAngle() + 90;
			return Numerical.isZero(angle) ? 0 : angle;
		}
		return 0;
	}

	function determineType(path, segments) {

		function isOrthogonal(i) {
			var segment = segments[i],
				point = segment.getPoint();
			return segment.getNext()._point.subtract(point).isOrthogonal(
					segment.getPrevious()._point.subtract(point));
		}

		function isColinear(i, j) {
			var seg1 = segments[i],
				seg2 = seg1.getNext(),
				seg3 = segments[j],
				seg4 = seg3.getNext();
			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
					&& seg2._point.subtract(seg1._point).isColinear(
						seg4._point.subtract(seg3._point));
		}

		// Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/
		var kappa = 4 * (Math.sqrt(2) - 1) / 3;

		function isArc(i) {
			var segment = segments[i],
				next = segment.getNext(),
				handle1 = segment._handleOut,
				handle2 = next._handleIn;
			if (handle1.isOrthogonal(handle2)) {
				var from = segment._point,
					to = next._point,
					corner = new Line(from, handle1).intersect(
							new Line(to, handle2));
				return corner && Numerical.isZero(handle1.getLength() /
						corner.subtract(from).getLength() - kappa)
					&& Numerical.isZero(handle2.getLength() /
						corner.subtract(to).getLength() - kappa);
			}
		}

		// See if actually have any curves in the path. Differentiate
		// between straight objects (line, polyline, rect, and  polygon) and
		// objects with curves(circle, ellipse, roundedRectangle).
		if (path.isPolygon()) {
			return  segments.length === 4 && path._closed
					&& isColinear(0, 2) && isColinear(1, 3)
					? 'rect'
					: segments.length >= 3
						? path._closed ? 'polygon' : 'polyline'
						: 'line';
		} else if (path._closed) {
			if (segments.length === 8) {
				var numArcs = 0,
					numColinears = 0;
				for (var i = 0; i < 8; i++) {
					if (isArc(i))
						numArcs++;
					var j = i + 4;
					if (isColinear(i, j >= 8 ? j - 8 : j))
						numColinears++;
				}
				if (numArcs === 4 && numColinears === 4)
					return 'roundrect';
			} else if (segments.length === 4
					&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
				// If the distance between (point0 and point2) and (point1
				// and point3) are equal, then it is a circle
				return Numerical.isZero(getDistance(segments, 0, 2)
						- getDistance(segments, 1, 3))
						? 'circle'
						: 'ellipse';
			}
		}
		return 'path';
	}

	function applyStyle(item, svg) {
		var attrs = {},
			style = item._style,
			parent = item.getParent(),
			parentStyle = parent && parent._style;

		if (item._name != null)
			attrs.id = item._name;

		Base.each(SvgStyles.properties, function(entry) {
			// Get a given style only if it differs from the value on the parent
			// (A layer or group which can have style values in SVG).
			var value = style[entry.get]();
			if (value != null && (!parentStyle
					|| !Base.equals(parentStyle[entry.get](), value))) {
				attrs[entry.attribute] = entry.type === 'color'
					? value.toCssString()
					: entry.type === 'array'
						? value.join(',')
						: entry.type === 'number'
							? formatNumber(value)
							: value;
			}
		});

		if (item._opacity != null)
			attrs.opacity = item._opacity;

		if (item._visibility != null)
			attrs._visibility = item._visibility ? 'visible' : 'hidden';

		return setAttributes(svg, attrs);
	}

	var exporters = {
		group: exportGroup,
		layer: exportGroup,
		path: exportPath,
		pointtext: exportText
		// TODO:
		// raster:
		// placedsymbol:
		// compoundpath:
	};

	return /** @Lends SvgExporter */{
		/**
		 * Takes the selected Paper.js project and parses all of its layers and
		 * groups to be placed into SVG groups, converting the project into one
		 * SVG group.
		 *
		 * @function
		 * @param {Project} project a Paper.js project
		 * @return {SVGSVGElement} the imported project converted to an SVG project
		 */
		 // TODO: Implement symbols and Gradients
		exportProject: function(project) {
			var svg = createElement('svg'),
				layers = project.layers;
			for (var i = 0, l = layers.length; i < l; i++) {
				svg.appendChild(this.exportItem(layers[i]));
			}
			return svg;
		},

		exportItem: function(item) {
			var exporter = exporters[item._type];
			// TODO: exporter == null: Not supported yet.
			var svg = exporter && exporter(item, item._type);
			return svg && applyStyle(item, svg);
		}
	};
};
<commit_msg>Remove isOrthogonal() as we don't use it anymore.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 */

/**
 * @name SvgExporter
 *
 * @class The SvgExporter object holds all the functionality to convert a
 * Paper.js DOM to a SVG DOM.
 */

var SvgExporter = this.SvgExporter = new function() {

	// Shortcut to Base.formatNumber
	var formatNumber = Base.formatNumber;

	function formatPoint(point) {
		return formatNumber(point.x) + ',' + formatNumber(point.y);
	}

	function setAttributes(svg, attrs) {
		for (var key in attrs) {
			var val = attrs[key];
			if (typeof val === 'number')
				val = formatNumber(val);
			svg.setAttribute(key, val);
		}
		return svg;
	}

	function createElement(tag, attrs) {
		return setAttributes(
			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
	}

	function getDistance(segments, index1, index2) {
		return segments[index1]._point.getDistance(segments[index2]._point);
	}

	function exportGroup(group) {
		var svg = createElement('g'),
			children = group._children;
		for (var i = 0, l = children.length; i < l; i++)
			svg.appendChild(SvgExporter.exportItem(children[i]));
		// Override default SVG style on groups, then apply style.
		return setAttributes(svg, {
			fill: 'none'
		});
	}

	function exportText(item) {
		var point = item.getPoint(),
			style = item._style,
			attrs = {
				x: point._x,
				y: point._y
			};
		if (style._font != null)
			attrs['font-family'] = style._font;
		if (style._fontSize != null)
			attrs['font-size'] = style._fontSize;
		var svg = createElement('text', attrs);
		svg.textContent = item.getContent();
		svg.setAttribute('transform','rotate(' + item.matrix.getRotation() + ',' + item.getPoint()._x + ',' + item.getPoint()._y +')');
		return svg;
	}

	function exportPath(path) {
		var segments = path._segments,
			type = determineType(path, segments),
			angle = determineAngle(path, segments, type),
			attrs;
		switch (type) {
		case 'path':
			attrs = {
				d: drawPath(path, segments)
			};
			break;
		case 'polyline':
		case 'polygon':
			var parts = [];
			for(i = 0; i < segments.length; i++) {
				var point = segments[i]._point;
				parts.push(formatPoint(point));
			}
			attrs = {
				points: parts.join(' ')
			};
			break;
		case 'rect':
			var width = getDistance(segments, 0, 3),
				height = getDistance(segments, 0, 1),
				// Counter-compensate the rotation angle
				point = segments[1]._point.rotate(-angle, path.getPosition());
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height
			};
			break;
		case 'roundrect':
			type = 'rect';
			// d-variables and point are used to determine the rounded corners
			// for the rounded rectangle
			var width = getDistance(segments, 1, 6),
				height = getDistance(segments, 0, 3),
				point = path.getBounds().getTopLeft(),
				dx2 = getDistance(segments, 0, 7),
				dy2 = getDistance(segments, 1, 2),
				dx3 = (width - dx2) / 2,
				dy3 = (height - dy2) / 2,
				point = new Point((segments[3]._point._x - dx3), (segments[2]._point._y - dy3)),
				rx = segments[3]._point._x - point.x,
				ry = segments[2]._point._y - point.y;
			attrs = {
				x: point._x,
				y: point._y,
				width: width,
				height: height,
				rx: rx,
				ry: ry
			};
			break;
		case'line':
			var first = segments[0]._point,
				last = segments[segments.length - 1]._point;
			attrs = {
				x1: first._x,
				y1: first._y,
				x2: last._x,
				y2: last._y
			};
			break;
		case 'circle':
			var radius = getDistance(segments, 0, 2) / 2,
				center = path.getPosition();
			attrs = {
				cx: center._x,
				cy: center._y,
				r: radius
			};
			break;
		case 'ellipse':
			var rx = getDistance(segments, 2, 0) / 2,
				ry = getDistance(segments, 3, 1) / 2,
				center = path.getPosition();
			attrs = {
				cx: center._x,
				cy: center._y,
				rx: rx,
				ry: ry
			};
			break;
		}
		var svg = createElement(type, attrs);
		if (angle) {
			var center = path.getPosition();
			svg.setAttribute('transform', 'rotate(' + formatNumber(angle) + ','
						+ formatPoint(center) + ')');
		}
		return svg;
	}

	function drawPath(path, segments) {
		var parts = [],
			style = path._style;
		parts.push('M' + formatPoint(segments[0]._point));
		for (i = 0; i < segments.length - 1; i++)
			drawCurve(parts, segments[i], segments[i + 1], false);
		// We only need to draw the connecting curve if it is not a line, and if
		// the path is cosed and has a stroke color, or if it is filled.
		if (path._closed && style._strokeColor || style._fillColor)
			drawCurve(parts, segments[segments.length - 1], segments[0], true);
		if (path._closed)
			parts.push('z');
		return parts.join(' ');
	}

	function drawCurve(parts, seg1, seg2, skipLine) {
		var point1 = seg1._point,
			point2 = seg2._point,
			handle1 = seg1._handleOut,
			handle2 = seg2._handleIn;
		if (handle1.isZero() && handle2.isZero()) {
			if (!skipLine) {
				// L = lineto: moving to a point with drawing
				parts.push('L' + formatPoint(point2));
			}
		} else {
			// c = relative curveto: handle1, handle2 + end - start, end - start
			var end = point2.subtract(point1);
			parts.push('c' + formatPoint(handle1),
				formatPoint(end.add(handle2)),
				formatPoint(end));
		}
	}

	function determineAngle(path, segments, type) {
		// If the object is a circle, ellipse, rectangle, or rounded rectangle,
		// see if they are placed at an angle.
		var topCenter = type === 'rect'
				? segments[1]._point.add(segments[2]._point).divide(2)
				: type === 'roundrect'
				? segments[3]._point.add(segments[4]._point).divide(2)
				: type === 'circle' || type === 'ellipse'
				? segments[1]._point
				: null;
		if (topCenter) {
			var angle = topCenter.subtract(path.getPosition()).getAngle() + 90;
			return Numerical.isZero(angle) ? 0 : angle;
		}
		return 0;
	}

	function determineType(path, segments) {

		function isColinear(i, j) {
			var seg1 = segments[i],
				seg2 = seg1.getNext(),
				seg3 = segments[j],
				seg4 = seg3.getNext();
			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
					&& seg2._point.subtract(seg1._point).isColinear(
						seg4._point.subtract(seg3._point));
		}

		// Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/
		var kappa = 4 * (Math.sqrt(2) - 1) / 3;

		function isArc(i) {
			var segment = segments[i],
				next = segment.getNext(),
				handle1 = segment._handleOut,
				handle2 = next._handleIn;
			if (handle1.isOrthogonal(handle2)) {
				var from = segment._point,
					to = next._point,
					corner = new Line(from, handle1).intersect(
							new Line(to, handle2));
				return corner && Numerical.isZero(handle1.getLength() /
						corner.subtract(from).getLength() - kappa)
					&& Numerical.isZero(handle2.getLength() /
						corner.subtract(to).getLength() - kappa);
			}
		}

		// See if actually have any curves in the path. Differentiate
		// between straight objects (line, polyline, rect, and  polygon) and
		// objects with curves(circle, ellipse, roundedRectangle).
		if (path.isPolygon()) {
			return  segments.length === 4 && path._closed
					&& isColinear(0, 2) && isColinear(1, 3)
					? 'rect'
					: segments.length >= 3
						? path._closed ? 'polygon' : 'polyline'
						: 'line';
		} else if (path._closed) {
			if (segments.length === 8) {
				var numArcs = 0,
					numColinears = 0;
				for (var i = 0; i < 8; i++) {
					if (isArc(i))
						numArcs++;
					var j = i + 4;
					if (isColinear(i, j >= 8 ? j - 8 : j))
						numColinears++;
				}
				if (numArcs === 4 && numColinears === 4)
					return 'roundrect';
			} else if (segments.length === 4
					&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
				// If the distance between (point0 and point2) and (point1
				// and point3) are equal, then it is a circle
				return Numerical.isZero(getDistance(segments, 0, 2)
						- getDistance(segments, 1, 3))
						? 'circle'
						: 'ellipse';
			}
		}
		return 'path';
	}

	function applyStyle(item, svg) {
		var attrs = {},
			style = item._style,
			parent = item.getParent(),
			parentStyle = parent && parent._style;

		if (item._name != null)
			attrs.id = item._name;

		Base.each(SvgStyles.properties, function(entry) {
			// Get a given style only if it differs from the value on the parent
			// (A layer or group which can have style values in SVG).
			var value = style[entry.get]();
			if (value != null && (!parentStyle
					|| !Base.equals(parentStyle[entry.get](), value))) {
				attrs[entry.attribute] = entry.type === 'color'
					? value.toCssString()
					: entry.type === 'array'
						? value.join(',')
						: entry.type === 'number'
							? formatNumber(value)
							: value;
			}
		});

		if (item._opacity != null)
			attrs.opacity = item._opacity;

		if (item._visibility != null)
			attrs._visibility = item._visibility ? 'visible' : 'hidden';

		return setAttributes(svg, attrs);
	}

	var exporters = {
		group: exportGroup,
		layer: exportGroup,
		path: exportPath,
		pointtext: exportText
		// TODO:
		// raster:
		// placedsymbol:
		// compoundpath:
	};

	return /** @Lends SvgExporter */{
		/**
		 * Takes the selected Paper.js project and parses all of its layers and
		 * groups to be placed into SVG groups, converting the project into one
		 * SVG group.
		 *
		 * @function
		 * @param {Project} project a Paper.js project
		 * @return {SVGSVGElement} the imported project converted to an SVG project
		 */
		 // TODO: Implement symbols and Gradients
		exportProject: function(project) {
			var svg = createElement('svg'),
				layers = project.layers;
			for (var i = 0, l = layers.length; i < l; i++) {
				svg.appendChild(this.exportItem(layers[i]));
			}
			return svg;
		},

		exportItem: function(item) {
			var exporter = exporters[item._type];
			// TODO: exporter == null: Not supported yet.
			var svg = exporter && exporter(item, item._type);
			return svg && applyStyle(item, svg);
		}
	};
};
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 * @author Stetson-Team-Alpha
 */

/**
 * @name SvgImporter
 * @class The SvgImporter object represents an object created using the SVG
 * Canvas that will be converted into a Paper.js object.
 * The SVG object is imported into Paper.js by converting it into items
 * within groups.
 *
 */
var SvgImporter = this.SvgImporter = new function() {

	function importGroup(svg) {
		var group = new Group(),
			nodes = svg.childNodes;
		for (var i = 0, l = nodes.length; i < l; i++) {
			var child = nodes[i];
			if (child.nodeType == 1) {
				var item = SvgImporter.importSvg(child);
				if (item)
					group.addChild(item);
			}
		}
		return group;
	}

	function importPoly(svg) {
		var poly = new Path(),
			points = svg.points,
			start = points.getItem(0);
		poly.moveTo(start);
		for (var i = 1, l = points.numberOfItems; i < l; i++)
			poly.lineTo(points.getItem(i));
		if (svg.nodeName.toLowerCase() == 'polygon')
			poly.closePath();
		return poly;
	}

	function getValue(svg, key, index) {
		var base = svg[key].baseVal;
		return index !== undefined
				? index < base.numberOfItems ? base.getItem(index).value || 0 : 0
				: base.value || 0;
	}

	function getPoint(svg, x, y, index) {
		return Point.create(getValue(svg, x, index), getValue(svg, y, index));
	}

	function getSize(svg, w, h, index) {
		return Size.create(getValue(svg, w, index), getValue(svg, h, index));
	}

	var importers = {
		g: importGroup,
		svg: importGroup,
		polygon: importPoly,
		polyline: importPoly,

		circle: function(svg) {
			return new Path.Circle(getPoint(svg, 'cx', 'cy'),
					getValue(svg, 'r'));
		},

		ellipse: function(svg) {
			var center = getPoint(svg, 'cx', 'cy'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.Oval(new Rectangle(center.subtract(radius),
					center.add(radius)));
		},

		rect: function(svg) {
			var point = getPoint(svg, 'x', 'y'),
				size = getSize(svg, 'width', 'height'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.RoundRectangle(new Rectangle(point, size), radius);
		},

		line: function(svg) {
			return new Path.Line(getPoint(svg, 'x1', 'y1'),
					getPoint(svg, 'x2', 'y2'));
		},

		text: function(svg) {
			var bottomLeft = getPoint(svg, 'x', 'y', 0),
				textLength = getValue(svg, 'textLength'),
				delta = getPoint(svg, 'dx', 'dy', 0);
			// Not supported by Paper.js
			// x: multiple values for x
			// y: multiple values for y
			// dx: multiple values for x
			// dy: multiple values for y
			// rotate: character rotation
			// lengthAdjust:
			var point = bottomLeft.add(delta).subtract(textLength / 2, 0);
			var text = new PointText(point);
			text.content = svg.textContent || '';
			return text;
		},

		path: function(svg) {
			var path = new Path();
			var segments = svg.pathSegList;
			var segment;
			var j;
			var relativeToPoint;
			var controlPoint;
			var prevCommand;
			var segmentTo;
			for (var i = 0; i < segments.numberOfItems; i++) {
				segment = segments.getItem(i);
				if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
					continue;
				}
				if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
					relativeToPoint = path.lastSegment.point;
				} else {
					relativeToPoint = new Point(0, 0);
				}
				segmentTo = new Point(segment.x, segment.y);
				segmentTo = segmentTo.add(relativeToPoint);
				switch (segment.pathSegType) {
				case SVGPathSeg.PATHSEG_CLOSEPATH:
					path.closePath();
					break;
				case SVGPathSeg.PATHSEG_MOVETO_ABS:
				case SVGPathSeg.PATHSEG_MOVETO_REL:
					path.moveTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_LINETO_ABS:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_REL:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
					path.lineTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
					path.cubicCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
					path.quadraticCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_ARC_ABS:
				case SVGPathSeg.PATHSEG_ARC_REL:
					//TODO: Implement Arcs.
					//TODO: Requires changes in Paper.js's Path to do.
					//TODO: http://www.w3.org/TR/SVG/implnote.html
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
					prevCommand = segments.getItem(i - 1);
					controlPoint = new Point(prevCommand.x2, prevCommand.y2);
					controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
					controlPoint = controlPoint.add(path.lastSegment.point);
					controlPoint = path.lastSegment.point.subtract(controlPoint);
					controlPoint = path.lastSegment.point.add(controlPoint);
					path.cubicCurveTo(
						controlPoint,
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
					for (j = i; j >= 0; --j) {
						prevCommand = segments.getItem(j);
						if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
							prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
						) {
							controlPoint = new Point(prevCommand.x1, prevCommand.y1);
							controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
							controlPoint = controlPoint.add(path.segments[j].point);
							break;
						}
					}
					for (j; j < i; ++j) {
						controlPoint = path.segments[j].point.subtract(controlPoint);
						controlPoint = path.segments[j].point.add(controlPoint);
					}
					path.quadraticCurveTo(controlPoint, segmentTo);
					break;
				}
			}

			return path;
		},

		symbol: function(svg) {
			var item = importGroup(svg);
			importAttributesAndStyles(svg, item);
			// TODO: We're returning a symbol. How to handle this?
			return new Symbol(item);
		}
	};

	/**
	 * Converts various SVG styles and attributes into Paper.js styles and
	 * attributes and applies them to the passed item.
	 *
	 * @param {SVGSVGElement} svg an SVG node to read style and attributes from.
	 * @param {Item} item the item to apply the style and attributes to.
	 */
	function importAttributesAndStyles(svg, item) {
		var name,
			value,
			cssName;
		for (var i = 0; i < svg.style.length; i++) {
			name = svg.style[i];
			cssName = name.replace(/-(.)/g, function(match, p) {
				return p.toUpperCase();
			});
			value = svg.style[cssName];
			applyAttributeOrStyle(name, value, item, svg);
		}
		for (var i = 0; i < svg.attributes.length; i++) {
			name = svg.attributes[i].name;
			value = svg.attributes[i].value;
			applyAttributeOrStyle(name, value, item, svg);
		}
	}

	/**
	 * Parses an SVG style attibute and applies it to a Paper.js item.
	 *
	 * @param {String} name an SVG style name
	 * @param value the value of the SVG style
	 * @param {Item} item the item to apply the style or attribute to.
	 * @param {SVGSVGElement} svg an SVG node
	 */
	 function applyAttributeOrStyle(name, value, item, svg) {
		if (!value) {
			return;
		}
		switch (name) {
		case 'id':
			item.name = value;
			break;
		case 'fill':
			if (value != 'none') {
				item.fillColor = value;
			}
			break;
		case 'stroke':
			if (value != 'none') {
				item.strokeColor = value;
			}
			break;
		case 'stroke-width':
			item.strokeWidth = parseFloat(value, 10);
			break;
		case 'stroke-linecap':
			item.strokeCap = value;
			break;
		case 'stroke-linejoin':
			item.strokeJoin = value;
			break;
		case 'stroke-dasharray':
			value = value.replace(/px/g, '');
			value = value.replace(/, /g, ',');
			value = value.replace(/ /g, ',');
			value = value.split(',');
			for (var i in value) {
				value[i] = parseFloat(value[i], 10);
			}
			item.dashArray = value;
			break;
		case 'stroke-dashoffset':
			item.dashOffset = parseFloat(value, 10);
			break;
		case 'stroke-miterlimit':
			item.miterLimit = parseFloat(value, 10);
			break;
		case 'transform':
			applyTransform(item, svg);
			break;
		case 'opacity':
			item.opacity = parseFloat(value, 10);
			break;
		case 'visibility':
			item.visibility = (value == 'visible') ? true : false;
			break;
		case 'font':
		case 'font-family':
		case 'font-size':
			//Implemented in characterStyle below.
			break;
		default:
			// Not supported yet.
			break;
		}
		if (item.characterStyle) {
			switch (name) {
			case 'font':
				var text = document.createElement('span');
				text.style.font = value;
				for (var i = 0; i < text.style.length; i++) {
					var n = text.style[i];
					applyAttributeOrStyle(n, text.style[n], item, svg);
				}
				break;
			case 'font-family':
				var fonts = value.split(',');
				fonts[0] = fonts[0].replace(/^\s+|\s+$/g, "");
				item.characterStyle.font = fonts[0];
				break;
			case 'font-size':
				item.characterStyle.fontSize = parseFloat(value, 10);
				break;
			}
		}
	}

	/**
	 * Applies the transformations specified on the SVG node to a Paper.js item
	 *
	 * @param {Item} item a Paper.js item
	 * @param {SVGSVGElement} svg an SVG node
	 */
	function applyTransform(item, svg) {
		var transforms = svg.transform.baseVal;
		var transform;
		var matrix = new Matrix();

		for (var i = 0; i < transforms.numberOfItems; i++) {
			transform = transforms.getItem(i);
			if (transform.type == SVGTransform.SVG_TRANSFORM_UNKNOWN) {
				continue;
			}
			var transformMatrix = new Matrix(
				transform.matrix.a,
				transform.matrix.c,
				transform.matrix.b,
				transform.matrix.d,
				transform.matrix.e,
				transform.matrix.f
			);
			switch (transform.type) {
			case SVGTransform.SVG_TRANSFORM_TRANSLATE:
				break;
			case SVGTransform.SVG_TRANSFORM_SCALE:
				break;
			//Compensate for SVG's theta rotation going the opposite direction
			case SVGTransform.SVG_TRANSFORM_MATRIX:
				var temp = transformMatrix.getShearX();
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(temp);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWX:
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(0);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWY:
				transformMatrix.setShearY(transformMatrix.getShearX());
				transformMatrix.setShearX(0);
				break;
			case SVGTransform.SVG_TRANSFORM_ROTATE:
				transformMatrix.setShearX(-transformMatrix.getShearX());
				transformMatrix.setShearY(-transformMatrix.getShearY());
				break;
			}
			matrix.concatenate(transformMatrix);
		}
		item.transform(matrix);
	}

	return /** @Lends SvgImporter */{
		/**
		 * Creates a Paper.js item using data parsed from the selected
		 * SVG DOM node.
		 *
		 * @param {SVGSVGElement} svg the SVG DOM node to convert
		 * @return {Item} the converted Paper.js item
		 */
		importSvg: function(svg) {
			var importer = importers[svg.nodeName.toLowerCase()];
			// TODO: importer == null: Not supported yet.
			var item = importer && importer(svg);
			if (item)
				importAttributesAndStyles(svg, item);
			return item;
		}
	};
};
<commit_msg>Improve loops.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 * @author Stetson-Team-Alpha
 */

/**
 * @name SvgImporter
 * @class The SvgImporter object represents an object created using the SVG
 * Canvas that will be converted into a Paper.js object.
 * The SVG object is imported into Paper.js by converting it into items
 * within groups.
 *
 */
var SvgImporter = this.SvgImporter = new function() {

	function importGroup(svg) {
		var group = new Group(),
			nodes = svg.childNodes;
		for (var i = 0, l = nodes.length; i < l; i++) {
			var child = nodes[i];
			if (child.nodeType == 1) {
				var item = SvgImporter.importSvg(child);
				if (item)
					group.addChild(item);
			}
		}
		return group;
	}

	function importPoly(svg) {
		var poly = new Path(),
			points = svg.points,
			start = points.getItem(0);
		poly.moveTo(start);
		for (var i = 1, l = points.numberOfItems; i < l; i++)
			poly.lineTo(points.getItem(i));
		if (svg.nodeName.toLowerCase() == 'polygon')
			poly.closePath();
		return poly;
	}

	function getValue(svg, key, index) {
		var base = svg[key].baseVal;
		return index !== undefined
				? index < base.numberOfItems ? base.getItem(index).value || 0 : 0
				: base.value || 0;
	}

	function getPoint(svg, x, y, index) {
		return Point.create(getValue(svg, x, index), getValue(svg, y, index));
	}

	function getSize(svg, w, h, index) {
		return Size.create(getValue(svg, w, index), getValue(svg, h, index));
	}

	var importers = {
		g: importGroup,
		svg: importGroup,
		polygon: importPoly,
		polyline: importPoly,

		circle: function(svg) {
			return new Path.Circle(getPoint(svg, 'cx', 'cy'),
					getValue(svg, 'r'));
		},

		ellipse: function(svg) {
			var center = getPoint(svg, 'cx', 'cy'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.Oval(new Rectangle(center.subtract(radius),
					center.add(radius)));
		},

		rect: function(svg) {
			var point = getPoint(svg, 'x', 'y'),
				size = getSize(svg, 'width', 'height'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.RoundRectangle(new Rectangle(point, size), radius);
		},

		line: function(svg) {
			return new Path.Line(getPoint(svg, 'x1', 'y1'),
					getPoint(svg, 'x2', 'y2'));
		},

		text: function(svg) {
			var bottomLeft = getPoint(svg, 'x', 'y', 0),
				textLength = getValue(svg, 'textLength'),
				delta = getPoint(svg, 'dx', 'dy', 0);
			// Not supported by Paper.js
			// x: multiple values for x
			// y: multiple values for y
			// dx: multiple values for x
			// dy: multiple values for y
			// rotate: character rotation
			// lengthAdjust:
			var point = bottomLeft.add(delta).subtract(textLength / 2, 0);
			var text = new PointText(point);
			text.content = svg.textContent || '';
			return text;
		},

		path: function(svg) {
			var path = new Path();
			var segments = svg.pathSegList;
			var segment;
			var j;
			var relativeToPoint;
			var controlPoint;
			var prevCommand;
			var segmentTo;
			for (var i = 0, l = segments.numberOfItems; i < l; i++) {
				segment = segments.getItem(i);
				if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
					continue;
				}
				if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
					relativeToPoint = path.lastSegment.point;
				} else {
					relativeToPoint = new Point(0, 0);
				}
				segmentTo = new Point(segment.x, segment.y);
				segmentTo = segmentTo.add(relativeToPoint);
				switch (segment.pathSegType) {
				case SVGPathSeg.PATHSEG_CLOSEPATH:
					path.closePath();
					break;
				case SVGPathSeg.PATHSEG_MOVETO_ABS:
				case SVGPathSeg.PATHSEG_MOVETO_REL:
					path.moveTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_LINETO_ABS:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_REL:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
					path.lineTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
					path.cubicCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
					path.quadraticCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_ARC_ABS:
				case SVGPathSeg.PATHSEG_ARC_REL:
					//TODO: Implement Arcs.
					//TODO: Requires changes in Paper.js's Path to do.
					//TODO: http://www.w3.org/TR/SVG/implnote.html
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
					prevCommand = segments.getItem(i - 1);
					controlPoint = new Point(prevCommand.x2, prevCommand.y2);
					controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
					controlPoint = controlPoint.add(path.lastSegment.point);
					controlPoint = path.lastSegment.point.subtract(controlPoint);
					controlPoint = path.lastSegment.point.add(controlPoint);
					path.cubicCurveTo(
						controlPoint,
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
					for (j = i; j >= 0; --j) {
						prevCommand = segments.getItem(j);
						if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
							prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
						) {
							controlPoint = new Point(prevCommand.x1, prevCommand.y1);
							controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
							controlPoint = controlPoint.add(path.segments[j].point);
							break;
						}
					}
					for (j; j < i; ++j) {
						controlPoint = path.segments[j].point.subtract(controlPoint);
						controlPoint = path.segments[j].point.add(controlPoint);
					}
					path.quadraticCurveTo(controlPoint, segmentTo);
					break;
				}
			}

			return path;
		},

		symbol: function(svg) {
			var item = importGroup(svg);
			importAttributesAndStyles(svg, item);
			// TODO: We're returning a symbol. How to handle this?
			return new Symbol(item);
		}
	};

	/**
	 * Converts various SVG styles and attributes into Paper.js styles and
	 * attributes and applies them to the passed item.
	 *
	 * @param {SVGSVGElement} svg an SVG node to read style and attributes from.
	 * @param {Item} item the item to apply the style and attributes to.
	 */
	function importAttributesAndStyles(svg, item) {
		var name,
			value,
			cssName;
		for (var i = 0; i < svg.style.length; i++) {
			name = svg.style[i];
			cssName = name.replace(/-(.)/g, function(match, p) {
				return p.toUpperCase();
			});
			value = svg.style[cssName];
			applyAttributeOrStyle(name, value, item, svg);
		}
		for (var i = 0; i < svg.attributes.length; i++) {
			name = svg.attributes[i].name;
			value = svg.attributes[i].value;
			applyAttributeOrStyle(name, value, item, svg);
		}
	}

	/**
	 * Parses an SVG style attibute and applies it to a Paper.js item.
	 *
	 * @param {String} name an SVG style name
	 * @param value the value of the SVG style
	 * @param {Item} item the item to apply the style or attribute to.
	 * @param {SVGSVGElement} svg an SVG node
	 */
	 function applyAttributeOrStyle(name, value, item, svg) {
		if (!value) {
			return;
		}
		switch (name) {
		case 'id':
			item.name = value;
			break;
		case 'fill':
			if (value != 'none') {
				item.fillColor = value;
			}
			break;
		case 'stroke':
			if (value != 'none') {
				item.strokeColor = value;
			}
			break;
		case 'stroke-width':
			item.strokeWidth = parseFloat(value, 10);
			break;
		case 'stroke-linecap':
			item.strokeCap = value;
			break;
		case 'stroke-linejoin':
			item.strokeJoin = value;
			break;
		case 'stroke-dasharray':
			value = value.replace(/px/g, '');
			value = value.replace(/, /g, ',');
			value = value.replace(/ /g, ',');
			value = value.split(',');
			for (var i in value) {
				value[i] = parseFloat(value[i], 10);
			}
			item.dashArray = value;
			break;
		case 'stroke-dashoffset':
			item.dashOffset = parseFloat(value, 10);
			break;
		case 'stroke-miterlimit':
			item.miterLimit = parseFloat(value, 10);
			break;
		case 'transform':
			applyTransform(item, svg);
			break;
		case 'opacity':
			item.opacity = parseFloat(value, 10);
			break;
		case 'visibility':
			item.visibility = (value == 'visible') ? true : false;
			break;
		case 'font':
		case 'font-family':
		case 'font-size':
			//Implemented in characterStyle below.
			break;
		default:
			// Not supported yet.
			break;
		}
		if (item.characterStyle) {
			switch (name) {
			case 'font':
				var text = document.createElement('span');
				text.style.font = value;
				for (var i = 0; i < text.style.length; i++) {
					var n = text.style[i];
					applyAttributeOrStyle(n, text.style[n], item, svg);
				}
				break;
			case 'font-family':
				var fonts = value.split(',');
				fonts[0] = fonts[0].replace(/^\s+|\s+$/g, "");
				item.characterStyle.font = fonts[0];
				break;
			case 'font-size':
				item.characterStyle.fontSize = parseFloat(value, 10);
				break;
			}
		}
	}

	/**
	 * Applies the transformations specified on the SVG node to a Paper.js item
	 *
	 * @param {Item} item a Paper.js item
	 * @param {SVGSVGElement} svg an SVG node
	 */
	function applyTransform(item, svg) {
		var transforms = svg.transform.baseVal;
		var transform;
		var matrix = new Matrix();

		for (var i = 0, l = transforms.numberOfItems; i < l; i++) {
			transform = transforms.getItem(i);
			if (transform.type == SVGTransform.SVG_TRANSFORM_UNKNOWN) {
				continue;
			}
			var transformMatrix = new Matrix(
				transform.matrix.a,
				transform.matrix.c,
				transform.matrix.b,
				transform.matrix.d,
				transform.matrix.e,
				transform.matrix.f
			);
			switch (transform.type) {
			case SVGTransform.SVG_TRANSFORM_TRANSLATE:
				break;
			case SVGTransform.SVG_TRANSFORM_SCALE:
				break;
			//Compensate for SVG's theta rotation going the opposite direction
			case SVGTransform.SVG_TRANSFORM_MATRIX:
				var temp = transformMatrix.getShearX();
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(temp);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWX:
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(0);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWY:
				transformMatrix.setShearY(transformMatrix.getShearX());
				transformMatrix.setShearX(0);
				break;
			case SVGTransform.SVG_TRANSFORM_ROTATE:
				transformMatrix.setShearX(-transformMatrix.getShearX());
				transformMatrix.setShearY(-transformMatrix.getShearY());
				break;
			}
			matrix.concatenate(transformMatrix);
		}
		item.transform(matrix);
	}

	return /** @Lends SvgImporter */{
		/**
		 * Creates a Paper.js item using data parsed from the selected
		 * SVG DOM node.
		 *
		 * @param {SVGSVGElement} svg the SVG DOM node to convert
		 * @return {Item} the converted Paper.js item
		 */
		importSvg: function(svg) {
			var importer = importers[svg.nodeName.toLowerCase()];
			// TODO: importer == null: Not supported yet.
			var item = importer && importer(svg);
			if (item)
				importAttributesAndStyles(svg, item);
			return item;
		}
	};
};
<|endoftext|>


<commit_before>///
// Dependencies
///

import * as formsActions from '../forms/actions';

///
// Action types
///

export const GET_LIST_START = 'TABSETS:GET_LIST_START';
export const GET_LIST_REJECTED = 'TABSETS:GET_LIST_REJECTED';
export const GET_LIST_FULFILLED = 'TABSETS:GET_LIST_FULFILLED';

export const GET_ITEM_START = 'TABSETS:GET_ITEM_START';
export const GET_ITEM_REJECTED = 'TABSETS:GET_ITEM_REJECTED';
export const GET_ITEM_FULFILLED = 'TABSETS:GET_ITEM_FULFILLED';

export const UPDATE_LIST = 'TABSETS:UPDATE_LIST';

export const SUBMIT_FORM = formsActions.FORM_SUBMIT_START + ':tabsets';

///
// Action creators
///

export function getList() {
	return {type: GET_LIST_START};
}

export function getItem(id) {
	return {type: GET_ITEM_START, payload: id};
}

export function updateList(item) {
	return {type: UPDATE_LIST, payload: item};
}

<commit_msg>Added actions for adding/updating tabset tabs<commit_after>///
// Dependencies
///

import * as formsActions from '../forms/actions';

///
// Action types
///

export const GET_LIST_START = 'TABSETS:GET_LIST_START';
export const GET_LIST_REJECTED = 'TABSETS:GET_LIST_REJECTED';
export const GET_LIST_FULFILLED = 'TABSETS:GET_LIST_FULFILLED';

export const GET_ITEM_START = 'TABSETS:GET_ITEM_START';
export const GET_ITEM_REJECTED = 'TABSETS:GET_ITEM_REJECTED';
export const GET_ITEM_FULFILLED = 'TABSETS:GET_ITEM_FULFILLED';

export const UPDATE_LIST = 'TABSETS:UPDATE_LIST';

export const SUBMIT_FORM = formsActions.FORM_SUBMIT_START + ':tabsets';

export const ADD_TAB = formsActions.FORM_SUBMIT_START + ':tabsetsTabs';
export const UPDATE_TABS_LIST = 'TABSETS:ADD_TAB';

///
// Action creators
///

export function getList() {
	return {type: GET_LIST_START};
}

export function getItem(id) {
	return {type: GET_ITEM_START, payload: id};
}

export function updateList(item) {
	return {type: UPDATE_LIST, payload: item};
}

export function addTab(tabsetId, tab) {
	return {type: ADD_TAB, payload: {tabsetId, tab}};
}

export function updateTabsList(tab) {
	return {type: UPDATE_TABS_LIST, payload: tab};
}

<|endoftext|>


<commit_before>'use strict'
const defaultOptions = {
  realpath: true,
  ignore: [ 'node_modules/**', 'bower_components/**' ]
}

function filez (glob, userOptions) {
  return () => {
    return function files (log) {
      const globby = require('../glob/globbieP');

      const options = Object.assign({}, defaultOptions, userOptions)

      // return globby(glob, options, (err, result) => {
      //   log(result);
      //   return result;
      // })

      return globby(glob, options).then(result => {
        log(result);
        return result;
      })
    }
  }
}

module.exports = filez
<commit_msg>lint task/strt-files<commit_after>'use strict'

const defaultOptions = {
  realpath: true,
  ignore: ['node_modules/**', 'bower_components/**']
}

function filez (glob, userOptions) {
  return () => {
    return function files (log) {
      const globby = require('../glob/globbieP')

      const options = Object.assign({}, defaultOptions, userOptions)

      // return globby(glob, options, (err, result) => {
      //   log(result);
      //   return result;
      // })

      return globby(glob, options).then((result) => {
        log(result)
        return result
      })
    }
  }
}

module.exports = filez
<|endoftext|>


<commit_before>const debug = require('debug')('node-telegram-bot-api');
const https = require('https');
const http = require('http');
const fs = require('fs');
const bl = require('bl');

class TelegramBotWebHook {

  constructor(token, options, callback) {
    this.token = token;
    this.callback = callback;
    this.regex = new RegExp(this.token);

    // define opts
    if (typeof options === 'boolean') {
      options = {}; // eslint-disable-line no-param-reassign
    }
    options.port = options.port || 8443;

    if (options.key && options.cert) { // HTTPS Server
      debug('HTTPS WebHook enabled');
      const opts = {
        key: fs.readFileSync(options.key),
        cert: fs.readFileSync(options.cert)
      };
      this._webServer = https.createServer(opts, this._requestListener);
    } else {
      debug('HTTP WebHook enabled');
      this._webServer = http.createServer(this._requestListener);
    }

    this._webServer.listen(options.port, options.host, () => {
      debug('WebHook listening on port %s', options.port);
    });
  }

  // used so that other funcs are not non-optimizable
  _safeParse(json) {
    try {
      return JSON.parse(json);
    } catch (err) {
      debug(err);
      return null;
    }
  }

  // pipe+parse body
  _parseBody = (err, body) => {
    if (err) {
      return debug(err);
    }

    const data = this._safeParse(body);
    if (data) {
      return this.callback(data);
    }

    return null;
  }

  // bound req listener
  _requestListener = (req, res) => {
    debug('WebHook request URL:', req.url);
    debug('WebHook request headers: %j', req.headers);

    // If there isn't token on URL
    if (!this.regex.test(req.url)) {
      debug('WebHook request unauthorized');
      res.statusCode = 401;
      res.end();
    } else if (req.method === 'POST') {
      req
        .pipe(bl(this._parseBody))
        .on('end', () => res.end('OK'));
    } else {
      // Authorized but not a POST
      debug('WebHook request isn\'t a POST');
      res.statusCode = 418; // I'm a teabot!
      res.end();
    }
  }

}

module.exports = TelegramBotWebHook;
<commit_msg>Fixes stream end event never being called<commit_after>const debug = require('debug')('node-telegram-bot-api');
const https = require('https');
const http = require('http');
const fs = require('fs');
const bl = require('bl');

class TelegramBotWebHook {

  constructor(token, options, callback) {
    this.token = token;
    this.callback = callback;
    this.regex = new RegExp(this.token);

    // define opts
    if (typeof options === 'boolean') {
      options = {}; // eslint-disable-line no-param-reassign
    }
    options.port = options.port || 8443;

    if (options.key && options.cert) { // HTTPS Server
      debug('HTTPS WebHook enabled');
      const opts = {
        key: fs.readFileSync(options.key),
        cert: fs.readFileSync(options.cert)
      };
      this._webServer = https.createServer(opts, this._requestListener);
    } else {
      debug('HTTP WebHook enabled');
      this._webServer = http.createServer(this._requestListener);
    }

    this._webServer.listen(options.port, options.host, () => {
      debug('WebHook listening on port %s', options.port);
    });
  }

  // used so that other funcs are not non-optimizable
  _safeParse(json) {
    try {
      return JSON.parse(json);
    } catch (err) {
      debug(err);
      return null;
    }
  }

  // pipe+parse body
  _parseBody = (err, body) => {
    if (err) {
      return debug(err);
    }

    const data = this._safeParse(body);
    if (data) {
      return this.callback(data);
    }

    return null;
  }

  // bound req listener
  _requestListener = (req, res) => {
    debug('WebHook request URL:', req.url);
    debug('WebHook request headers: %j', req.headers);

    // If there isn't token on URL
    if (!this.regex.test(req.url)) {
      debug('WebHook request unauthorized');
      res.statusCode = 401;
      res.end();
    } else if (req.method === 'POST') {
      req
        .pipe(bl(this._parseBody))
        .on('finish', () => res.end('OK'));
    } else {
      // Authorized but not a POST
      debug('WebHook request isn\'t a POST');
      res.statusCode = 418; // I'm a teabot!
      res.end();
    }
  }

}

module.exports = TelegramBotWebHook;
<|endoftext|>


<commit_before>TestCase("Croaker.SampleTest.Tests", {
  testSomething: function() {
    assertThat([], empty());
    assertThat([1,2,3], hasItem(3));

    var user = {name: 'andy', age: 15};
    assertThat(user, equalTo(user));
    assertThat(user, hasMember('name'));
    assertThat(user, hasMember('name', 'andy'));
    assertThat(user.name, equalTo('andy'));
    assertThat(user, allOf(hasMember('name'), hasMember('age')));
  },

  setUp: function () {
    JsHamcrest.Integration.JsTestDriver();
  }
});<commit_msg>removed sample test file<commit_after><|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'

import { State, isInStateSlot } from '../core/model'
import { isReceive } from '../core/actions'
import { shouldFetch } from '../core/reducer'
import { isFunction, trueFn, identity } from '../util/object'
export const ARTEMISA = 'ARTEMISA'

export const isArtemisaType = type => type.indexOf(ARTEMISA) === 0
export const isArtemisaReceive = action => isReceive(action) && isArtemisaType(action.originType)
export const isArtemisaAction = action => isArtemisaType(action.type)

export function storagePropertyNameForAction(action) {
  return action.originType.slice(ARTEMISA.length + 1)
}

/**
 * Base class of the higher order component.
 */
class AbstractWithFetches extends React.Component {

  componentDidMount() { this.tryToFetch() }
  componentDidUpdate() { this.tryToFetch() }

  tryToFetch() {
    const { state, dispatch } = this.props;
    this.getFetches().forEach(fetch => {
      this.dispatchFetch(fetch, state, dispatch)
    })
  }

  dispatchFetch({ storeFieldName, call, on }, state, dispatch) {
    if (!on(this.props, state)) {
      return;
    }
    const action = ({
      type: `${ARTEMISA}_${storeFieldName}`,
      dataApiCall: call(this.props, state)
    })
    const should = shouldFetch(state.artemisa[storeFieldName], action.dataApiCall.path, s => s.path)
    if (should) {
      dispatch(action)
    }
  }

  render() {
    const fetched = this.mapStateToProps()
    const Wrapped = this.getWrappedComponent()
    /* eslint no-unused-vars: 0 */
    const { state, ...props } = this.props
    return <Wrapped {...props} {...fetched} />
  }

  mapStateToProps() {
    const { state } = this.props
    return this.getFetches().reduce((props, fetch) => {
      if (fetch.on(props, state)) {
        props[fetch.propName] = this.mapFetchToProp(fetch, state)
      }
      return props
    }, {})
  }

  mapFetchToProp({ storeFieldName, transforming }, state) {
    const stateValue = state.artemisa[storeFieldName]
    return isInStateSlot(stateValue, State.FETCHED) ? { ...stateValue, value: transforming(stateValue.value) } : stateValue
  }

}

/**
 * A Fetch internal Descriptor has the following form
 * {
 *    propName (always present): property name to use to inject the slot,
 *    storeFieldName (always present): the property to store the cache in the store.
 *    call (always present): function(props, state) that returns the call object
 *    transforming: optional transformation
 * }
 */
const createFetchDescriptors = fetches => Object.keys(fetches).map(propName => {
  const value = fetches[propName]
  return {
    propName,
    storeFieldName: value.name || propName,     // defaults to propName
    call: isFunction(value) ? value : value.call,
    transforming: value.transforming ? value.transforming : identity,
    on: value.on ? value.on : trueFn
  }
})

export const fetchingData = fetches => WrappedComponent => {
  const fetchDescriptors = createFetchDescriptors(fetches)
  class WithFetches extends AbstractWithFetches {
    getFetches() { return fetchDescriptors }
    getWrappedComponent() { return WrappedComponent }
  }
  WithFetches.displayName = `WithFetches(${getDisplayName(WrappedComponent)})`;
  return connect(state => ({ state }), dispatch => ({ dispatch }))(WithFetches)
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}<commit_msg>prefer use of match to make in more explicit that this is handling a string<commit_after>import React from 'react'
import { connect } from 'react-redux'

import { State, isInStateSlot } from '../core/model'
import { isReceive } from '../core/actions'
import { shouldFetch } from '../core/reducer'
import { isFunction, trueFn, identity } from '../util/object'
export const ARTEMISA = 'ARTEMISA'

export const isArtemisaType = type => !!type.match('ARTEMISA')
export const isArtemisaReceive = action => isReceive(action) && isArtemisaType(action.originType)
export const isArtemisaAction = action => isArtemisaType(action.type)

export function storagePropertyNameForAction(action) {
  return action.originType.slice(ARTEMISA.length + 1)
}

/**
 * Base class of the higher order component.
 */
class AbstractWithFetches extends React.Component {

  componentDidMount() { this.tryToFetch() }
  componentDidUpdate() { this.tryToFetch() }

  tryToFetch() {
    const { state, dispatch } = this.props;
    this.getFetches().forEach(fetch => {
      this.dispatchFetch(fetch, state, dispatch)
    })
  }

  dispatchFetch({ storeFieldName, call, on }, state, dispatch) {
    if (!on(this.props, state)) {
      return;
    }
    const action = ({
      type: `${ARTEMISA}_${storeFieldName}`,
      dataApiCall: call(this.props, state)
    })
    const should = shouldFetch(state.artemisa[storeFieldName], action.dataApiCall.path, s => s.path)
    if (should) {
      dispatch(action)
    }
  }

  render() {
    const fetched = this.mapStateToProps()
    const Wrapped = this.getWrappedComponent()
    /* eslint no-unused-vars: 0 */
    const { state, ...props } = this.props
    return <Wrapped {...props} {...fetched} />
  }

  mapStateToProps() {
    const { state } = this.props
    return this.getFetches().reduce((props, fetch) => {
      if (fetch.on(props, state)) {
        props[fetch.propName] = this.mapFetchToProp(fetch, state)
      }
      return props
    }, {})
  }

  mapFetchToProp({ storeFieldName, transforming }, state) {
    const stateValue = state.artemisa[storeFieldName]
    return isInStateSlot(stateValue, State.FETCHED) ? { ...stateValue, value: transforming(stateValue.value) } : stateValue
  }

}

/**
 * A Fetch internal Descriptor has the following form
 * {
 *    propName (always present): property name to use to inject the slot,
 *    storeFieldName (always present): the property to store the cache in the store.
 *    call (always present): function(props, state) that returns the call object
 *    transforming: optional transformation
 * }
 */
const createFetchDescriptors = fetches => Object.keys(fetches).map(propName => {
  const value = fetches[propName]
  return {
    propName,
    storeFieldName: value.name || propName,     // defaults to propName
    call: isFunction(value) ? value : value.call,
    transforming: value.transforming ? value.transforming : identity,
    on: value.on ? value.on : trueFn
  }
})

export const fetchingData = fetches => WrappedComponent => {
  const fetchDescriptors = createFetchDescriptors(fetches)
  class WithFetches extends AbstractWithFetches {
    getFetches() { return fetchDescriptors }
    getWrappedComponent() { return WrappedComponent }
  }
  WithFetches.displayName = `WithFetches(${getDisplayName(WrappedComponent)})`;
  return connect(state => ({ state }), dispatch => ({ dispatch }))(WithFetches)
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}<|endoftext|>


<commit_before>/* jshint multistr: true */

Vue.component('inject-form', {

  mixins: [apiMixin, notificationMixin],

  template: '\
    <form @submit.prevent="inject">\

\
        Type\
        <select id="inject-type" v-model="type" class="form-control custom-select">\
          <option value="icmp4">ICMPv4/Echo request</option>\
          <option value="icmp6">ICMPv6/Echo request</option>\
          <option value="tcp4">TCP/IPv4</option>\
          <option value="tcp6">TCP/IPv6</option>\
          <option value="udp4">UDP/IPv4</option>\
          <option value="udp6">UDP/IPv6</option>\
        </select>\

\

\
        Source\
        <node-selector class="inject-target"\
                       placeholder="From"\
                       id="inject-src"\
                       v-model="node1"></node-selector>\

\
          IP: \
          <input id="inject-src-ip" class="form-control" v-model="srcIP" placeholder="Auto"/>\

\

\
          MAC: \
          <input id="inject-src-mac" class="form-control" v-model="srcMAC" placeholder="Auto"/>\

\

\

\
        Destination\
        <node-selector placeholder="To"\
                       class="inject-target"\
                       id="inject-dst"\
                       v-model="node2"></node-selector>\

\
          IP: \
          <input id="inject-dst-ip" class="form-control" v-model="dstIP" placeholder="Auto"/>\

\

\
          MAC: \
          <input id="inject-dst-mac" class="form-control" v-model="dstMAC" placeholder="Auto"/>\

\

\

\
        Nb. of packets\
        <input id="inject-count" type="number" class="form-control input-sm" v-model.number="count" min="1" />\

\

\

\
          ICMP ID\
          <input id="inject-id" type="number" class="form-control input-sm" v-model.number="id" min="0" />\

\

\
          \
            <input id="inject-increment" type="checkbox" class="form-check-input" v-model="increment">\
            Increment ID at every packet\
            \
          \

\

\

\
        \
          <input type="radio" id="random" value="random" v-model="mode"> Payload Length\
          \
        \
        \
          <input type="radio" id="custom" value="custom" v-model="mode"> Payload\
          \
        \

\

\
        Payload length\
        <input id="payload-length" type="number" class="form-control input-sm" v-model.number="payloadlength" min="0" />\

\

\
        Payload\
        <input id="payld" type="text" class="form-control input-sm" v-model="payload"/>\

\

\
        Increment payload size\
        <input id="increment-payload" type="number" class="form-control input-sm" v-model.number="incrementPayload" min="0" />\

\

\

\
          Src Port\
          <input id="src-port" type="number" class="form-control input-sm" v-model.number="port1" min="0" />\

\

\
          Dst Port\
          <input id="dst-port" type="number" class="form-control input-sm" v-model.number="port2" min="0" />\

\

\

\
        Interval in milliseconds\
        <input id="inject-interval" type="number" class="form-control input-sm" v-model.number="interval" min="0" />\

\
      <button type="submit" id="inject" class="btn btn-primary">Inject</button>\
      <button type="button" class="btn btn-danger" @click="reset">Reset</button>\
    </form>\
  ',

  data: function() {

    return {
      node1: "",
      node2: "",
      count: 1,
      type: "icmp4",
      id: 0,
      interval: 0,
      increment: false,
      port1: 0,
      port2: 0,
      payloadlength: 0,
      srcNode: null,
      dstNode: null,
      srcIP: "",
      dstIP: "",
      srcMAC: "",
      dstMAC: "",
      mode: "random",
      payload: "",
      incrementPayload: 0,
    };
  },

  created: function() {
    if (this.$store.state.currentNode) {
      this.node1 = this.$store.state.currentNode.ID;
    }
  },

  beforeDestroy: function() {
    // FIXME: we should just call reset() here,
    // but the watchers are not being evaluated :/
    if (this.node1) {
      this.highlightNode(this.node1, false);
    }
    if (this.node2) {
      this.highlightNode(this.node2, false);
    }
  },

  computed: {

    error: function() {
      if (!this.node1 || !this.node2) {
        return "Source and destination interfaces must be selected";
      } else if (this.srcIP === "" && this.dstIP === "") {
        return "Source and Destination IPs need to be given by user";
      } else if (this.srcIP === "") {
        return "Source IP need to be given by user";
      } else if (this.dstIP === "") {
        return "Destination IP need to be given by user";
      } else if (this.srcMAC === "" && this.dstMAC === "") {
        return "Source and Destination MACs need to be given by user";
      } else if (this.srcMAC === "") {
        return "Source MAC need to be given by user";
      } else if (this.dstMAC === "") {
        return "Destination MAC need to be given by user";
      } else {
        return;
      }
    },

  },

  watch: {

    node1: function(newVal, oldVal) {
      if (oldVal) {
        this.highlightNode(oldVal, false);
      }
      if (newVal) {
        this.highlightNode(newVal, true);
        this.srcIP = this.getIP(this.srcNode = this.$store.state.currentNode);
        this.srcMAC = this.getMAC(this.srcNode);
      }
    },

    node2: function(newVal, oldVal) {
      if (oldVal) {
        this.highlightNode(oldVal, false);
      }
      if (newVal) {
        this.highlightNode(newVal, true);
        this.dstIP = this.getIP(this.dstNode = this.$store.state.currentNode);
        this.dstMAC = this.getMAC(this.dstNode);
      }
    },

    type: function(newVal, oldVal) {
      if(newVal.slice(-1) != oldVal.slice(-1)) {
        if(this.srcNode !== null) this.srcIP = this.getIP(this.srcNode);
        if(this.dstNode !== null) this.dstIP = this.getIP(this.dstNode);
      }
    }

  },

  methods: {

    highlightNode: function(id, bool) {
      if (!id) return;
      if (bool)
        this.$store.commit('highlight', id);
      else
        this.$store.commit('unhighlight', id);
    },

    getIP: function(node) {
      md = node.metadata.Neutron ? node.metadata.Neutron : node.metadata;
      ipFamily = this.type.slice(-1);
      if (ipFamily == "4" && "IPV4" in md) {
        return md.IPV4[0];
      } else if (ipFamily == "6" && "IPV6" in md) {
        return md.IPV6[0];
      } else {
        return "";
      }
    },

    getMAC: function(node) {
      if (node.metadata.ExtID && node.metadata.ExtID["attached-mac"]) return node.metadata.ExtID["attached-mac"];
      return node.metadata.MAC || "";
    },

    reset: function() {
      var self = this;
      this.node1 = this.node2 = "";
      this.count = 1;
      this.type = "icmp4";
      this.payloadlength = this.incrementPayload = 0;
      this.srcNode = this.dstNode = null;
      this.srcIP = this.dstIP = "";
      this.srcMAC = this.dstMAC = "";
      this.mode = "random";
      this.payload = "";
    },

    inject: function() {
      var self = this;
      if (this.error) {
        this.$error({message: this.error});
        return;
      }
      if (this.mode == "random") {
        this.payload = "x".repeat(this.payloadlength);
      }
      var ttl = this.interval*this.count;
      var headers = {"X-Resource-TTL": ttl + 5000 + "ms"};
      $.ajax({
        dataType: "json",
        url: '/api/injectpacket',
        data: JSON.stringify({
          "Src": "G.V().Has('TID', '" + this.node1 + "')",
          "Dst": "G.V().Has('TID', '" + this.node2 + "')",
          "SrcPort": this.port1,
          "DstPort": this.port2,
          "SrcIP": this.srcIP,
          "DstIP": this.dstIP,
          "SrcMAC": this.srcMAC,
          "DstMAC": this.dstMAC,
          "Type": this.type,
          "Count": this.count,
          "ICMPID": this.id,
          "Increment": this.increment,
          "Interval": this.interval,
          "Payload": this.payload,
          "IncrementPayload": this.incrementPayload,
        }),
        headers: headers,
        contentType: "application/json; charset=utf-8",
        method: 'POST',
      })
      .then(function() {
        self.$success({message: 'Packet injected'});
      })
      .fail(function(e) {
        self.$error({message: 'Packet injection error: ' + e.responseText});
      });

      self.highlightNode(self.node1, false);
      self.highlightNode(self.node2, false);
    },

  }

});
<commit_msg>pi: allow user to set ttl from UI<commit_after>/* jshint multistr: true */

Vue.component('inject-form', {

  mixins: [apiMixin, notificationMixin],

  template: '\
    <form @submit.prevent="inject">\

\
        Type\
        <select id="inject-type" v-model="type" class="form-control custom-select">\
          <option value="icmp4">ICMPv4/Echo request</option>\
          <option value="icmp6">ICMPv6/Echo request</option>\
          <option value="tcp4">TCP/IPv4</option>\
          <option value="tcp6">TCP/IPv6</option>\
          <option value="udp4">UDP/IPv4</option>\
          <option value="udp6">UDP/IPv6</option>\
        </select>\

\

\
        Source\
        <node-selector class="inject-target"\
                       placeholder="From"\
                       id="inject-src"\
                       v-model="node1"></node-selector>\

\
          IP: \
          <input id="inject-src-ip" class="form-control" v-model="srcIP" placeholder="Auto"/>\

\

\
          MAC: \
          <input id="inject-src-mac" class="form-control" v-model="srcMAC" placeholder="Auto"/>\

\

\

\
        Destination\
        <node-selector placeholder="To"\
                       class="inject-target"\
                       id="inject-dst"\
                       v-model="node2"></node-selector>\

\
          IP: \
          <input id="inject-dst-ip" class="form-control" v-model="dstIP" placeholder="Auto"/>\

\

\
          MAC: \
          <input id="inject-dst-mac" class="form-control" v-model="dstMAC" placeholder="Auto"/>\

\

\

\
        Nb. of packets\
        <input id="inject-count" type="number" class="form-control input-sm" v-model.number="count" min="1" />\

\

\

\
          ICMP ID\
          <input id="inject-id" type="number" class="form-control input-sm" v-model.number="id" min="0" />\

\

\
          \
            <input id="inject-increment" type="checkbox" class="form-check-input" v-model="increment">\
            Increment ID at every packet\
            \
          \

\

\

\
        \
          <input type="radio" id="random" value="random" v-model="mode"> Payload Length\
          \
        \
        \
          <input type="radio" id="custom" value="custom" v-model="mode"> Payload\
          \
        \

\

\
        Payload length\
        <input id="payload-length" type="number" class="form-control input-sm" v-model.number="payloadlength" min="0" />\

\

\
        Payload\
        <input id="payld" type="text" class="form-control input-sm" v-model="payload"/>\

\

\
        Increment payload size\
        <input id="increment-payload" type="number" class="form-control input-sm" v-model.number="incrementPayload" min="0" />\

\

\

\
          Src Port\
          <input id="src-port" type="number" class="form-control input-sm" v-model.number="port1" min="0" />\

\

\
          Dst Port\
          <input id="dst-port" type="number" class="form-control input-sm" v-model.number="port2" min="0" />\

\

\

\
        Interval in milliseconds\
        <input id="inject-interval" type="number" class="form-control input-sm" v-model.number="interval" min="0" />\

\

\
        Time To Live (TTL)\
        <input id="ttl" type="number" class="form-control input-sm" v-model.number="ttl" min="0" max="255" />\

\
      <button type="submit" id="inject" class="btn btn-primary">Inject</button>\
      <button type="button" class="btn btn-danger" @click="reset">Reset</button>\
    </form>\
  ',

  data: function() {

    return {
      node1: "",
      node2: "",
      count: 1,
      type: "icmp4",
      id: 0,
      interval: 0,
      increment: false,
      port1: 0,
      port2: 0,
      payloadlength: 0,
      srcNode: null,
      dstNode: null,
      srcIP: "",
      dstIP: "",
      srcMAC: "",
      dstMAC: "",
      mode: "random",
      payload: "",
      incrementPayload: 0,
      ttl: 64,
    };
  },

  created: function() {
    if (this.$store.state.currentNode) {
      this.node1 = this.$store.state.currentNode.ID;
    }
  },

  beforeDestroy: function() {
    // FIXME: we should just call reset() here,
    // but the watchers are not being evaluated :/
    if (this.node1) {
      this.highlightNode(this.node1, false);
    }
    if (this.node2) {
      this.highlightNode(this.node2, false);
    }
  },

  computed: {

    error: function() {
      if (!this.node1 || !this.node2) {
        return "Source and destination interfaces must be selected";
      } else if (this.srcIP === "" && this.dstIP === "") {
        return "Source and Destination IPs need to be given by user";
      } else if (this.srcIP === "") {
        return "Source IP need to be given by user";
      } else if (this.dstIP === "") {
        return "Destination IP need to be given by user";
      } else if (this.srcMAC === "" && this.dstMAC === "") {
        return "Source and Destination MACs need to be given by user";
      } else if (this.srcMAC === "") {
        return "Source MAC need to be given by user";
      } else if (this.dstMAC === "") {
        return "Destination MAC need to be given by user";
      } else {
        return;
      }
    },

  },

  watch: {

    node1: function(newVal, oldVal) {
      if (oldVal) {
        this.highlightNode(oldVal, false);
      }
      if (newVal) {
        this.highlightNode(newVal, true);
        this.srcIP = this.getIP(this.srcNode = this.$store.state.currentNode);
        this.srcMAC = this.getMAC(this.srcNode);
      }
    },

    node2: function(newVal, oldVal) {
      if (oldVal) {
        this.highlightNode(oldVal, false);
      }
      if (newVal) {
        this.highlightNode(newVal, true);
        this.dstIP = this.getIP(this.dstNode = this.$store.state.currentNode);
        this.dstMAC = this.getMAC(this.dstNode);
      }
    },

    type: function(newVal, oldVal) {
      if(newVal.slice(-1) != oldVal.slice(-1)) {
        if(this.srcNode !== null) this.srcIP = this.getIP(this.srcNode);
        if(this.dstNode !== null) this.dstIP = this.getIP(this.dstNode);
      }
    }

  },

  methods: {

    highlightNode: function(id, bool) {
      if (!id) return;
      if (bool)
        this.$store.commit('highlight', id);
      else
        this.$store.commit('unhighlight', id);
    },

    getIP: function(node) {
      md = node.metadata.Neutron ? node.metadata.Neutron : node.metadata;
      ipFamily = this.type.slice(-1);
      if (ipFamily == "4" && "IPV4" in md) {
        return md.IPV4[0];
      } else if (ipFamily == "6" && "IPV6" in md) {
        return md.IPV6[0];
      } else {
        return "";
      }
    },

    getMAC: function(node) {
      if (node.metadata.ExtID && node.metadata.ExtID["attached-mac"]) return node.metadata.ExtID["attached-mac"];
      return node.metadata.MAC || "";
    },

    reset: function() {
      var self = this;
      this.node1 = this.node2 = "";
      this.count = 1;
      this.type = "icmp4";
      this.payloadlength = this.incrementPayload = 0;
      this.srcNode = this.dstNode = null;
      this.srcIP = this.dstIP = "";
      this.srcMAC = this.dstMAC = "";
      this.mode = "random";
      this.payload = "";
      this.ttl = 64;
    },

    inject: function() {
      var self = this;
      if (this.error) {
        this.$error({message: this.error});
        return;
      }
      if (this.mode == "random") {
        this.payload = "x".repeat(this.payloadlength);
      }
      var requestTTL = this.interval*this.count;
      var headers = {"X-Resource-TTL": requestTTL + 5000 + "ms"};
      $.ajax({
        dataType: "json",
        url: '/api/injectpacket',
        data: JSON.stringify({
          "Src": "G.V().Has('TID', '" + this.node1 + "')",
          "Dst": "G.V().Has('TID', '" + this.node2 + "')",
          "SrcPort": this.port1,
          "DstPort": this.port2,
          "SrcIP": this.srcIP,
          "DstIP": this.dstIP,
          "SrcMAC": this.srcMAC,
          "DstMAC": this.dstMAC,
          "Type": this.type,
          "Count": this.count,
          "ICMPID": this.id,
          "Increment": this.increment,
          "Interval": this.interval,
          "Payload": this.payload,
          "IncrementPayload": this.incrementPayload,
          "TTL": this.ttl,
        }),
        headers: headers,
        contentType: "application/json; charset=utf-8",
        method: 'POST',
      })
      .then(function() {
        self.$success({message: 'Packet injected'});
      })
      .fail(function(e) {
        self.$error({message: 'Packet injection error: ' + e.responseText});
      });

      self.highlightNode(self.node1, false);
      self.highlightNode(self.node2, false);
    },

  }

});
<|endoftext|>


<commit_before>import React from 'react';
import { VelocityComponent, VelocityTransitionGroup } from 'velocity-react';

const CHECK_SVG = (
  <svg xmlns="http://www.w3.org/2000/svg" version="1" width="18px" height="18px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" style={{ verticalAlign: 'bottom' }}>
    <path d="M 12 2 C 6.5 2 2 6.5 2 12 C 2 17.5 6.5 22 12 22 C 17.2 22 21.40625 18.1 21.90625 13 L 19.90625 13 C 19.40625 16.9 16 20 12 20 C 7.6 20 4 16.4 4 12 C 4 7.6 7.6 4 12 4 C 13.4 4 14.70625 4.4 15.90625 5 L 17.40625 3.5 C 15.80625 2.6 14 2 12 2 z M 20.5 4.875 L 11.1875 13.78125 L 7.5 10.09375 L 6.09375 11.5 L 10.5 15.90625 L 11.1875 16.59375 L 11.90625 15.90625 L 21.90625 6.3125 L 20.5 4.875 z" color="#000" overflow="visible" enable-background="accumulate"></path>
  </svg>
);

const NextSteps = (props) => {
  let nameMap = {
    express: 'Express',
    meteor: 'Meteor',
    jekyll: 'Jekyll',
    middleman: 'Middleman',
    library: 'JavaScript Library',
    mongodb: 'MongoDB',
    mysql: 'MySQL',
    postgresql: 'PostgreSQL',
    sqlite: 'SQLite',
  };

  let urlMap = {
    express: '-express',
    meteor: '-meteor',
    jekyll: '-jekyll',
    middleman: '-middleman',
    library: '-js-library',
    mongodb: '-mongodb',
    mysql: '-mysql',
    postgresql: '-postgresql',
    sqlite: '-sqlite',
  };

  let gettingStartedText = `See how to get started with ${nameMap[props.framework || props.staticSiteGenerator || props.platform]}`;

  if (props.database && props.database !== 'none') {
    gettingStartedText += ` and <a href="https://github.com/sahat/boilerplate#${urlMap[props.database]}" target="_blank">setup</a> <strong>${nameMap[props.database]}</strong> database.`
  } else {
    gettingStartedText += `.`;
  }

  const learnMap = {
    jekyll: `<strong>Jekyll</strong>`,
    middleman: `<strong>Middleman</strong>`,
    library: `creating a <strong>JavaScript Library</strong> and best practices`,
    react: `<strong>React</strong> and <strong>Redux</strong>`,
    angular: `<strong>Angular 2</strong> and <strong>TypeScript</strong>`,
  };

  let learningText = `Learn more about ${learnMap[props.jsFramework || props.staticSiteGenerator || props.platform]} using these curated free resources.`;

  return (

        <VelocityComponent runOnMount animation="transition.fadeIn" duration={900} delay={700}>

{CHECK_SVG} All Set!

        </VelocityComponent>
        <VelocityComponent runOnMount animation="transition.fadeIn" duration={900} delay={1100}>

Your boilerplate project is now ready to be used.

        </VelocityComponent>

          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={1500}>

          </VelocityComponent>
          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={1800}>

          </VelocityComponent>
          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={2100}>

                  See frequently asked questions before opening an issue on GitHub. For general questions visit our Gitter chat.

          </VelocityComponent>

  );
};

export default NextSteps;
<commit_msg>Show dynamic deployment text in next steps getting started<commit_after>import React from 'react';
import { VelocityComponent, VelocityTransitionGroup } from 'velocity-react';

const CHECK_SVG = (
  <svg xmlns="http://www.w3.org/2000/svg" version="1" width="18px" height="18px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" style={{ verticalAlign: 'bottom' }}>
    <path d="M 12 2 C 6.5 2 2 6.5 2 12 C 2 17.5 6.5 22 12 22 C 17.2 22 21.40625 18.1 21.90625 13 L 19.90625 13 C 19.40625 16.9 16 20 12 20 C 7.6 20 4 16.4 4 12 C 4 7.6 7.6 4 12 4 C 13.4 4 14.70625 4.4 15.90625 5 L 17.40625 3.5 C 15.80625 2.6 14 2 12 2 z M 20.5 4.875 L 11.1875 13.78125 L 7.5 10.09375 L 6.09375 11.5 L 10.5 15.90625 L 11.1875 16.59375 L 11.90625 15.90625 L 21.90625 6.3125 L 20.5 4.875 z" color="#000" overflow="visible" enable-background="accumulate"></path>
  </svg>
);

const NextSteps = (props) => {
  let nameMap = {
    express: 'Express',
    meteor: 'Meteor',
    jekyll: 'Jekyll',
    middleman: 'Middleman',
    library: 'JavaScript Library',
    mongodb: 'MongoDB',
    mysql: 'MySQL',
    postgresql: 'PostgreSQL',
    sqlite: 'SQLite',
    heroku: 'Heroku',
    azure: 'Microsoft Azure',
    bluemix: 'IBM Bluemix',
    digitalOcean: 'Digital Ocean'
  };

  let urlMap = {
    express: '-express',
    meteor: '-meteor',
    jekyll: '-jekyll',
    middleman: '-middleman',
    library: '-js-library',
    mongodb: '-mongodb',
    mysql: '-mysql',
    postgresql: '-postgresql',
    sqlite: '-sqlite',
  };

  let gettingStartedText = `See how to get started with ${nameMap[props.framework || props.staticSiteGenerator || props.platform]}`;

  if (props.database && props.database !== 'none') {
    gettingStartedText += ` and <a href="https://github.com/sahat/boilerplate#${urlMap[props.database]}" target="_blank">setup</a> <strong>${nameMap[props.database]}</strong> database.`
  } else {
    gettingStartedText += `.`;
  }

  if (props.deployment && props.deployment !== 'none') {
    gettingStartedText += ` When ready to deploy, see our <a href="https://github.com/sahat/boilerplate#" target="_blank"> ${nameMap[props.deployment]} deployment guide</a>.`
  }

    const learnMap = {
    express: `<strong>Express</strong>`,
    meteor: `<strong>Meteor</strong>`,
    jekyll: `<strong>Jekyll</strong>`,
    middleman: `<strong>Middleman</strong>`,
    library: `creating a <strong>JavaScript Library</strong> and best practices`,
    react: `<strong>React</strong> and <strong>Redux</strong>`,
    angular: `<strong>Angular 2</strong> and <strong>TypeScript</strong>`,
    mysql: `<strong>Knex.js ORM</strong>`,
    sqlite: `<strong>Knex.js ORM</strong>`,
    postgresql: `<strong>Knex.js ORM</strong>`,
    mongodb: `<strong>Mongoose</strong>`,
  };

  const jsFramework = props.jsFramework !== 'none' ? props.jsFramework : null;
  const database = props.database !== 'none' ? `${learnMap[props.framework]} and ${learnMap[props.database]}` : null;

  let learningText = `Learn more about ${learnMap[jsFramework] || database || learnMap[props.framework || props.staticSiteGenerator || props.platform]} using these curated free resources.`;

  return (

        <VelocityComponent runOnMount animation="transition.fadeIn" duration={900} delay={700}>

{CHECK_SVG} All Set!

        </VelocityComponent>
        <VelocityComponent runOnMount animation="transition.fadeIn" duration={900} delay={1100}>

Your boilerplate project is now ready to be used.

        </VelocityComponent>

          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={1500}>

          </VelocityComponent>
          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={1800}>

          </VelocityComponent>
          <VelocityComponent runOnMount animation="transition.slideUpIn" duration={900} delay={2100}>

                  See frequently asked questions before opening an issue on GitHub. For general questions, visit our Gitter chat.

          </VelocityComponent>

  );
};

export default NextSteps;
<|endoftext|>