<commit_before>define(function(require) {

	'use strict';

	var Utils = require('utils/utils');

	var GraphView          = require('views/graph');
	var StaticGraphView    = require('views/graph/static');
	var IntensityGraphView = require('views/graph/intensity');

	var html = require('text!templates/screen-graph.html');

	/*
	 * "Local" variables for functions to share and recycle
	 */
	var length,
	    lat,
		latWidth,
		latHeight,
		height,
		xSpacing,
		points,
	    i,
	    j;

	/**
	 * ScreenGraphView shows the values of a certain row of the
	 *   lattice in real time in the form of a curve.
	 */
	var ScreenGraphView = StaticGraphView.extend({

		template: _.template(html),
		className: 'screen-graph-view',

		events: {
			'click .screen-graph-show-button' : 'show',
			'click .screen-graph-hide-button' : 'hide'
		},

		initialize: function(options) {
			// Default values
			options = _.extend({
				title: 'Screen',
				x: null,
				y: null,
				latitudinalGridLines: 0,
				longitudinalGridLines: 0
			}, options);

			StaticGraphView.prototype.initialize.apply(this, [options]);

			if (options.heatmapView)
				this.heatmapView = options.heatmapView;
			else
				throw 'ScreenGraphView requires a HeatmapView instance to render.';

			// Ratio between pixels and cell width
			this.xSpacing = 1;

			// History of colors along the edge of the heatmap
			this.colorHistory = [];
			this.colorHistoryIndex = 0;
			this.colorHistoryLength = 120;

			// Initialize each record in the color history as an array of points
			for (var h = 0; h < this.colorHistoryLength; h++) {
				this.colorHistory[h] = [];
				for (var j = 0; j < this.waveSimulation.lattice.height; j++) {
					this.colorHistory[h].push({
						r: 0,
						g: 0,
						b: 0,
						a: 0
					});
				}
			}

			//
			this.intensityScale = 7;

			// Initialize values for our colors array
			this.colors = [];
			for (var j = 0; j < this.waveSimulation.lattice.height; j++) {
				this.colors[j] = {
					r: 0,
					g: 0,
					b: 0
				};
			}

			// For syncronizing with the simulation
			this.accumulator = 0;
			this.time = 0;
			this.timestep = this.waveSimulation.timestep;

			this.intensityGraphView = new IntensityGraphView({
				waveSimulation: this.waveSimulation,
				heatmapView: this.heatmapView,
				screenGraphView: this
			});
		},

		/**
		 * Renders html container
		 */
		renderContainer: function() {
			this.$el.html(this.template(this.graphInfo));

			this.$showButton = this.$('.screen-graph-show-button');
			this.$hideButton = this.$('.screen-graph-hide-button');

			this.$showChartButton = this.$('.screen-graph-show-chart-button');
			//this.$hideButton = this.$('.screen-graph-hide-button');

			this.intensityGraphView.render();
			this.$('.intensity-graph-placeholder').replaceWith(this.intensityGraphView.el);
		},

		/**
		 * Does the actual resizing of the canvas. We need to also update
		 *  our cached xSpacing whenever the canvas size changes.
		 */
		resize: function() {
			GraphView.prototype.resize.apply(this);
			this.xSpacing = this.width / (this.waveSimulation.lattice.width - 1);
		},

		/**
		 *
		 */
		postRender: function() {
			this.intensityGraphView.postRender();

			StaticGraphView.prototype.postRender.apply(this);
		},

		/**
		 *
		 */
		initPoints: function() {},

		/**
		 * Draws the colors in this.colors all the way down the canvas,
		 *   interpolating between colors.
		 */
		drawColors: function() {
			/*
			 * Get the ratio of lattice cells to pixels so we can figure
			 *   out which colors (directly correlating to lattice cell)
			 *   we're between at any given pixel (y).
			 */
			var ratio = this.height / this.waveSimulation.lattice.height;
			var width = this.width;

			var maxColorIndex = this.colors.length - 1;

			var a, // The starting color's index
			    b, // The ending color's index
			    progress, // Progress from the starting to ending color
			    colors  = this.colors,
			    context = this.context;

			for (var y = 0; y < this.height; y++) {
				a = Math.floor(y / ratio);
				b = a + 1;

				// Catch rounding errors with floats
				if (b > maxColorIndex) {
					b--;
					a--;
				}

				// The unrounded index minus the start index
				progress = (y / ratio) - a;

				/*
				 * Using our two colors and our progress between colors,
				 *   find the linearly interpolated color between them.
				 */
				context.fillStyle = Utils.rgbToHex(
					parseInt(Utils.lerp(colors[a].r, colors[b].r, progress)),
					parseInt(Utils.lerp(colors[a].g, colors[b].g, progress)),
					parseInt(Utils.lerp(colors[a].b, colors[b].b, progress))
				);
				context.fillRect(0, y, width, 1);
			}
		},

		/**
		 *
		 */
		updateColorHistory: function() {
			this.heatmapView.getAvgEdgeColors(this.colorHistory[this.colorHistoryIndex++]);

			if (this.colorHistoryIndex == this.colorHistoryLength) {
				this.colorHistoryIndex = 0;
				this.colorHistoryFilled = true;
			}
		},

		/**
		 *
		 */
		calculateColors: function() {
			var scalar;
			if (!this.colorHistoryFilled)
				scalar = this.intensityScale / (this.colorHistoryIndex + 1);
			else
				scalar = this.intensityScale / this.colorHistoryLength;

			var colors = this.colors,
			    color;

			var height = this.waveSimulation.lattice.height;
			for (var j = 0; j < height; j++) {
				color = colors[j];
				color.r = 0;
				color.g = 0;
				color.b = 0;
				for (var h = 0; h < this.colorHistoryLength; h++) {
					color.r += this.colorHistory[h][j].r * this.colorHistory[h][j].a;
					color.g += this.colorHistory[h][j].g * this.colorHistory[h][j].a;
					color.b += this.colorHistory[h][j].b * this.colorHistory[h][j].a;
				}
				color.r *= scalar;
				color.g *= scalar;
				color.b *= scalar;
				color.r = Math.min(color.r, 255);
				color.g = Math.min(color.g, 255);
				color.b = Math.min(color.b, 255);
			}
		},

		/**
		 * Responds to resize events and draws everything.
		 */
		update: function(time, delta) {
			if (this.resizeOnNextUpdate)
				this.resize();

			if (!this.paused) {
				this.accumulator += delta;

				while (this.accumulator >= this.timestep) {
					this.time += this.timestep;

					this._update(time, delta);

					this.accumulator -= this.timestep;
				}
			}
		},

		_update: function(time, delta) {
			this.updateColorHistory();
			this.calculateColors();
			this.drawColors();
			this.intensityGraphView.update(time, delta);
		},

		show: function(event) {
			if (this.toggling)
				return;

			StaticGraphView.prototype.show.apply(this, [event]);

			this.heatmapView.enableScreenMode();
			this.intensityGraphView.$el.addClass('visible');
		},

		hide: function(event) {
			if (this.toggling)
				return;

			StaticGraphView.prototype.hide.apply(this, [event]);

			this.heatmapView.disableScreenMode();
			this.intensityGraphView.$el.removeClass('visible');
		},

		animationDuration: function() {
			return 400;
		}
	});

	return ScreenGraphView;
});
<commit_msg>Fixed a little rendering bug on the intensity graph when showing/hiding the screen view<commit_after>define(function(require) {

	'use strict';

	var Utils = require('utils/utils');

	var GraphView          = require('views/graph');
	var StaticGraphView    = require('views/graph/static');
	var IntensityGraphView = require('views/graph/intensity');

	var html = require('text!templates/screen-graph.html');

	/*
	 * "Local" variables for functions to share and recycle
	 */
	var length,
	    lat,
		latWidth,
		latHeight,
		height,
		xSpacing,
		points,
	    i,
	    j;

	/**
	 * ScreenGraphView shows the values of a certain row of the
	 *   lattice in real time in the form of a curve.
	 */
	var ScreenGraphView = StaticGraphView.extend({

		template: _.template(html),
		className: 'screen-graph-view',

		events: {
			'click .screen-graph-show-button' : 'show',
			'click .screen-graph-hide-button' : 'hide'
		},

		initialize: function(options) {
			// Default values
			options = _.extend({
				title: 'Screen',
				x: null,
				y: null,
				latitudinalGridLines: 0,
				longitudinalGridLines: 0
			}, options);

			StaticGraphView.prototype.initialize.apply(this, [options]);

			if (options.heatmapView)
				this.heatmapView = options.heatmapView;
			else
				throw 'ScreenGraphView requires a HeatmapView instance to render.';

			// Ratio between pixels and cell width
			this.xSpacing = 1;

			// History of colors along the edge of the heatmap
			this.colorHistory = [];
			this.colorHistoryIndex = 0;
			this.colorHistoryLength = 120;

			// Initialize each record in the color history as an array of points
			for (var h = 0; h < this.colorHistoryLength; h++) {
				this.colorHistory[h] = [];
				for (var j = 0; j < this.waveSimulation.lattice.height; j++) {
					this.colorHistory[h].push({
						r: 0,
						g: 0,
						b: 0,
						a: 0
					});
				}
			}

			//
			this.intensityScale = 7;

			// Initialize values for our colors array
			this.colors = [];
			for (var j = 0; j < this.waveSimulation.lattice.height; j++) {
				this.colors[j] = {
					r: 0,
					g: 0,
					b: 0
				};
			}

			// For syncronizing with the simulation
			this.accumulator = 0;
			this.time = 0;
			this.timestep = this.waveSimulation.timestep;

			this.intensityGraphView = new IntensityGraphView({
				waveSimulation: this.waveSimulation,
				heatmapView: this.heatmapView,
				screenGraphView: this
			});
		},

		/**
		 * Renders html container
		 */
		renderContainer: function() {
			this.$el.html(this.template(this.graphInfo));

			this.$showButton = this.$('.screen-graph-show-button');
			this.$hideButton = this.$('.screen-graph-hide-button');

			this.$showChartButton = this.$('.screen-graph-show-chart-button');
			//this.$hideButton = this.$('.screen-graph-hide-button');

			this.intensityGraphView.render();
			this.$('.intensity-graph-placeholder').replaceWith(this.intensityGraphView.el);
		},

		/**
		 * Does the actual resizing of the canvas. We need to also update
		 *  our cached xSpacing whenever the canvas size changes.
		 */
		resize: function() {
			GraphView.prototype.resize.apply(this);
			this.xSpacing = this.width / (this.waveSimulation.lattice.width - 1);
		},

		/**
		 *
		 */
		postRender: function() {
			this.intensityGraphView.postRender();

			StaticGraphView.prototype.postRender.apply(this);
		},

		/**
		 *
		 */
		initPoints: function() {},

		/**
		 * Draws the colors in this.colors all the way down the canvas,
		 *   interpolating between colors.
		 */
		drawColors: function() {
			/*
			 * Get the ratio of lattice cells to pixels so we can figure
			 *   out which colors (directly correlating to lattice cell)
			 *   we're between at any given pixel (y).
			 */
			var ratio = this.height / this.waveSimulation.lattice.height;
			var width = this.width;

			var maxColorIndex = this.colors.length - 1;

			var a, // The starting color's index
			    b, // The ending color's index
			    progress, // Progress from the starting to ending color
			    colors  = this.colors,
			    context = this.context;

			for (var y = 0; y < this.height; y++) {
				a = Math.floor(y / ratio);
				b = a + 1;

				// Catch rounding errors with floats
				if (b > maxColorIndex) {
					b--;
					a--;
				}

				// The unrounded index minus the start index
				progress = (y / ratio) - a;

				/*
				 * Using our two colors and our progress between colors,
				 *   find the linearly interpolated color between them.
				 */
				context.fillStyle = Utils.rgbToHex(
					parseInt(Utils.lerp(colors[a].r, colors[b].r, progress)),
					parseInt(Utils.lerp(colors[a].g, colors[b].g, progress)),
					parseInt(Utils.lerp(colors[a].b, colors[b].b, progress))
				);
				context.fillRect(0, y, width, 1);
			}
		},

		/**
		 *
		 */
		updateColorHistory: function() {
			this.heatmapView.getAvgEdgeColors(this.colorHistory[this.colorHistoryIndex++]);

			if (this.colorHistoryIndex == this.colorHistoryLength) {
				this.colorHistoryIndex = 0;
				this.colorHistoryFilled = true;
			}
		},

		/**
		 *
		 */
		calculateColors: function() {
			var scalar;
			if (!this.colorHistoryFilled)
				scalar = this.intensityScale / (this.colorHistoryIndex + 1);
			else
				scalar = this.intensityScale / this.colorHistoryLength;

			var colors = this.colors,
			    color;

			var height = this.waveSimulation.lattice.height;
			for (var j = 0; j < height; j++) {
				color = colors[j];
				color.r = 0;
				color.g = 0;
				color.b = 0;
				for (var h = 0; h < this.colorHistoryLength; h++) {
					color.r += this.colorHistory[h][j].r * this.colorHistory[h][j].a;
					color.g += this.colorHistory[h][j].g * this.colorHistory[h][j].a;
					color.b += this.colorHistory[h][j].b * this.colorHistory[h][j].a;
				}
				color.r *= scalar;
				color.g *= scalar;
				color.b *= scalar;
				color.r = Math.min(color.r, 255);
				color.g = Math.min(color.g, 255);
				color.b = Math.min(color.b, 255);
			}
		},

		/**
		 * Responds to resize events and draws everything.
		 */
		update: function(time, delta) {
			if (this.resizeOnNextUpdate)
				this.resize();

			if (!this.paused) {
				this.accumulator += delta;

				while (this.accumulator >= this.timestep) {
					this.time += this.timestep;

					this._update(time, delta);

					this.accumulator -= this.timestep;
				}
			}
		},

		_update: function(time, delta) {
			this.updateColorHistory();
			this.calculateColors();
			this.drawColors();
			this.intensityGraphView.update(time, delta);
		},

		show: function(event) {
			if (this.toggling)
				return;

			StaticGraphView.prototype.show.apply(this, [event]);

			this.heatmapView.enableScreenMode();
			this.intensityGraphView.$el.addClass('visible');
		},

		_afterShow: function() {
			StaticGraphView.prototype._afterShow.apply(this, [event]);

			this.intensityGraphView.resize();
		},

		hide: function(event) {
			if (this.toggling)
				return;

			StaticGraphView.prototype.hide.apply(this, [event]);

			this.heatmapView.disableScreenMode();
			this.intensityGraphView.$el.removeClass('visible');
		},

		animationDuration: function() {
			return 400;
		}
	});

	return ScreenGraphView;
});
<|endoftext|>


<commit_before>const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const ExtractTextPlugin = require("extract-text-webpack-plugin");

const uglifyConfig = require('./uglify.config');

module.exports = (config, { isProd, isAnalyzer }) => {
  config.plugins = [
    new HtmlWebpackPlugin({
      template: './app/markup/index.html'
    })
  ];

  config.plugins.push(
    new ExtractTextPlugin({ filename: 'default.css', allChunks: true })
  );

  if (isProd) {
    config.plugins.push(
      new webpack.optimize.UglifyJsPlugin(uglifyConfig)
    );
  }

  if (isAnalyzer) {
    config.plugins.push(
      new BundleAnalyzerPlugin()
    );
  }

  return config;
};
<commit_msg>Generate styles folder inside dist to organize css files better #3<commit_after>const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const ExtractTextPlugin = require("extract-text-webpack-plugin");

const uglifyConfig = require('./uglify.config');

module.exports = (config, { isProd, isAnalyzer }) => {
  config.plugins = [
    new HtmlWebpackPlugin({
      template: './app/markup/index.html'
    })
  ];

  config.plugins.push(
    new ExtractTextPlugin({ filename: 'styles/default.css', allChunks: true })
  );

  if (isProd) {
    config.plugins.push(
      new webpack.optimize.UglifyJsPlugin(uglifyConfig)
    );
  }

  if (isAnalyzer) {
    config.plugins.push(
      new BundleAnalyzerPlugin()
    );
  }

  return config;
};
<|endoftext|>


<commit_before>/**
 * StopFlash
 *
 * https://github.com/JWhile/StopFlash
 *
 * content_script.js
 */

// function isFlash(HTMLElement element):boolean
var isFlash = function(element)
{
    return (element.nodeName === 'OBJECT' || element.nodeName === 'EMBED');
};

// class FlashCollection
function FlashCollection(doc)
{
    this.flashElements = []; // :Array<FlashElement>

    this.port = chrome.runtime.connect({'name': 'stopflashContentScript'}); // :chrome.runtime.Port

    this.elementId = 0; // :int

    this.whitelist = []; // :Array<String>

    this.id = 0; // :int

    var self = this;

    this.port.onMessage.addListener(function(req)
    {
        if(req['stopflashWhitelist'])
        {
            self.whitelist = req['stopflashWhitelist'];
            self.id = req['stopflashCollectionId'];

            // init
            self.add(doc.getElementsByTagName('OBJECT'));
            self.add(doc.getElementsByTagName('EMBED'));

            self.sendData();
        }

        if(req['stopflashBlock'])
        {
            var e = self.getById(req['stopflashBlock'].id);

            if(e != null)
            {
                e.block();

                self.sendData();
            }
        }

        if(req['stopflashUnblock'])
        {
            var e = self.getById(req['stopflashUnblock'].id);

            if(e != null)
            {
                e.unblock();

                self.sendData();
            }
        }
    });

    this.port.postMessage({'stopflashInit': true, 'stopflashIsMainFrame': (window == window.top)});

    var observer = new MutationObserver(function(changes)
    {
        var changed = false;

        for(var i = 0, c; i < changes.length; ++i)
        {
            c = changes[i];

            if(c.addedNodes != null)
            {
                changed = (self.add(c.addedNodes) || false);
            }

            if(c.removedNodes != null)
            {
                changed = (self.remove(c.removedNodes) || false);
            }
        }

        if(changed)
        {
            self.sendData();
        }
    });

    observer.observe(doc, {childList: true, subtree: true});

    this.sendData();
}
// function sendData():void
FlashCollection.prototype.sendData = function()
{
    var data = [];

    for(var i = 0; i < this.flashElements.length; ++i)
    {
        data.push(this.flashElements[i].getData());
    }

    this.port.postMessage({'stopflashDataUpdate': true, 'stopflashData': data, 'stopflashCollectionId': this.id});
};
// function add(NodeList elements):boolean
FlashCollection.prototype.add = function(elements)
{
    var changed = false;

    for(var i = 0, e; i < elements.length; ++i)
    {
        e = elements[i];

        if(isFlash(e))
        {
            var parent = e;

            while(parent = parent.parentNode)
            {
                if(isFlash(parent))
                {
                    e = parent;
                }
            }

            var f = this.get(e);

            if(f == null)
            {
                f = new FlashElement(this, ++this.elementId, e);

                if(f.isValid())
                {
                    this.flashElements.push(f);

                    if(!f.isWhitelist())
                    {
                        f.block();
                    }

                    changed = true;
                }
            }
            else
            {
                f.update();

                if(f.blocked)
                {
                    f.block();
                }
            }
        }
    }

    return changed;
};
// function remove(NodeList elements):boolean
FlashCollection.prototype.remove = function(elements)
{
    var changed = false;

    for(var i = 0, f; i < elements.length; ++i)
    {
        f = this.get(elements[i]);

        if(f != null && !f.blocked)
        {
            f.remove();

            changed = true;
        }
    }

    return changed;
};
// function get(HTMLElement element)
FlashCollection.prototype.get = function(element)
{
    for(var i = 0; i < this.flashElements.length; ++i)
    {
        if(element == this.flashElements[i].element)
        {
            return this.flashElements[i];
        }
    }

    return null;
};
// function getById(int id)
FlashCollection.prototype.getById = function(id)
{
    for(var i = 0; i < this.flashElements.length; ++i)
    {
        if(id === this.flashElements[i].id)
        {
            return this.flashElements[i];
        }
    }

    return null;
};

var styles = [
    'float', 'clear',
    'position', 'top', 'left',
    'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
    'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
    'border-top', 'border-right', 'border-bottom', 'border-left'
]; // :Array<String>

var iconSizes = [128, 64, 48]; // :Array<int>

// class FlashElement
function FlashElement(collection, id, element)
{
    this.collection = collection; // :FlashCollection

    this.id = id; // :int

    this.parent = element.parentNode; // :HTMLElement
    this.element = element; // :HTMLElement
    this.nextSibling = element.nextSibling; // :HTMLElement

    this.blocked = false; // :boolean

    var self = this;

    var width = parseInt(Builder.getStyle(element, 'width'));
    var height = parseInt(Builder.getStyle(element, 'height'));

    var size = 32;

    for(var i = 0, s; i < iconSizes.length; ++i)
    {
        s = iconSizes[i];

        if(s < width && s < height)
        {
            size = s;
            break;
        }
    }

    var replacement = new Builder('div')
        .css('display', 'block')
        .css('width', width +'px')
        .css('height', height +'px')
        .css('cursor', 'pointer')
        .css('min-width', '34px')
        .css('min-height', '34px')
        .css('background', '#333 url("'+ chrome.extension.getURL('icons/stopflash_'+ size +'.png') +'") center center no-repeat')
        .event('click', function()
        {
            self.unblock();

            self.collection.sendData();
        });

    for(var i = 0; i < styles.length; ++i)
    {
        replacement.css(styles[i], Builder.getStyle(element, styles[i]));
    }

    this.replacement = replacement.node; // :HTMLElement
}
// function isValid():boolean
FlashElement.prototype.isValid = function()
{
    return (this.parent != null && this.getUrl() != null);
};
// function getData():Object
FlashElement.prototype.getData = function()
{
    return {
        'collection': this.collection.id,
        'id': this.id,
        'type': this.element.nodeName,
        'url': this.getUrl(),
        'blocked': this.blocked,
        'whitelist': this.isWhitelist()
    };
};
// function isWhitelist():boolean
FlashElement.prototype.isWhitelist = function()
{
    var url = this.getUrl();

    for(var i = 0; i < this.collection.whitelist.length; ++i)
    {
        if(this.collection.whitelist[i].indexOf(url) >= 0)
        {
            return true;
        }
    }

    return false;
};
// function getUrl():String
FlashElement.prototype.getUrl = function()
{
    if(this.element.nodeName === 'EMBED')
    {
        return this.element.src;
    }
    else if(this.element.nodeName === 'OBJECT')
    {
        if(this.element.data)
        {
            return this.element.data;
        }
        else
        {
            var child = this.element.firstChild;

            while(child != null)
            {
                if(child.nodeName === 'PARAM' && (child.name === 'src' || child.name === 'movie'))
                {
                    return child.value;
                }

                child = child.nextSibling;
            }
        }
    }

    return null;
};
// function update():void
FlashElement.prototype.update = function()
{
    if(this.element.parentNode)
    {
        this.parent = this.element.parentNode;
        this.nextSibling = this.element.nextSibling;
    }
    else if(this.replacement.parentNode)
    {
        this.parent = this.replacement.parentNode;
        this.nextSibling = this.replacement.nextSibling;
    }
};
// function block():void
FlashElement.prototype.block = function()
{
    this.blocked = true;

    this.parent.insertBefore(this.replacement, this.nextSibling);

    this.parent.removeChild(this.element);
};
// function unblock():void
FlashElement.prototype.unblock = function()
{
    this.blocked = false;

    this.parent.insertBefore(this.element, this.nextSibling);

    this.parent.removeChild(this.replacement);
};
// function remove():void
FlashElement.prototype.remove = function()
{
    if(this.replacement.parentNode)
    {
        this.replacement.parentNode.removeChild(this.replacement);
    }

    if(this.element.parentNode)
    {
        this.element.parentNode.removeChild(this.element);
    }
};

// main
var collection = null;

var main = function()
{
    collection = new FlashCollection(document);
};

document.addEventListener('DOMContentLoaded', main, false);
<commit_msg>Fix isWhitelist() is not right<commit_after>/**
 * StopFlash
 *
 * https://github.com/JWhile/StopFlash
 *
 * content_script.js
 */

// function isFlash(HTMLElement element):boolean
var isFlash = function(element)
{
    return (element.nodeName === 'OBJECT' || element.nodeName === 'EMBED');
};

// class FlashCollection
function FlashCollection(doc)
{
    this.flashElements = []; // :Array<FlashElement>

    this.port = chrome.runtime.connect({'name': 'stopflashContentScript'}); // :chrome.runtime.Port

    this.elementId = 0; // :int

    this.whitelist = []; // :Array<String>

    this.id = 0; // :int

    var self = this;

    this.port.onMessage.addListener(function(req)
    {
        if(req['stopflashWhitelist'])
        {
            self.whitelist = req['stopflashWhitelist'];
            self.id = req['stopflashCollectionId'];

            // init
            self.add(doc.getElementsByTagName('OBJECT'));
            self.add(doc.getElementsByTagName('EMBED'));

            self.sendData();
        }

        if(req['stopflashBlock'])
        {
            var e = self.getById(req['stopflashBlock'].id);

            if(e != null)
            {
                e.block();

                self.sendData();
            }
        }

        if(req['stopflashUnblock'])
        {
            var e = self.getById(req['stopflashUnblock'].id);

            if(e != null)
            {
                e.unblock();

                self.sendData();
            }
        }
    });

    this.port.postMessage({'stopflashInit': true, 'stopflashIsMainFrame': (window == window.top)});

    var observer = new MutationObserver(function(changes)
    {
        var changed = false;

        for(var i = 0, c; i < changes.length; ++i)
        {
            c = changes[i];

            if(c.addedNodes != null)
            {
                changed = (self.add(c.addedNodes) || false);
            }

            if(c.removedNodes != null)
            {
                changed = (self.remove(c.removedNodes) || false);
            }
        }

        if(changed)
        {
            self.sendData();
        }
    });

    observer.observe(doc, {childList: true, subtree: true});

    this.sendData();
}
// function sendData():void
FlashCollection.prototype.sendData = function()
{
    var data = [];

    for(var i = 0; i < this.flashElements.length; ++i)
    {
        data.push(this.flashElements[i].getData());
    }

    this.port.postMessage({'stopflashDataUpdate': true, 'stopflashData': data, 'stopflashCollectionId': this.id});
};
// function add(NodeList elements):boolean
FlashCollection.prototype.add = function(elements)
{
    var changed = false;

    for(var i = 0, e; i < elements.length; ++i)
    {
        e = elements[i];

        if(isFlash(e))
        {
            var parent = e;

            while(parent = parent.parentNode)
            {
                if(isFlash(parent))
                {
                    e = parent;
                }
            }

            var f = this.get(e);

            if(f == null)
            {
                f = new FlashElement(this, ++this.elementId, e);

                if(f.isValid())
                {
                    this.flashElements.push(f);

                    if(!f.isWhitelist())
                    {
                        f.block();
                    }

                    changed = true;
                }
            }
            else
            {
                f.update();

                if(f.blocked)
                {
                    f.block();
                }
            }
        }
    }

    return changed;
};
// function remove(NodeList elements):boolean
FlashCollection.prototype.remove = function(elements)
{
    var changed = false;

    for(var i = 0, f; i < elements.length; ++i)
    {
        f = this.get(elements[i]);

        if(f != null && !f.blocked)
        {
            f.remove();

            changed = true;
        }
    }

    return changed;
};
// function get(HTMLElement element)
FlashCollection.prototype.get = function(element)
{
    for(var i = 0; i < this.flashElements.length; ++i)
    {
        if(element == this.flashElements[i].element)
        {
            return this.flashElements[i];
        }
    }

    return null;
};
// function getById(int id)
FlashCollection.prototype.getById = function(id)
{
    for(var i = 0; i < this.flashElements.length; ++i)
    {
        if(id === this.flashElements[i].id)
        {
            return this.flashElements[i];
        }
    }

    return null;
};

var styles = [
    'float', 'clear',
    'position', 'top', 'left',
    'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
    'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
    'border-top', 'border-right', 'border-bottom', 'border-left'
]; // :Array<String>

var iconSizes = [128, 64, 48]; // :Array<int>

// class FlashElement
function FlashElement(collection, id, element)
{
    this.collection = collection; // :FlashCollection

    this.id = id; // :int

    this.parent = element.parentNode; // :HTMLElement
    this.element = element; // :HTMLElement
    this.nextSibling = element.nextSibling; // :HTMLElement

    this.blocked = false; // :boolean

    var self = this;

    var width = parseInt(Builder.getStyle(element, 'width'));
    var height = parseInt(Builder.getStyle(element, 'height'));

    var size = 32;

    for(var i = 0, s; i < iconSizes.length; ++i)
    {
        s = iconSizes[i];

        if(s < width && s < height)
        {
            size = s;
            break;
        }
    }

    var replacement = new Builder('div')
        .css('display', 'block')
        .css('width', width +'px')
        .css('height', height +'px')
        .css('cursor', 'pointer')
        .css('min-width', '34px')
        .css('min-height', '34px')
        .css('background', '#333 url("'+ chrome.extension.getURL('icons/stopflash_'+ size +'.png') +'") center center no-repeat')
        .event('click', function()
        {
            self.unblock();

            self.collection.sendData();
        });

    for(var i = 0; i < styles.length; ++i)
    {
        replacement.css(styles[i], Builder.getStyle(element, styles[i]));
    }

    this.replacement = replacement.node; // :HTMLElement
}
// function isValid():boolean
FlashElement.prototype.isValid = function()
{
    return (this.parent != null && this.getUrl() != null);
};
// function getData():Object
FlashElement.prototype.getData = function()
{
    return {
        'collection': this.collection.id,
        'id': this.id,
        'type': this.element.nodeName,
        'url': this.getUrl(),
        'blocked': this.blocked,
        'whitelist': this.isWhitelist()
    };
};
// function isWhitelist():boolean
FlashElement.prototype.isWhitelist = function()
{
    var url = this.getUrl();

    for(var i = 0; i < this.collection.whitelist.length; ++i)
    {
        if(url.indexOf(this.collection.whitelist[i]) >= 0)
        {
            return true;
        }
    }

    return false;
};
// function getUrl():String
FlashElement.prototype.getUrl = function()
{
    if(this.element.nodeName === 'EMBED')
    {
        return this.element.src;
    }
    else if(this.element.nodeName === 'OBJECT')
    {
        if(this.element.data)
        {
            return this.element.data;
        }
        else
        {
            var child = this.element.firstChild;

            while(child != null)
            {
                if(child.nodeName === 'PARAM' && (child.name === 'src' || child.name === 'movie'))
                {
                    return child.value;
                }

                child = child.nextSibling;
            }
        }
    }

    return null;
};
// function update():void
FlashElement.prototype.update = function()
{
    if(this.element.parentNode)
    {
        this.parent = this.element.parentNode;
        this.nextSibling = this.element.nextSibling;
    }
    else if(this.replacement.parentNode)
    {
        this.parent = this.replacement.parentNode;
        this.nextSibling = this.replacement.nextSibling;
    }
};
// function block():void
FlashElement.prototype.block = function()
{
    this.blocked = true;

    this.parent.insertBefore(this.replacement, this.nextSibling);

    this.parent.removeChild(this.element);
};
// function unblock():void
FlashElement.prototype.unblock = function()
{
    this.blocked = false;

    this.parent.insertBefore(this.element, this.nextSibling);

    this.parent.removeChild(this.replacement);
};
// function remove():void
FlashElement.prototype.remove = function()
{
    if(this.replacement.parentNode)
    {
        this.replacement.parentNode.removeChild(this.replacement);
    }

    if(this.element.parentNode)
    {
        this.element.parentNode.removeChild(this.element);
    }
};

// main
var collection = null;

var main = function()
{
    collection = new FlashCollection(document);
};

document.addEventListener('DOMContentLoaded', main, false);
<|endoftext|>


<commit_before>const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  service: 'SendGrid',
  auth: {
    user: process.env.SENDGRID_USER,
    pass: process.env.SENDGRID_PASSWORD
  }
});

/**
 * GET /contact
 * Contact form page.
 */
exports.getContact = (req, res) => {

  var unknownUser = true;
  if (req.user) {
    unknownUser = false;
  }

  res.render('contact', {
    title: 'Contact',
    unknownUser: unknownUser,
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 */
exports.postContact = (req, res) => {
  var fromName;
  var fromEmail;
  if (!req.user) {
    req.assert('name', 'Name cannot be blank').notEmpty();
    req.assert('email', 'Email is not valid').isEmail();
  }
  req.assert('message', 'Message cannot be blank').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/contact');
  }

  if (!req.user) {
    fromName = req.body.name;
    fromEmail = req.body.email;
  } else {
    fromName = req.user.profile.name || '';
    fromEmail = req.user.email;
  }

  const mailOptions = {
    to: 'your@email.com',
    from: `${fromName} <${fromEmail}>`,
    subject: 'Contact Form | Hackathon Starter',
    text: req.body.message
  };

  transporter.sendMail(mailOptions, (err) => {
    if (err) {
      req.flash('errors', { msg: err.message });
      return res.redirect('/contact');
    }
    req.flash('success', { msg: 'Email has been sent successfully!' });
    res.redirect('/contact');
  });
};
<commit_msg>Minor improvement in contact controller Initialize conditionally when declaring the variable<commit_after>const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  service: 'SendGrid',
  auth: {
    user: process.env.SENDGRID_USER,
    pass: process.env.SENDGRID_PASSWORD
  }
});

/**
 * GET /contact
 * Contact form page.
 */
exports.getContact = (req, res) => {

  var unknownUser = (req.user) ? false : true;

  res.render('contact', {
    title: 'Contact',
    unknownUser: unknownUser,
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 */
exports.postContact = (req, res) => {
  var fromName;
  var fromEmail;
  if (!req.user) {
    req.assert('name', 'Name cannot be blank').notEmpty();
    req.assert('email', 'Email is not valid').isEmail();
  }
  req.assert('message', 'Message cannot be blank').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/contact');
  }

  if (!req.user) {
    fromName = req.body.name;
    fromEmail = req.body.email;
  } else {
    fromName = req.user.profile.name || '';
    fromEmail = req.user.email;
  }

  const mailOptions = {
    to: 'your@email.com',
    from: `${fromName} <${fromEmail}>`,
    subject: 'Contact Form | Hackathon Starter',
    text: req.body.message
  };

  transporter.sendMail(mailOptions, (err) => {
    if (err) {
      req.flash('errors', { msg: err.message });
      return res.redirect('/contact');
    }
    req.flash('success', { msg: 'Email has been sent successfully!' });
    res.redirect('/contact');
  });
};
<|endoftext|>


<commit_before>describe('Simple use cases', function() {
  it('should return a Text element containing "Some text"', function() {
    var elem = fundomplate()("Some text")()
    chai.assert.equal("Some text", elem.textContent)
  })

  it('should return a Text element containing "123"', function() {
    var elem = fundomplate()(123)()
    chai.assert.equal("123", elem.textContent)
  })

  it('should return a Text element containing "Some" "other" "text"', function() {
    var elem = fundomplate()(["Some", "other", "text"])()
    chai.assert.equal("Someothertext", elem.textContent)
  })

  it('should return a Text element containing "Some" "123.45"', function() {
    var elem = fundomplate()(["Some", 123.45])()
    chai.assert.equal("Some123.45", elem.textContent)
  })

  it('should return a
My title
 element', function() {
    var elem = fundomplate("h1")("My title")()
    chai.assert.equal("
My title
", elem.outerHTML)
  })

  it('should return a <form>....</form> element', function() {
    var elem = fundomplate("form")({ action: "https://github.com/ybr/fundomplate"},
      fundomplate("fieldset")(
        fundomplate("label")({ "for": "myinput" }, "My input"),
        fundomplate("input")({ type: "text", id: "myinput" })
      ),
      fundomplate("div")({ "class": "actions"},
        fundomplate("button")({ type: "submit" }, "OK")
      )
    )()
    chai.assert.equal(elem.tagName, "FORM")
    chai.assert.equal('<form action="https://github.com/ybr/fundomplate"><fieldset>My input<input id="myinput" type="text"></fieldset>
<button type="submit">OK</button>
</form>', elem.outerHTML)
  })
})
<commit_msg>Fix tests<commit_after>describe('Simple use cases', function() {
  var assert = chai.assert

  it('should return a Text element containing "Some text"', function() {
    var elem = fundomplate()("Some text")()
    assert.equal("Some text", elem.textContent)
  })

  it('should return a Text element containing "123"', function() {
    var elem = fundomplate()(123)()
    assert.equal("123", elem.textContent)
  })

  it('should return a Text element containing "Some" "other" "text"', function() {
    var elem = fundomplate()(["Some", "other", "text"])()
    assert.equal("Someothertext", elem.textContent)
  })

  it('should return a Text element containing "Some" "123.45"', function() {
    var elem = fundomplate()(["Some", 123.45])()
    assert.equal("Some123.45", elem.textContent)
  })

  it('should return a
My title
 element', function() {
    var elem = fundomplate("h1")("My title")()
    assert.equal("
My title
", elem.outerHTML)
  })

  it('should return a <form>....</form> element', function() {
    var form = fundomplate("form")({ action: "https://github.com/ybr/fundomplate"},
      fundomplate("fieldset")(
        fundomplate("label")({ "for": "myinput" }, "My input"),
        fundomplate("input")({ type: "text", id: "myinput" })
      ),
      fundomplate("div")({ "class": "actions"},
        fundomplate("button")({ type: "submit" }, "OK")
      )
    )()

    assert.equal(form.tagName, 'FORM')
    assert.equal(form.getAttribute('action'), 'https://github.com/ybr/fundomplate')

    var fieldset = form.childNodes.item(0)
    assert.equal(fieldset.tagName, 'FIELDSET')

    var label = fieldset.childNodes.item(0)
    assert.equal(label.tagName, 'LABEL')
    assert.equal(label.getAttribute('for'), 'myinput')

    var labelText = label.childNodes.item(0)
    assert.equal(labelText.nodeName, '#text')
    assert.equal(labelText.nodeValue, 'My input')

    var input = fieldset.childNodes.item(1)
    assert.equal(input.tagName, 'INPUT')
    assert.equal(input.getAttribute('id'), 'myinput')
    assert.equal(input.getAttribute('type'), 'text')

    var actions = form.childNodes.item(1)
    assert.equal(actions.tagName, 'DIV')
    assert.equal(actions.getAttribute('class'), 'actions')

    var button = actions.childNodes.item(0)
    assert.equal(button.tagName, 'BUTTON')
    assert.equal(button.getAttribute('type'), 'submit')

    var buttonText = button.childNodes.item(0)
    assert.equal(buttonText.nodeName, '#text')
    assert.equal(buttonText.nodeValue, 'OK')
  })
})
<|endoftext|>


<commit_before>import 'whatwg-fetch'
import React, { Component } from 'react'
import isPlainObject from '../utils/isPlainObject'
import deepValue from '../utils/deepValue'
import shallowEqual from '../utils/shallowEqual'
import PromiseState from '../PromiseState'
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'

const defaultMapPropsToRequestsToProps = () => ({})

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

// Helps track hot reloading.
let nextVersion = 0

export default function connect(mapPropsToRequestsToProps, options = {}) {
  const finalMapPropsToRequestsToProps = mapPropsToRequestsToProps || defaultMapPropsToRequestsToProps
  const { withRef = false } = options

  // Helps track hot reloading.
  const version = nextVersion++

  function coerceMappings(rawMappings) {
    invariant(
      isPlainObject(rawMappings),
      '`mapPropsToRequestsToProps` must return an object. Instead received %s.',
      rawMappings
    )

    const mappings = {}
    Object.keys(rawMappings).forEach(prop => {
      mappings[prop] = coerceMapping(prop, rawMappings[prop])
    })
    return mappings
  }

  function coerceMapping(prop, mapping) {
    if (Function.prototype.isPrototypeOf(mapping)) {
      return mapping
    } else if (typeof mapping === 'string') {
      return { url: mapping }
    } else if (isPlainObject(mapping)) {
      invariant(mapping.url, 'Mapping for `%s` of type Object must have `url` attribute.', prop)
      return mapping
    } else if (Array.isArray(mapping)) {
      invariant(false, 'Mapping as array no longer supported. Use a plain object instead with the first element as the `url` attribute.')
    } else if (mapping instanceof window.Request) {
      invariant(false, 'Request object no longer supported. Use a plain object instead with first argument as the `url` attribute.')
    } else {
      invariant(false, 'Mapping for `%s` must be either a string or a plain object. Instead received %s', prop, mapping)
    }
  }

  function didMappingChange(prev, next) {
    return ![ 'url', 'method', 'headers', 'body' ].every((c) => {
      return shallowEqual(deepValue(prev, c), deepValue(next, c))
    })
  }

  function buildRequest(mapping) {
    return new window.Request(mapping.url, {
      method: mapping.method || 'GET',
      headers: Object.assign({
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }, mapping.headers),
      credentials: mapping.credentials || 'same-origin',
      body: mapping.body
    })
  }

  function handleResponse(response) {
    const json = response.json() // TODO: support other response types
    if (response.status >= 200 && response.status < 300) { // TODO: support custom acceptable statuses
      return json
    } else {
      return json.then((errorJson) => {
        const { id, error, message } = errorJson
        if (error) {
          throw new Error(error, id)
        } else if (message) {
          throw new Error(message, id)
        } else {
          throw new Error(errorJson)
        }
      })
    }
  }

  return function wrapWithConnect(WrappedComponent) {
    class RefetchConnect extends Component {
      constructor(props, context) {
        super(props, context)
        this.version = version
        this.state = { mappings: {}, startedAts: {}, data: {}, refreshTimeouts: {} }
      }

      componentWillMount() {
        this.refetchDataFromProps()
      }

      componentWillReceiveProps(nextProps) {
        this.refetchDataFromProps(nextProps)
      }

      componentWillUnmount() {
        this.clearAllRefreshTimeouts()
      }

      render() {
        const ref = withRef ? 'wrappedInstance' : null
        return (
          <WrappedComponent { ...this.state.data } { ...this.props } ref={ref}/>
        )
      }

      getWrappedInstance() {
        invariant(withRef,
          `To access the wrapped instance, you need to specify ` +
          `{ withRef: true } as the fourth argument of the connect() call.`
        )

        return this.refs.wrappedInstance
      }

      refetchDataFromProps(props = this.props) {
        this.refetchDataFromMappings(finalMapPropsToRequestsToProps(props) || {})
      }

      refetchDataFromMappings(mappings) {
        mappings = coerceMappings(mappings)
        Object.keys(mappings).forEach(prop => {
          const mapping = mappings[prop]

          if (Function.prototype.isPrototypeOf(mapping)) {
            this.setAtomicState(prop, new Date(), mapping, (...args) => {
              this.refetchDataFromMappings(mapping(...args || {}))
            })
            return
          }

          if (mapping.force || mapping.refreshing || didMappingChange(this.state.mappings[prop], mapping)) {
            this.refetchDatum(prop, mapping)
          }
        })
      }

      refetchDatum(prop, mapping) {
        const startedAt = new Date()

        if (this.state.refreshTimeouts[prop]) {
          window.clearTimeout(this.state.refreshTimeouts[prop])
        }

        const request = buildRequest(mapping)
        const meta = { request: request }

        this.setAtomicState(prop, startedAt, mapping, new PromiseState({
          pending: !mapping.refreshing,
          refreshing: !!mapping.refreshing,
          fulfilled: !!mapping.refreshing,
          value: mapping.refreshing && this.state.data[prop] ? this.state.data[prop].value : null,
          meta: meta
        }), null)

        window.fetch(request).then(response => {
          meta.response = response

          return Promise.resolve(response).then(handleResponse).then(value => {
            let refreshTimeout = null
            if (mapping.refreshInterval > 0) {
              refreshTimeout = window.setTimeout(() => {
                this.refetchDatum(prop, Object.assign({}, mapping, { refreshing: true }))
              }, mapping.refreshInterval)
            }

            this.setAtomicState(prop, startedAt, mapping, new PromiseState({
              fulfilled: true,
              value: value,
              meta: meta
            }), refreshTimeout)
          }).catch(reason => {
            this.setAtomicState(prop, startedAt, mapping, new PromiseState({
              rejected: true,
              reason: reason,
              meta: meta
            }), null)
          })
        })
      }

      setAtomicState(prop, startedAt, mapping, datum, refreshTimeout) {
        this.setState((prevState) => {
          if (startedAt < prevState.startedAts[prop]) {
            return {}
          }

          return {
            startedAts: Object.assign(
              prevState.startedAts, {
                [prop]: startedAt
              }),
            mappings: Object.assign(
              prevState.mappings, {
                [prop]: mapping
              }),
            data: Object.assign(
              prevState.data, {
                [prop]: datum
              }),
            refreshTimeouts: Object.assign(
              prevState.refreshTimeouts, {
                [prop]: refreshTimeout
              })
          }

        })
      }

      clearAllRefreshTimeouts() {
        Object.keys(this.state.refreshTimeouts).forEach((prop) => {
          clearTimeout(this.state.refreshTimeouts[prop])
        })
      }
    }

    RefetchConnect.displayName = `Refetch.connect(${getDisplayName(WrappedComponent)})`
    RefetchConnect.WrappedComponent = WrappedComponent

    if (process.env.NODE_ENV !== 'production') {
      RefetchConnect.prototype.componentWillUpdate = function componentWillUpdate() {
        if (this.version === version) {
          return
        }

        // We are hot reloading!
        this.version = version
        this.clearAllRefreshTimeouts()
        this.refetchDataFromProps()
      }
    }

    return hoistStatics(RefetchConnect, WrappedComponent)
  }
}
<commit_msg>Set force:true when actually refreshing instead of implicitly forcing when refreshing:true<commit_after>import 'whatwg-fetch'
import React, { Component } from 'react'
import isPlainObject from '../utils/isPlainObject'
import deepValue from '../utils/deepValue'
import shallowEqual from '../utils/shallowEqual'
import PromiseState from '../PromiseState'
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'

const defaultMapPropsToRequestsToProps = () => ({})

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

// Helps track hot reloading.
let nextVersion = 0

export default function connect(mapPropsToRequestsToProps, options = {}) {
  const finalMapPropsToRequestsToProps = mapPropsToRequestsToProps || defaultMapPropsToRequestsToProps
  const { withRef = false } = options

  // Helps track hot reloading.
  const version = nextVersion++

  function coerceMappings(rawMappings) {
    invariant(
      isPlainObject(rawMappings),
      '`mapPropsToRequestsToProps` must return an object. Instead received %s.',
      rawMappings
    )

    const mappings = {}
    Object.keys(rawMappings).forEach(prop => {
      mappings[prop] = coerceMapping(prop, rawMappings[prop])
    })
    return mappings
  }

  function coerceMapping(prop, mapping) {
    if (Function.prototype.isPrototypeOf(mapping)) {
      return mapping
    } else if (typeof mapping === 'string') {
      return { url: mapping }
    } else if (isPlainObject(mapping)) {
      invariant(mapping.url, 'Mapping for `%s` of type Object must have `url` attribute.', prop)
      return mapping
    } else if (Array.isArray(mapping)) {
      invariant(false, 'Mapping as array no longer supported. Use a plain object instead with the first element as the `url` attribute.')
    } else if (mapping instanceof window.Request) {
      invariant(false, 'Request object no longer supported. Use a plain object instead with first argument as the `url` attribute.')
    } else {
      invariant(false, 'Mapping for `%s` must be either a string or a plain object. Instead received %s', prop, mapping)
    }
  }

  function didMappingChange(prev, next) {
    return ![ 'url', 'method', 'headers', 'body' ].every((c) => {
      return shallowEqual(deepValue(prev, c), deepValue(next, c))
    })
  }

  function buildRequest(mapping) {
    return new window.Request(mapping.url, {
      method: mapping.method || 'GET',
      headers: Object.assign({
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }, mapping.headers),
      credentials: mapping.credentials || 'same-origin',
      body: mapping.body
    })
  }

  function handleResponse(response) {
    const json = response.json() // TODO: support other response types
    if (response.status >= 200 && response.status < 300) { // TODO: support custom acceptable statuses
      return json
    } else {
      return json.then((errorJson) => {
        const { id, error, message } = errorJson
        if (error) {
          throw new Error(error, id)
        } else if (message) {
          throw new Error(message, id)
        } else {
          throw new Error(errorJson)
        }
      })
    }
  }

  return function wrapWithConnect(WrappedComponent) {
    class RefetchConnect extends Component {
      constructor(props, context) {
        super(props, context)
        this.version = version
        this.state = { mappings: {}, startedAts: {}, data: {}, refreshTimeouts: {} }
      }

      componentWillMount() {
        this.refetchDataFromProps()
      }

      componentWillReceiveProps(nextProps) {
        this.refetchDataFromProps(nextProps)
      }

      componentWillUnmount() {
        this.clearAllRefreshTimeouts()
      }

      render() {
        const ref = withRef ? 'wrappedInstance' : null
        return (
          <WrappedComponent { ...this.state.data } { ...this.props } ref={ref}/>
        )
      }

      getWrappedInstance() {
        invariant(withRef,
          `To access the wrapped instance, you need to specify ` +
          `{ withRef: true } as the fourth argument of the connect() call.`
        )

        return this.refs.wrappedInstance
      }

      refetchDataFromProps(props = this.props) {
        this.refetchDataFromMappings(finalMapPropsToRequestsToProps(props) || {})
      }

      refetchDataFromMappings(mappings) {
        mappings = coerceMappings(mappings)
        Object.keys(mappings).forEach(prop => {
          const mapping = mappings[prop]

          if (Function.prototype.isPrototypeOf(mapping)) {
            this.setAtomicState(prop, new Date(), mapping, (...args) => {
              this.refetchDataFromMappings(mapping(...args || {}))
            })
            return
          }

          if (mapping.force || didMappingChange(this.state.mappings[prop], mapping)) {
            this.refetchDatum(prop, mapping)
          }
        })
      }

      refetchDatum(prop, mapping) {
        const startedAt = new Date()

        if (this.state.refreshTimeouts[prop]) {
          window.clearTimeout(this.state.refreshTimeouts[prop])
        }

        const request = buildRequest(mapping)
        const meta = { request: request }

        this.setAtomicState(prop, startedAt, mapping, new PromiseState({
          pending: !mapping.refreshing,
          refreshing: !!mapping.refreshing,
          fulfilled: !!mapping.refreshing,
          value: mapping.refreshing && this.state.data[prop] ? this.state.data[prop].value : null,
          meta: meta
        }), null)

        window.fetch(request).then(response => {
          meta.response = response

          return Promise.resolve(response).then(handleResponse).then(value => {
            let refreshTimeout = null
            if (mapping.refreshInterval > 0) {
              refreshTimeout = window.setTimeout(() => {
                this.refetchDatum(prop, Object.assign({}, mapping, { refreshing: true, force: true }))
              }, mapping.refreshInterval)
            }

            this.setAtomicState(prop, startedAt, mapping, new PromiseState({
              fulfilled: true,
              value: value,
              meta: meta
            }), refreshTimeout)
          }).catch(reason => {
            this.setAtomicState(prop, startedAt, mapping, new PromiseState({
              rejected: true,
              reason: reason,
              meta: meta
            }), null)
          })
        })
      }

      setAtomicState(prop, startedAt, mapping, datum, refreshTimeout) {
        this.setState((prevState) => {
          if (startedAt < prevState.startedAts[prop]) {
            return {}
          }

          return {
            startedAts: Object.assign(
              prevState.startedAts, {
                [prop]: startedAt
              }),
            mappings: Object.assign(
              prevState.mappings, {
                [prop]: mapping
              }),
            data: Object.assign(
              prevState.data, {
                [prop]: datum
              }),
            refreshTimeouts: Object.assign(
              prevState.refreshTimeouts, {
                [prop]: refreshTimeout
              })
          }

        })
      }

      clearAllRefreshTimeouts() {
        Object.keys(this.state.refreshTimeouts).forEach((prop) => {
          clearTimeout(this.state.refreshTimeouts[prop])
        })
      }
    }

    RefetchConnect.displayName = `Refetch.connect(${getDisplayName(WrappedComponent)})`
    RefetchConnect.WrappedComponent = WrappedComponent

    if (process.env.NODE_ENV !== 'production') {
      RefetchConnect.prototype.componentWillUpdate = function componentWillUpdate() {
        if (this.version === version) {
          return
        }

        // We are hot reloading!
        this.version = version
        this.clearAllRefreshTimeouts()
        this.refetchDataFromProps()
      }
    }

    return hoistStatics(RefetchConnect, WrappedComponent)
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { GridList, GridTile } from 'material-ui/GridList';
import MenuItem from 'material-ui/MenuItem';
import composeAsyncContainer from '../../common/AsyncContainer';
import composeDescribedDataCard from '../../common/DescribedDataCard';
import DataCard from '../../common/DataCard';
import GeoChart from '../../vis/GeoChart';
import { fetchDemoQueryGeo, fetchQueryGeo, resetGeo } from '../../../actions/explorerActions';
import { DownloadButton } from '../../common/IconButton';
import ActionMenu from '../../common/ActionMenu';
import messages from '../../../resources/messages';
import { hasPermissions, getUserRoles, PERMISSION_LOGGED_IN } from '../../../lib/auth';
import { queryPropertyHasChanged } from '../../../lib/explorerUtil';

const localMessages = {
  title: { id: 'explorer.geo.title', defaultMessage: 'Geographic Attention' },
  intro: { id: 'explorer.geo.info',
    defaultMessage: '
Here is a heatmap of countries mentioned in this collection (based on a sample of sentences). Darker countried are mentioned more. Click a country to load a Dashboard search showing you how the sources in this collection cover it.
' },
  descriptionIntro: { id: 'explorer.geo.help.title', defaultMessage: 'About Geographic Attention' },
};

class GeoPreview extends React.Component {
  componentWillReceiveProps(nextProps) {
    const { lastSearchTime, fetchData } = this.props;

    if (nextProps.lastSearchTime !== lastSearchTime) {
      fetchData(nextProps.queries);
    }
  }
  shouldComponentUpdate(nextProps) {
    const { results, queries } = this.props;
    // only re-render if results, any labels, or any colors have changed
    if (results.length) { // may have reset results so avoid test if results is empty
      const labelsHaveChanged = queryPropertyHasChanged(queries.slice(0, results.length), nextProps.queries.slice(0, results.length), 'label');
      const colorsHaveChanged = queryPropertyHasChanged(queries.slice(0, results.length), nextProps.queries.slice(0, results.length), 'color');
      return (
        ((labelsHaveChanged || colorsHaveChanged))
         || (results !== nextProps.results)
      );
    }
    return false; // if both results and queries are empty, don't update
  }
  downloadCsv = (query) => {
    let url = null;
    if (parseInt(query.searchId, 10) >= 0) {
      url = `/api/explorer/geography/geography.csv/${query.searchId}/${query.index}`;
    } else {
      url = `/api/explorer/geography/geography.csv/[{"q":"${query.q}"}]/${query.index}`;
    }
    window.location = url;
  }

  /* handleCountryClick = (event, geo) => {
    const { results } = this.props;

    // TODO are we supporting this?
    const countryName = geo.name;
    const countryTagId = geo.tags_id;
    // const url = `https://dashboard.mediacloud.org/#query/["(tags_id_stories: ${countryTagId})"]/[{"sets":[${collectionId}]}]/[]/[]/[{"uid":1,"name":"${collectionName} - ${countryName}","color":"55868A"}]`;
    // window.open(url, '_blank');
  } */

  render() {
    const { results, intl, queries } = this.props;
    const { formatMessage } = intl;
    let content = null;
    if (results.length === 1) {
      content = <GeoChart data={results[0]} countryMaxColorScale={queries[0].color} hideLegend />;
    } else {
      const mapTiles = results.map((geoSet, idx) =>
        (<GridTile key={idx}>

{queries && queries.length > idx ? queries[idx].label : ''}

          <GeoChart data={geoSet} countryMaxColorScale={queries && queries.length > idx ? queries[idx].color : ''} hideLegend />
        </GridTile>
        )
      );
      content = (
        <GridList
          className="geo-mini-cards"
          cellHeight={400}
        >
          {mapTiles}
        </GridList>
      );
    }
    return (
      <DataCard>

          <ActionMenu>
            {queries.map((q, idx) =>
              <MenuItem
                key={idx}
                className="action-icon-menu-item"
                primaryText={formatMessage(messages.downloadDataCsv, { name: q.label })}
                rightIcon={<DownloadButton />}
                onTouchTap={() => this.downloadCsv(q)}
              />
            )}
          </ActionMenu>

<FormattedMessage {...localMessages.title} />

        {content}
      </DataCard>
    );
  }

}

GeoPreview.propTypes = {
  lastSearchTime: React.PropTypes.number.isRequired,
  queries: React.PropTypes.array.isRequired,
  user: React.PropTypes.object.isRequired,
  // from composition
  intl: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  results: React.PropTypes.array.isRequired,
  // from mergeProps
  asyncFetch: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
};

const mapStateToProps = state => ({
  lastSearchTime: state.explorer.lastSearchTime.time,
  user: state.user,
  fetchStatus: state.explorer.geo.fetchStatus,
  results: state.explorer.geo.results,
});

const mapDispatchToProps = (dispatch, state) => ({
  fetchData: (queries) => {
    /* this should trigger when the user clicks the Search button or changes the URL
     for n queries, run the dispatch with each parsed query
    */
    const isLoggedInUser = hasPermissions(getUserRoles(state.user), PERMISSION_LOGGED_IN);
    dispatch(resetGeo());
    if (isLoggedInUser) {
      const runTheseQueries = queries || state.queries;
      runTheseQueries.map((q) => {
        const infoToQuery = {
          start_date: q.startDate,
          end_date: q.endDate,
          q: q.q,
          index: q.index,
          sources: q.sources.map(s => s.id),
          collections: q.collections.map(c => c.id),
        };
        return dispatch(fetchQueryGeo(infoToQuery));
      });
    } else if (queries || state.queries) { // else assume DEMO mode, but assume the queries have been loaded
      const runTheseQueries = queries || state.queries;
      runTheseQueries.map((q, index) => {
        const demoInfo = {
          index, // should be same as q.index btw
          search_id: q.searchId, // may or may not have these
          query_id: q.id,
          q: q.q, // only if no query id, means demo user added a keyword
        };
        return dispatch(fetchDemoQueryGeo(demoInfo)); // id
      });
    }
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      dispatchProps.fetchData(ownProps.queries);
    },
  });
}

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps, mergeProps)(
       composeDescribedDataCard(localMessages.descriptionIntro, [messages.storyCountHelpText])(
        composeAsyncContainer(
          GeoPreview
        )
      )
    )
  );
<commit_msg>better geo help text<commit_after>import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { GridList, GridTile } from 'material-ui/GridList';
import MenuItem from 'material-ui/MenuItem';
import composeAsyncContainer from '../../common/AsyncContainer';
import composeDescribedDataCard from '../../common/DescribedDataCard';
import DataCard from '../../common/DataCard';
import GeoChart from '../../vis/GeoChart';
import { fetchDemoQueryGeo, fetchQueryGeo, resetGeo } from '../../../actions/explorerActions';
import { DownloadButton } from '../../common/IconButton';
import ActionMenu from '../../common/ActionMenu';
import messages from '../../../resources/messages';
import { hasPermissions, getUserRoles, PERMISSION_LOGGED_IN } from '../../../lib/auth';
import { queryPropertyHasChanged } from '../../../lib/explorerUtil';

const localMessages = {
  title: { id: 'explorer.geo.title', defaultMessage: 'Geographic Coverage' },
  help: { id: 'explorer.geo.help',
    defaultMessage: '
Here is a heatmap of countries mentioned in this collection (based on a sample of sentences). Darker countried are mentioned more. Click a country to load a Dashboard search showing you how the sources in this collection cover it.
' },
  descriptionIntro: { id: 'explorer.geo.help.title', defaultMessage: 'About Geographic Attention' },
};

class GeoPreview extends React.Component {
  componentWillReceiveProps(nextProps) {
    const { lastSearchTime, fetchData } = this.props;

    if (nextProps.lastSearchTime !== lastSearchTime) {
      fetchData(nextProps.queries);
    }
  }
  shouldComponentUpdate(nextProps) {
    const { results, queries } = this.props;
    // only re-render if results, any labels, or any colors have changed
    if (results.length) { // may have reset results so avoid test if results is empty
      const labelsHaveChanged = queryPropertyHasChanged(queries.slice(0, results.length), nextProps.queries.slice(0, results.length), 'label');
      const colorsHaveChanged = queryPropertyHasChanged(queries.slice(0, results.length), nextProps.queries.slice(0, results.length), 'color');
      return (
        ((labelsHaveChanged || colorsHaveChanged))
         || (results !== nextProps.results)
      );
    }
    return false; // if both results and queries are empty, don't update
  }
  downloadCsv = (query) => {
    let url = null;
    if (parseInt(query.searchId, 10) >= 0) {
      url = `/api/explorer/geography/geography.csv/${query.searchId}/${query.index}`;
    } else {
      url = `/api/explorer/geography/geography.csv/[{"q":"${query.q}"}]/${query.index}`;
    }
    window.location = url;
  }

  /* handleCountryClick = (event, geo) => {
    const { results } = this.props;

    // TODO are we supporting this?
    const countryName = geo.name;
    const countryTagId = geo.tags_id;
    // const url = `https://dashboard.mediacloud.org/#query/["(tags_id_stories: ${countryTagId})"]/[{"sets":[${collectionId}]}]/[]/[]/[{"uid":1,"name":"${collectionName} - ${countryName}","color":"55868A"}]`;
    // window.open(url, '_blank');
  } */

  render() {
    const { results, intl, queries } = this.props;
    const { formatMessage } = intl;
    let content = null;
    if (results.length === 1) {
      content = <GeoChart data={results[0]} countryMaxColorScale={queries[0].color} hideLegend />;
    } else {
      const mapTiles = results.map((geoSet, idx) =>
        (<GridTile key={idx}>

{queries && queries.length > idx ? queries[idx].label : ''}

          <GeoChart data={geoSet} countryMaxColorScale={queries && queries.length > idx ? queries[idx].color : ''} hideLegend />
        </GridTile>
        )
      );
      content = (
        <GridList
          className="geo-mini-cards"
          cellHeight={400}
        >
          {mapTiles}
        </GridList>
      );
    }
    return (
      <DataCard>

          <ActionMenu>
            {queries.map((q, idx) =>
              <MenuItem
                key={idx}
                className="action-icon-menu-item"
                primaryText={formatMessage(messages.downloadDataCsv, { name: q.label })}
                rightIcon={<DownloadButton />}
                onTouchTap={() => this.downloadCsv(q)}
              />
            )}
          </ActionMenu>

<FormattedMessage {...localMessages.title} />

        {content}
      </DataCard>
    );
  }

}

GeoPreview.propTypes = {
  lastSearchTime: React.PropTypes.number.isRequired,
  queries: React.PropTypes.array.isRequired,
  user: React.PropTypes.object.isRequired,
  // from composition
  intl: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  results: React.PropTypes.array.isRequired,
  // from mergeProps
  asyncFetch: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
};

const mapStateToProps = state => ({
  lastSearchTime: state.explorer.lastSearchTime.time,
  user: state.user,
  fetchStatus: state.explorer.geo.fetchStatus,
  results: state.explorer.geo.results,
});

const mapDispatchToProps = (dispatch, state) => ({
  fetchData: (queries) => {
    /* this should trigger when the user clicks the Search button or changes the URL
     for n queries, run the dispatch with each parsed query
    */
    const isLoggedInUser = hasPermissions(getUserRoles(state.user), PERMISSION_LOGGED_IN);
    dispatch(resetGeo());
    if (isLoggedInUser) {
      const runTheseQueries = queries || state.queries;
      runTheseQueries.map((q) => {
        const infoToQuery = {
          start_date: q.startDate,
          end_date: q.endDate,
          q: q.q,
          index: q.index,
          sources: q.sources.map(s => s.id),
          collections: q.collections.map(c => c.id),
        };
        return dispatch(fetchQueryGeo(infoToQuery));
      });
    } else if (queries || state.queries) { // else assume DEMO mode, but assume the queries have been loaded
      const runTheseQueries = queries || state.queries;
      runTheseQueries.map((q, index) => {
        const demoInfo = {
          index, // should be same as q.index btw
          search_id: q.searchId, // may or may not have these
          query_id: q.id,
          q: q.q, // only if no query id, means demo user added a keyword
        };
        return dispatch(fetchDemoQueryGeo(demoInfo)); // id
      });
    }
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      dispatchProps.fetchData(ownProps.queries);
    },
  });
}

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps, mergeProps)(
      composeDescribedDataCard(localMessages.help, [messages.heatMapHelpText])(
        composeAsyncContainer(
          GeoPreview
        )
      )
    )
  );
<|endoftext|>


<commit_before>(function () {

    'use strict';

    function replaceVids() {
    	setTimeout(function () {
	    	var
	    		spans = document.querySelectorAll('.file-video-default-poster:not(.webmify)'),
	    		video;

	    	for (var ea in spans) {
	    		spans[ea].classList.add('webmify');
	    		video = document.createElement('video');
	    		video.src = spans[ea].parentElement.nextSibling.children[0].href;
	    		video.autoplay = false;
	    		video.controls = true;
	    		spans[ea].parentNode.replaceChild(video, spans[ea]);
	    	}
    	}, 1000);
    }

	replaceVids();

}());<commit_msg>Tested, working<commit_after>(function () {

    'use strict';

    function replaceVids() {
        setTimeout(function () {
            var
                selector = '.hc-chat-scrollbox .file-video-default-poster:not(.webmify)',
                spans = document.querySelectorAll(selector),
                video,
                src;

            for (var ea = 0; ea < spans.length; ea += 1) {
                spans[ea].classList.add('webmify');
                src = spans[ea].parentElement.nextSibling.children[0].href;
                if (src.split('.').pop() === 'webm') {
                    video = document.createElement('video');
                    video.src = src;
                    video.autoplay = false;
                    video.controls = true;
                    spans[ea].parentNode.replaceChild(video, spans[ea]);
                    delete spans[ea];
                }
            }

            replaceVids();

        }, 2500);
    }

    replaceVids();

}());<|endoftext|>


<commit_before>'use strict';
var v = require('2valid');
var _ = require('lodash');
var async = require('async');

exports = module.exports = function (name, model) {

  return {
    makeLinks: function(name, id, relations) {
      var res = {};
      res['/' + name + '/' + id] = {
        GET: 'self',
        PUT: 'update',
        PATCH: 'replace',
        DELETE: 'delete',
      };

      res['/' + name] = {
        GET: 'get all ' + name,
        POST: 'add new resource to ' + name,
        DELETE: 'erase ' + name,
      };

      var relKeys = Object.keys(relations);
      relKeys.forEach( function(key) {
        if(relations[key].table1 === name)
          res['/' + key.replace( ':' + relations[key].name, id)] = {
            GET: 'get '+relations[key].table2,
            POST: 'add '+relations[key].table2,
            DELETE: 'delete '+relations[key].table2,
          };
      });
      return res;
    },

    getRelationsData: function (req) {
      var rel = req._relations[req.route.path.replace(/^\/+/, '')];
      if (rel && req.params[rel.name]) rel.data = req.params[rel.name];
      return rel;
    },

    updateRelationsDoc: function (rel, doc, type, field) {
      if(!rel || !doc || !rel[type] || !rel.data) return doc;
      if(rel[type] === 'array') {
        if(!doc[rel[field]]) doc[rel[field]] = [];
        doc[rel[field]].push(rel.data);
      } else {
        doc[rel[field]] = rel.data;
      }
      return doc;
    },

    get: function (req, res, next) {
      var _this = this;
      var q = req.query;
      if (!q._fields) q._fields = q._filter;
      if (!q._start) q._start = q._begin || q._page;
      if (!q._limit) q._limit = q._per_page;
      if (!q._sort) q._sort = q._order;

      var start = parseInt(q._start || 0);
      var limit = parseInt(q._limit || 10);

      var fields = {};
      if (q._fields) {
        var fieldsNames = q._fields.split(',');
        for (var i = 0; i < fieldsNames.length; i++)
          fields[fieldsNames[i]] = 1;
      }

      var sort = {};
      if (q._sort) {
        var sortNames = q._sort.split(',');
        for (var i = 0; i < sortNames.length; i++)
          sort[sortNames[i].replace(/^-/, '')] = (sortNames[i].indexOf('-') === 0) ? -1 : 1;
      }

      var search = _.pickBy(req.query, function (value, key) { return !key.match(/^_/); });

      var searchKeys = Object.keys(search);
      for (var key in search) {
        var f = search[key].match(/^\/(.+)\/([igumy]*)$/);
        if (f && f[1]) {
          search[key] = new RegExp(f[1], f[2]);
        }

        if (search[key] == parseInt(search[key])) {
          search[key] = { $in: [parseInt(search[key]), search[key]] };
        }
      }

      if(req.params.login) search.login = req.params.login;
      if(req.params.group) search.group = req.params.group;
      if(!req.params.group && !req.params.login){
        search.login = undefined;
        search.group = undefined;
      }

      var rel = _this.getRelationsData(req);

      req._log.debug(req._id, name, 'model.get(',
        [search, fields, sort, start, limit, rel].prettyJSON().join(', '), ')');

      model.get(search, fields, sort, start, limit, rel, function (err, docs) {
        if (err) return req._error.show(err);
        if (!docs || !docs[0]) return req._error.NOT_FOUND(name, search);

        docs.forEach(function(doc) {
          doc._links = _this.makeLinks(name, doc._id, req._relations);
        });

        res.json(docs);
      });
    },

    getById: function (req, res, next) {
      var _this = this;
      var search = { _id: req.params._id };
      req._log.debug(req._id, name, 'model.get(', search, ', {} , {} , 0 , 1 , null )');
      model.get(search, {}, {}, 0, 1, null, function (err, docs) {
        if (err) return req._error.show(err);
        if (!docs || !docs[0]) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);

        var doc = docs[0];
        doc._links = _this.makeLinks(name, doc._id, req._relations);

        res.json(doc);
      });
    },

    add: function (req, res, next) {
      var _this = this;
      var doc = req.body;

      async.waterfall([
        function(flow) {
          if (req._options.validation) {
            v.validate(model.object, doc, flow);
          } else {
            flow();
          }
        },
        function(flow) {
          var rel = _this.getRelationsData(req);
          doc = _this.updateRelationsDoc(rel, doc, 'type2', 'field2');

          if(req.params.login) doc.login = req.params.login;
          if(req.params.group) doc.group = req.params.group;

          model.add(doc, flow);
        },
      ], function(err, result, rel) {
        if (err) return req._error.DATA_VALIDATION_ERROR(err);
        // Update related document current data
        var rel = _this.getRelationsData(req);
        if (rel && rel.type1) {
          var id = rel.data;
          var m = req.models[rel.table1];
          m.get({_id: id}, {}, {}, 0, 1, null, function(err,data){
            rel.data = doc._id;
            data[0] = _this.updateRelationsDoc(rel, data[0], 'type1', 'field1');
            m.update(id, {$set: data[0]}, function(){});
          });
        }

        doc._links = _this.makeLinks(name, doc._id, req._relations);

        res.location('/' + name + '/' + doc._id);
        res.status(201).json(doc);
      });

    },

    replace: function (req, res, next) {
      var _this = this;
      var toUpdate = req.body;
      v.validate(model.object, toUpdate, { notRequired: req._updateOnly }, function (err) {
        if (err && req._options.validation) return req._error.DATA_VALIDATION_ERROR(err);

        var search = { _id: req.params._id };
        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        model.get(search, {}, {}, 0, 1, null, function (err, oldDoc) {
          if (Array.isArray(oldDoc)) oldDoc = oldDoc[0];

          if (err) return req._error.show(err);
          if (!oldDoc) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);

          if (!req._updateOnly) oldDoc = { _id: req.params._id };
          toUpdate = _.merge(oldDoc, toUpdate);
          model.update(req.params._id, toUpdate, function (err, doc) {
            if (err) return req._error.show(err);

            if(req.params.login) doc.login = req.params.login;
            if(req.params.group) doc.group = req.params.group;

            doc._links = _this.makeLinks(name, doc._id, req._relations);

            res.location('/' + name + '/' + doc._id);
            res.status(200).json(toUpdate);
          });

        });
      });
    },

    update: function (req, res, next) {
      req._updateOnly = 1;
      this.replace(req, res, next);
    },

    delete: function (req, res, next) {
      var rel = this.getRelationsData(req);
      if(!rel || !rel.data) {
        var search = req.params._id? { _id: req.params._id } : {};

        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        model.delete(search, function (err, doc) {
          if (err) return req._error.show(err);
          if (!doc || !doc.result.n) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);
          res.json({ ok: 1, deleted: _.map(doc.ops, '_id'), updated: [] });
        });
      } else {
        var m = req.models[rel.table2];
        var search = {};
        search[rel.field2] = { $in: [rel.data] };

        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        m.get(search, {}, {}, null, null, null, function (err, doc) {
          var deleted = [];
          var updated = [];
          doc.forEach( function(d) {
            var index = d[rel.field2].indexOf(rel.data);
            if (index > -1) {
              d[rel.field2].splice(index, 1);
            }
            if(!d[rel.field2][0]) {
              deleted.push(d._id);
              m.delete({_id: d._id}, function(err,ddd){console.log(err,ddd)})
            } else {
              updated.push(d._id);
              m.update(d._id, {$set:{writers:d[rel.field2]}}, function(err,ddd){console.log(err,ddd)})
            }
          });
          res.json({ ok: 1, deleted: deleted, updated: updated });
        })
      }
    },

  };

};
<commit_msg>_fields and _links showing<commit_after>'use strict';
var v = require('2valid');
var _ = require('lodash');
var async = require('async');

exports = module.exports = function (name, model) {

  return {
    makeLinks: function(name, id, relations) {
      var res = {};
      res['/' + name + '/' + id] = {
        GET: 'self',
        PUT: 'update',
        PATCH: 'replace',
        DELETE: 'delete',
      };

      res['/' + name] = {
        GET: 'get all ' + name,
        POST: 'add new resource to ' + name,
        DELETE: 'erase ' + name,
      };

      var relKeys = Object.keys(relations);
      relKeys.forEach( function(key) {
        if(relations[key].table1 === name)
          res['/' + key.replace( ':' + relations[key].name, id)] = {
            GET: 'get '+relations[key].table2,
            POST: 'add '+relations[key].table2,
            DELETE: 'delete '+relations[key].table2,
          };
      });
      return res;
    },

    getRelationsData: function (req) {
      var rel = req._relations[req.route.path.replace(/^\/+/, '')];
      if (rel && req.params[rel.name]) rel.data = req.params[rel.name];
      return rel;
    },

    updateRelationsDoc: function (rel, doc, type, field) {
      if(!rel || !doc || !rel[type] || !rel.data) return doc;
      if(rel[type] === 'array') {
        if(!doc[rel[field]]) doc[rel[field]] = [];
        doc[rel[field]].push(rel.data);
      } else {
        doc[rel[field]] = rel.data;
      }
      return doc;
    },

    get: function (req, res, next) {
      var _this = this;
      var q = req.query;
      if (!q._fields) q._fields = q._filter;
      if (!q._start) q._start = q._begin || q._page;
      if (!q._limit) q._limit = q._per_page;
      if (!q._sort) q._sort = q._order;

      var start = parseInt(q._start || 0);
      var limit = parseInt(q._limit || 10);

      var fields = {};
      if (q._fields) {
        var fieldsNames = q._fields.split(',');
        for (var i = 0; i < fieldsNames.length; i++)
          fields[fieldsNames[i]] = 1;
      }

      var sort = {};
      if (q._sort) {
        var sortNames = q._sort.split(',');
        for (var i = 0; i < sortNames.length; i++)
          sort[sortNames[i].replace(/^-/, '')] = (sortNames[i].indexOf('-') === 0) ? -1 : 1;
      }

      var search = _.pickBy(req.query, function (value, key) { return !key.match(/^_/); });

      var searchKeys = Object.keys(search);
      for (var key in search) {
        var f = search[key].match(/^\/(.+)\/([igumy]*)$/);
        if (f && f[1]) {
          search[key] = new RegExp(f[1], f[2]);
        }

        if (search[key] == parseInt(search[key])) {
          search[key] = { $in: [parseInt(search[key]), search[key]] };
        }
      }

      if(req.params.login) search.login = req.params.login;
      if(req.params.group) search.group = req.params.group;
      if(!req.params.group && !req.params.login){
        search.login = undefined;
        search.group = undefined;
      }

      var rel = _this.getRelationsData(req);

      req._log.debug(req._id, name, 'model.get(',
        [search, fields, sort, start, limit, rel].prettyJSON().join(', '), ')');

      model.get(search, fields, sort, start, limit, rel, function (err, docs) {
        if (err) return req._error.show(err);
        if (!docs || !docs[0]) return req._error.NOT_FOUND(name, search);

        if(!fields || fields._links) {
          docs.forEach(function(doc) {
            doc._links = _this.makeLinks(name, doc._id, req._relations);
          })
        }

        res.json(docs);
      });
    },

    getById: function (req, res, next) {
      var _this = this;
      var search = { _id: req.params._id };
      req._log.debug(req._id, name, 'model.get(', search, ', {} , {} , 0 , 1 , null )');
      model.get(search, {}, {}, 0, 1, null, function (err, docs) {
        if (err) return req._error.show(err);
        if (!docs || !docs[0]) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);

        var doc = docs[0];
        doc._links = _this.makeLinks(name, doc._id, req._relations);

        res.json(doc);
      });
    },

    add: function (req, res, next) {
      var _this = this;
      var doc = req.body;

      async.waterfall([
        function(flow) {
          if (req._options.validation) {
            v.validate(model.object, doc, flow);
          } else {
            flow();
          }
        },
        function(flow) {
          var rel = _this.getRelationsData(req);
          doc = _this.updateRelationsDoc(rel, doc, 'type2', 'field2');

          if(req.params.login) doc.login = req.params.login;
          if(req.params.group) doc.group = req.params.group;

          model.add(doc, flow);
        },
      ], function(err, result, rel) {
        if (err) return req._error.DATA_VALIDATION_ERROR(err);
        // Update related document current data
        var rel = _this.getRelationsData(req);
        if (rel && rel.type1) {
          var id = rel.data;
          var m = req.models[rel.table1];
          m.get({_id: id}, {}, {}, 0, 1, null, function(err,data){
            rel.data = doc._id;
            data[0] = _this.updateRelationsDoc(rel, data[0], 'type1', 'field1');
            m.update(id, {$set: data[0]}, function(){});
          });
        }

        doc._links = _this.makeLinks(name, doc._id, req._relations);

        res.location('/' + name + '/' + doc._id);
        res.status(201).json(doc);
      });

    },

    replace: function (req, res, next) {
      var _this = this;
      var toUpdate = req.body;
      v.validate(model.object, toUpdate, { notRequired: req._updateOnly }, function (err) {
        if (err && req._options.validation) return req._error.DATA_VALIDATION_ERROR(err);

        var search = { _id: req.params._id };
        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        model.get(search, {}, {}, 0, 1, null, function (err, oldDoc) {
          if (Array.isArray(oldDoc)) oldDoc = oldDoc[0];

          if (err) return req._error.show(err);
          if (!oldDoc) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);

          if (!req._updateOnly) oldDoc = { _id: req.params._id };
          toUpdate = _.merge(oldDoc, toUpdate);
          model.update(req.params._id, toUpdate, function (err, doc) {
            if (err) return req._error.show(err);

            if(req.params.login) doc.login = req.params.login;
            if(req.params.group) doc.group = req.params.group;

            doc._links = _this.makeLinks(name, doc._id, req._relations);

            res.location('/' + name + '/' + doc._id);
            res.status(200).json(toUpdate);
          });

        });
      });
    },

    update: function (req, res, next) {
      req._updateOnly = 1;
      this.replace(req, res, next);
    },

    delete: function (req, res, next) {
      var rel = this.getRelationsData(req);
      if(!rel || !rel.data) {
        var search = req.params._id? { _id: req.params._id } : {};

        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        model.delete(search, function (err, doc) {
          if (err) return req._error.show(err);
          if (!doc || !doc.result.n) return req._error.NOT_FOUND(name.replace(/s$/, ''), search);
          res.json({ ok: 1, deleted: _.map(doc.ops, '_id'), updated: [] });
        });
      } else {
        var m = req.models[rel.table2];
        var search = {};
        search[rel.field2] = { $in: [rel.data] };

        if(req.params.login) search.login = req.params.login;
        if(req.params.group) search.group = req.params.group;

        m.get(search, {}, {}, null, null, null, function (err, doc) {
          var deleted = [];
          var updated = [];
          doc.forEach( function(d) {
            var index = d[rel.field2].indexOf(rel.data);
            if (index > -1) {
              d[rel.field2].splice(index, 1);
            }
            if(!d[rel.field2][0]) {
              deleted.push(d._id);
              m.delete({_id: d._id}, function(err,ddd){console.log(err,ddd)})
            } else {
              updated.push(d._id);
              m.update(d._id, {$set:{writers:d[rel.field2]}}, function(err,ddd){console.log(err,ddd)})
            }
          });
          res.json({ ok: 1, deleted: deleted, updated: updated });
        })
      }
    },

  };

};
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {Field, reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import { Link } from 'react-router-dom';
import {submitContactForm} from '../actions'

class Contact extends Component {

    renderField(field) {
        const {
            meta: {
                touched,
                error
            }
        } = field;
        const className = `form-group ${touched && error
            ? 'has-danger'
            : ''}`;
        return (

                {field.label}
                <input className="form-control" type={field.type} {...field.input}/>

                    {touched
                        ? error
                        : ''}

        );
    }

    onSubmit(values) {
        this.props.submitContactForm(values, () => {
            this.props.history.push('/');
        });
    }

    render() {
        const {handleSubmit} = this.props;

        return (

              <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
                <Field label="Nombre" name="name" type="text" component={this.renderField}/>
                <Field label="Correo Electrónico" name="email" type="text" component={this.renderField}/>
                <Field label="Mensaje" name="message" type="text" component={this.renderField}/>
                <button type="submit" className="btn btn-primary">Submit</button>
              </form>

        );
    }
}

function validate(values) {
    const errors = {};

    if (!values.name) {
        errors.name = "Necesitamos tu nombre.";
    }

    if (!values.email) {
        errors.email = "Necesitamos tu correo para poder conctactarte.";
    }

    if (!values.message) {
        errors.message = "Favor, ingresa un mensaje.";
    }

    return errors;
}

export default reduxForm({validate, form: 'ContactForm'})(connect(null, {submitContactForm})(Contact));
<commit_msg>error messages<commit_after>import React, {Component} from 'react';
import {Field, reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import { Link } from 'react-router-dom';
import {submitContactForm} from '../actions'

class Contact extends Component {

    renderField(field) {
        const {
            meta: {
                touched,
                error
            }
        } = field;

        const className = `form-group ${touched && error
            ? 'has-danger'
            : ''}`;

        return (

                {field.label}
                <input className="form-control" type={field.type} {...field.input}/>

                    {touched
                        ? error
                        : ''}

        );
    }

    onSubmit(values) {
        this.props.submitContactForm(values, () => {
            this.props.history.push('/');
        });
    }

    render() {
        const {handleSubmit} = this.props;

        return (

              <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
                <Field label="Nombre" name="name" type="text" component={this.renderField}/>
                <Field label="Correo Electrónico" name="email" type="text" component={this.renderField}/>
                <Field label="Mensaje" name="message" type="text" component={this.renderField}/>
                <button type="submit" className="btn btn-primary">Submit</button>
              </form>

        );
    }
}

function validate(values) {
    const errors = {};

    if (!values.name) {
        errors.name = "Necesitamos tu nombre.";
    }

    if (!values.email) {
        errors.email = "Necesitamos tu correo para poder contactarte.";
    }

    if (!values.message) {
        errors.message = "Favor, ingresa un mensaje.";
    }

    return errors;
}

export default reduxForm({validate, form: 'ContactForm'})(connect(null, {submitContactForm})(Contact));
<|endoftext|>


<commit_before>'use strict'

const path = require('path')
const gulp = require('gulp')
const gutil = require('gulp-util')
const svgmin = require('gulp-svgmin')
const config = require('../config')

gulp.task('svg', () => {
  return gulp.src(config.svg.src)
    .pipe(svgmin())
    .pipe(gulp.dest(path.join(config.dest[gutil.env.version], config.images.destDirName)))
})
<commit_msg>move path resolution into variable for svg task<commit_after>'use strict'

const path = require('path')
const gulp = require('gulp')
const gutil = require('gulp-util')
const svgmin = require('gulp-svgmin')
const config = require('../config')

gulp.task('svg', () => {
  const dest = path.join(config.dest[gutil.env.version], config.images.destDirName)

  return gulp.src(config.svg.src)
    .pipe(svgmin())
    .pipe(gulp.dest(dest))
})
<|endoftext|>