<commit_before>// read local Storage
var savedPhrases = JSON.parse(localStorage.getItem('savedPhrases'));
var phrasesLeft;
var phrasesTotal;
if ($.isArray(savedPhrases)){
	phrasesLeft = savedPhrases.slice();
	phrasesTotal = savedPhrases.slice();
}
else{
	phrasesLeft = [];
	phrasesTotal = [];
}
var savedMin = JSON.parse(localStorage.getItem('minDuration'));
var savedMax = JSON.parse(localStorage.getItem('maxDuration'));
var minDuration;
var maxDuration;
if (savedMin && savedMax){
	minDuration = savedMin;
	maxDuration = savedMax;
}
else{
	minDuration = 0;
	maxDuration = 10;
}

function Timer(){
	// pick random duration after which end the current game
	this.duration = Math.random() * (maxDuration - minDuration) + minDuration;
	console.log(this.duration);
	this.tick = function(){
		this.time = (Date.now() - this.startTime) / 1000;
		$('#timer').html(this.time.toFixed(2) + " seconds");
		$("#reset").show();
		$("#cont").html("Continue");
		if (this.time > this.duration || !(this.ticking)){
			clearInterval(this.interval);
			$("#gameMenu").hide();
			$("#startMenu").show();
			this.ticking = false;
			if (this.time > this.duration){
				$("#log").html("You ran out of time.");
			}
		}
	};
	this.stop = function(){
		this.ticking = false;
	};
	this.start = function(){
		this.startTime = Date.now();
		this.ticking = true;
		this.interval = setInterval(this.tick.bind(this), 10);
	};
}

//callback functions
var uploadFile = function(event) {
    var input = event.target;
    var reader = new FileReader();
    reader.onload = function(){
    	var text = reader.result;
    	phrasesLeft = phrasesLeft.concat(text.split("\n"));
    	phrasesTotal = phrasesTotal.concat(text.split("\n"));
    	localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
    };
    reader.readAsText(input.files[0]);
    $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
};

var timer;
var cont = function(){
	$("#startMenu").hide();
	$("#gameMenu").show();
	timer = new Timer();
	timer.start();
	next();
};

var next = function(){
	if (phrasesLeft.length === 0){
		$("#log").html("BOOM! You ran out of catchphrases!");
		$("#cont").hide();
		timer.stop();
	}
	var idx = Math.floor(Math.random() * phrasesLeft.length);
	var nextPhrase = phrasesLeft.splice(idx, 1)[0];
	$("#phrase").html(nextPhrase);
};

var reset = function(){
	phrasesLeft = phrasesTotal.slice();
	$("#reset").hide();
	$("#cont").html("Play");
	$("#cont").show();
};

// listen for button clicks and keypresses
$(document).ready(function(){
	reset();
	console.log("Hello, nerds!");
	 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
	$("#gameMenu").hide();
	$("#reset").hide();
	$("#cont").click(cont);
	$("#next").click(next);
	$("#reset").click(reset);
	$("#newPhrase").keyup(function(key){
		if (key.which === 13){
			// Carriage return
			var newPhrase = document.getElementById("newPhrase").value;
			document.getElementById("newPhrase").value = "";
			phrasesTotal.push(newPhrase);
			phrasesLeft.push(newPhrase);
			localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
			 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
		}
	});
	$("#durationRange").slider({
      range: true,
      min: 0,
      max: 10,
      values: [minDuration, maxDuration],
      slide: function(event, ui) {
      	  minDuration = ui.values[0];
          maxDuration = ui.values[1];
          localStorage.setItem('minDuration', JSON.stringify(minDuration));
          localStorage.setItem('maxDuration', JSON.stringify(maxDuration));
          $("#amount").val(minDuration + "s - " + maxDuration + "s");
      }
    });
    $( "#amount" ).val($( "#durationRange" ).slider( "values", 0 ) + "s - " +
    			       $( "#durationRange" ).slider( "values", 1 ) + "s" );
});

// math
var sgn = function(x){
	if(x === 0){
        return 0;
    }
    else if(x > 0){
        return 1;
    }
    else {
        return -1;
    }
};

var sqrd = function(x){
	return x * x;
};

var normpdf = function(x, a, b, mu, sigma){
	if (a <= x && x <= b){
		return Math.exp(- sqrd(Math.pow((x - mu) / sigma)) / 2) / (Math.sqrt(2 * Math.PI) * sigma);
	}
	else{
		return 0;
	}
};

var erfinv = function(x){
    var a  = 0.147;
    if(x === 0){
    	return 0;
    }
    else {
        var ln = Math.log(1-x*x);
        var firstSqrt = Math.sqrt(sqrd(2 /(Math.PI * a) + ln /2) - ln / a );
        var secondSqrt = Math.sqrt(firstSqrt - (2 / (Math.PI * a) + ln / 2));
        return sgn(x) * secondSqrt;
    }
};

var probit = function(x){
	return Math.sqrt(2) * erfinv(2 * x -1);
};

var normrv = function(a, b, mu, sigma){
	if (a >= b){
		throw "Nonsensical range";
	}
	while (true){
		var rv = mu + sigma * probit(Math.random());
		if (a <= rv && rv <= b){
			return rv;
		}
	}
};

var m = [80, 80, 80, 80]; // margins
var w = 1000 - m[1] - m[3]; // width
var h = 400 - m[0] - m[2]; // height
// create a simple data array that we'll plot with a line (this array represents only the Y values, X will just be the index location)
var data = [3, 6, 2, 7, 5, 2, 0, 3, 8, 9, 2, 5, 9, 3, 6, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 2, 7];
// X scale will fit all values from data[] within pixels 0-w
var x = d3.scale.linear().domain([0, data.length]).range([0, w]);
// Y scale will fit values from 0-10 within pixels h-0 (Note the inverted domain for the y-scale: bigger is up!)
var y = d3.scale.linear().domain([0, 10]).range([h, 0]);
// automatically determining max range can work something like this
// var y = d3.scale.linear().domain([0, d3.max(data)]).range([h, 0]);
// create a line function that can convert data[] into x and y points
var line = d3.svg.line()
	// assign the X function to plot our line as we wish
	.x(function(d,i) {
		// verbose logging to show what's actually being done
		console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
		// return the X coordinate where we want to plot this datapoint
		return x(i);
	})
	.y(function(d) {
		// verbose logging to show what's actually being done
		console.log('Plotting Y value for data point: ' + d + ' to be at: ' + y(d) + " using our yScale.");
		// return the Y coordinate where we want to plot this datapoint
		return y(d);
	})
	// Add an SVG element with the desired dimensions and margin.
	var graph = d3.select("#graph").append("svg:svg")
	      .attr("width", w + m[1] + m[3])
	      .attr("height", h + m[0] + m[2])
	    .append("svg:g")
	      .attr("transform", "translate(" + m[3] + "," + m[0] + ")");
	// create yAxis
	var xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(true);
	// Add the x-axis.
	graph.append("svg:g")
	      .attr("class", "x axis")
	      .attr("transform", "translate(0," + h + ")")
	      .call(xAxis);
	// create left yAxis
	var yAxisLeft = d3.svg.axis().scale(y).ticks(4).orient("left");
	// Add the y-axis to the left
	graph.append("svg:g")
	      .attr("class", "y axis")
	      .attr("transform", "translate(-25,0)")
	      .call(yAxisLeft);

  	// Add the line by appending an svg:path element with the data line we created above
	// do this AFTER the axes above so that the line is above the tick-lines
  	graph.append("svg:path").attr("d", line(data));
<commit_msg>bl.ocks code added<commit_after>// read local Storage
var savedPhrases = JSON.parse(localStorage.getItem('savedPhrases'));
var phrasesLeft;
var phrasesTotal;
if ($.isArray(savedPhrases)){
	phrasesLeft = savedPhrases.slice();
	phrasesTotal = savedPhrases.slice();
}
else{
	phrasesLeft = [];
	phrasesTotal = [];
}
var savedMin = JSON.parse(localStorage.getItem('minDuration'));
var savedMax = JSON.parse(localStorage.getItem('maxDuration'));
var minDuration;
var maxDuration;
if (savedMin && savedMax){
	minDuration = savedMin;
	maxDuration = savedMax;
}
else{
	minDuration = 0;
	maxDuration = 10;
}

function Timer(){
	// pick random duration after which end the current game
	this.duration = Math.random() * (maxDuration - minDuration) + minDuration;
	console.log(this.duration);
	this.tick = function(){
		this.time = (Date.now() - this.startTime) / 1000;
		$('#timer').html(this.time.toFixed(2) + " seconds");
		$("#reset").show();
		$("#cont").html("Continue");
		if (this.time > this.duration || !(this.ticking)){
			clearInterval(this.interval);
			$("#gameMenu").hide();
			$("#startMenu").show();
			this.ticking = false;
			if (this.time > this.duration){
				$("#log").html("You ran out of time.");
			}
		}
	};
	this.stop = function(){
		this.ticking = false;
	};
	this.start = function(){
		this.startTime = Date.now();
		this.ticking = true;
		this.interval = setInterval(this.tick.bind(this), 10);
	};
}

//callback functions
var uploadFile = function(event) {
    var input = event.target;
    var reader = new FileReader();
    reader.onload = function(){
    	var text = reader.result;
    	phrasesLeft = phrasesLeft.concat(text.split("\n"));
    	phrasesTotal = phrasesTotal.concat(text.split("\n"));
    	localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
    };
    reader.readAsText(input.files[0]);
    $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
};

var timer;
var cont = function(){
	$("#startMenu").hide();
	$("#gameMenu").show();
	timer = new Timer();
	timer.start();
	next();
};

var next = function(){
	if (phrasesLeft.length === 0){
		$("#log").html("BOOM! You ran out of catchphrases!");
		$("#cont").hide();
		timer.stop();
	}
	var idx = Math.floor(Math.random() * phrasesLeft.length);
	var nextPhrase = phrasesLeft.splice(idx, 1)[0];
	$("#phrase").html(nextPhrase);
};

var reset = function(){
	phrasesLeft = phrasesTotal.slice();
	$("#reset").hide();
	$("#cont").html("Play");
	$("#cont").show();
};

// listen for button clicks and keypresses
$(document).ready(function(){
	reset();
	console.log("Hello, nerds!");
	 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
	$("#gameMenu").hide();
	$("#reset").hide();
	$("#cont").click(cont);
	$("#next").click(next);
	$("#reset").click(reset);
	$("#newPhrase").keyup(function(key){
		if (key.which === 13){
			// Carriage return
			var newPhrase = document.getElementById("newPhrase").value;
			document.getElementById("newPhrase").value = "";
			phrasesTotal.push(newPhrase);
			phrasesLeft.push(newPhrase);
			localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
			 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
		}
	});
	$("#durationRange").slider({
      range: true,
      min: 0,
      max: 10,
      values: [minDuration, maxDuration],
      slide: function(event, ui) {
      	  minDuration = ui.values[0];
          maxDuration = ui.values[1];
          localStorage.setItem('minDuration', JSON.stringify(minDuration));
          localStorage.setItem('maxDuration', JSON.stringify(maxDuration));
          $("#amount").val(minDuration + "s - " + maxDuration + "s");
      }
    });
    $( "#amount" ).val($( "#durationRange" ).slider( "values", 0 ) + "s - " +
    			       $( "#durationRange" ).slider( "values", 1 ) + "s" );
});

// math
var sgn = function(x){
	if(x === 0){
        return 0;
    }
    else if(x > 0){
        return 1;
    }
    else {
        return -1;
    }
};

var sqrd = function(x){
	return x * x;
};

var normpdf = function(x, a, b, mu, sigma){
	if (a <= x && x <= b){
		return Math.exp(- sqrd(Math.pow((x - mu) / sigma)) / 2) / (Math.sqrt(2 * Math.PI) * sigma);
	}
	else{
		return 0;
	}
};

var erfinv = function(x){
    var a  = 0.147;
    if(x === 0){
    	return 0;
    }
    else {
        var ln = Math.log(1-x*x);
        var firstSqrt = Math.sqrt(sqrd(2 /(Math.PI * a) + ln /2) - ln / a );
        var secondSqrt = Math.sqrt(firstSqrt - (2 / (Math.PI * a) + ln / 2));
        return sgn(x) * secondSqrt;
    }
};

var probit = function(x){
	return Math.sqrt(2) * erfinv(2 * x -1);
};

var normrv = function(a, b, mu, sigma){
	if (a >= b){
		throw "Nonsensical range";
	}
	while (true){
		var rv = mu + sigma * probit(Math.random());
		if (a <= rv && rv <= b){
			return rv;
		}
	}
};

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var formatDate = d3.time.format("%d-%b-%y");

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.tsv("data.tsv", type, function(error, data) {
  if (error) throw error;

  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain(d3.extent(data, function(d) { return d.close; }));

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Price ($)");

  svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);
});

function type(d) {
  d.date = formatDate.parse(d.date);
  d.close = +d.close;
  return d;
}
<|endoftext|>


<commit_before>import './index.css';
import 'skatejs-named-slots';
import kickflip, { emit, link } from '../../src/index';
import create, { button, form, input, p, slot, text } from '../../src/vdom';

const Xtodo = kickflip('x-todo', {
  events: {
    'todo-value' (e) {
      this.value = e.detail;
    },
    'todo-add' (e) {
      this.items = this.items.concat(e.detail);
      this.value = '';
    },
    'todo-remove' (e) {
      this.items = this.items.filter(item => item !== e.detail);
    }
  },
  properties: {
    items: {
      attribute: false,
      default () {
        return [];
      }
    },
    value: {
      default: ''
    }
  },
  render (elem) {
    create('x-list', { value: elem.value }, function () {
      elem.items.forEach(item => create('x-item', item));
    });
  }
});

const Xlist = kickflip('x-list', {
  events: {
    keyup () {
      emit(this, 'todo-value', { detail: this.value });
    },
    submit (e) {
      e.preventDefault();
      emit(this, 'todo-add', { detail: this.value });
    }
  },
  properties: {
    value: { default: '' }
  },
  render (elem) {
    form(function () {
      input({ onkeyup: link(elem), value: elem.value });
      button({ type: 'submit' }, 'add');
    });
    slot(function () {
      p('There is nothing to do.');
    });
  }
});

const Xitem = kickflip('x-item', {
  events: {
    'click button' () {
      emit(this, 'todo-remove', { detail: this.textContent });
    }
  },
  render () {
    slot();
    text(' ');
    button('remove');
  }
});
<commit_msg>Event ordering.<commit_after>import './index.css';
import 'skatejs-named-slots';
import kickflip, { emit, link } from '../../src/index';
import create, { button, form, input, p, slot, text } from '../../src/vdom';

const Xtodo = kickflip('x-todo', {
  events: {
    'todo-add' (e) {
      this.items = this.items.concat(e.detail);
      this.value = '';
    },
    'todo-remove' (e) {
      this.items = this.items.filter(item => item !== e.detail);
    },
    'todo-value' (e) {
      this.value = e.detail;
    }
  },
  properties: {
    items: {
      attribute: false,
      default () {
        return [];
      }
    },
    value: {
      default: ''
    }
  },
  render (elem) {
    create('x-list', { value: elem.value }, function () {
      elem.items.forEach(item => create('x-item', item));
    });
  }
});

const Xlist = kickflip('x-list', {
  events: {
    keyup () {
      emit(this, 'todo-value', { detail: this.value });
    },
    submit (e) {
      e.preventDefault();
      emit(this, 'todo-add', { detail: this.value });
    }
  },
  properties: {
    value: { default: '' }
  },
  render (elem) {
    form(function () {
      input({ onkeyup: link(elem), value: elem.value });
      button({ type: 'submit' }, 'add');
    });
    slot(function () {
      p('There is nothing to do.');
    });
  }
});

const Xitem = kickflip('x-item', {
  events: {
    'click button' () {
      emit(this, 'todo-remove', { detail: this.textContent });
    }
  },
  render () {
    slot();
    text(' ');
    button('remove');
  }
});
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (pro<commit_msg>Update character<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (proc<|endoftext|>


<commit_before>udefine(['mixedice', 'gameboard/loop', './addable', './base', './graphics', './scene', './renderable', './updateable'], function(mixedice, Loop, addable, Base, Graphics, Scene, renderable, updateable) {
	'use strict';

  var Game = function(descriptor) {
  	if (!this instanceof Game) {
  		return new Game(descriptor);
  	}

  	var self = this;

  	Base.extend([this, Game.prototype], 'Game', function() {
  		descriptor.call(this);

  		Graphics.trigger('initialize', this);
  	});

  	this.container = null;
  	this.width = window.innerWidth;
  	this.height = window.innerHeight;
  	this.color = 'rgb(255, 255, 255)';

  	this.call();

  	renderable.call(this);
  	updateable.call(this);

  	Loop.on('update', function(dt) {
  		self.trigger('update', dt / 1000);
  	});

  	Loop.on('render', function() {
  		self.trigger('render');
  	});
  };

  Game.prototype.addScene = function() {
    this.queue.push(addable(Scene, this.children).apply(this, arguments));
  };

  Game.prototype.showScene = function(name) {
    this.activeScene = name;
    this.trigger('show', this.activeScene, this.children[this.activeScene]);
  };

  Game.prototype.run = function(name) {
  	Loop.run();

  	if (name) {
  		this.showScene(name);
  	}
  };

  return Game;
});
<commit_msg>Game emits resize and orientationchange events<commit_after>udefine(['mixedice', 'gameboard/loop', './addable', './base', './graphics', './scene', './renderable', './updateable'], function(mixedice, Loop, addable, Base, Graphics, Scene, renderable, updateable) {
	'use strict';

  var Game = function(descriptor) {
  	if (!this instanceof Game) {
  		return new Game(descriptor);
  	}

  	var self = this;

  	Base.extend([this, Game.prototype], 'Game', function() {
  		descriptor.call(this);

  		Graphics.trigger('initialize', this);
  	});

  	this.container = null;
  	this.width = window.innerWidth;
  	this.height = window.innerHeight;
  	this.color = 'rgb(255, 255, 255)';

  	this.call();

  	renderable.call(this);
  	updateable.call(this);

  	Loop.on('update', function(dt) {
  		self.trigger('update', dt / 1000);
  	});

  	Loop.on('render', function() {
  		self.trigger('render');
  	});

  	root.addEventListener('resize', function() {
  		self.trigger('resize');
  	}, false);

    root.addEventListener('orientationchange', function() {
    	self.trigger('orientationchange');
    }, false);
  };

  Game.prototype.addScene = function() {
    this.queue.push(addable(Scene, this.children).apply(this, arguments));
  };

  Game.prototype.showScene = function(name) {
    this.activeScene = name;
    this.trigger('show', this.activeScene, this.children[this.activeScene]);
  };

  Game.prototype.run = function(name) {
  	Loop.run();

  	if (name) {
  		this.showScene(name);
  	}
  };

  return Game;
});
<|endoftext|>


<commit_before>/**
 * Exporter: output TopoJSON from hexagon grid
 *
 * Primary reference:
 * https://github.com/mbostock/topojson/wiki/Introduction
 *
 * For GeoJSON geometry specifications see:
 * http://geojson.org/geojson-spec.html#appendix-a-geometry-examples
 */

import {color} from 'd3-color'
import {nest} from 'd3-collection'
import {topology} from 'topojson/server.js'
import {version} from '../../package.json'
import gridGeometry from '../geometry/GridGeometry'
import {fipsColor} from '../utils'
import geographyResource from '../resources/GeographyResource'

export const OBJECT_ID = 'tiles'

class Exporter {
  /** Convert hexagon offset coordinates to TopoJSON */
  toTopoJson(tiles, dataset, metricPerTile, geography) {
    const maxTileY = tiles.reduce(
      (max, tile) => Math.max(max, tile.position.y),
      -Infinity
    )
    const geoCodeToName = geographyResource.getGeoCodeHash(geography)
    // Aggregate tiles by state
    const tilesByState = {}
    tiles.forEach(tile => {
      if (!tilesByState[tile.id]) {
        tilesByState[tile.id] = []
      }
      tilesByState[tile.id].push(tile)
    })
    dataset.forEach(d => {
      // even if no tiles, make sure all entries in dataset are added to object
      if (!tilesByState[d[0]]) { tilesByState[d[0]] = null }
    })

    const features = Object.keys(tilesByState).map(stateId => {
      const stateTiles = tilesByState[stateId]
      let tilesCoordinates = null
      let geometry = null
      if (stateTiles !== null) {
        // Generate feature Geometry
        tilesCoordinates = stateTiles.map(tile => {
          // if maxTileY is odd, then subtract one to maintain correct staggering
          const center = gridGeometry.tileCenterPoint({
            x: tile.position.x,
            y: (maxTileY - tile.position.y) - (maxTileY % 2),
          })
          const hexagonPoints = gridGeometry.getPointsAround(center, true)
          hexagonPoints.push([hexagonPoints[0][0], hexagonPoints[0][1]])
          return hexagonPoints
        })
        if (tilesCoordinates.length !== 1) {
          geometry = {
            type: 'MultiPolygon',
            coordinates: tilesCoordinates.map(t => [t]),
          }
        } else {
          geometry = {
            type: 'Polygon',
            coordinates: tilesCoordinates,
          }
        }
      }
      const feature = {
        type: 'Feature',
        geometry,
        id: stateId,
        properties: {
          name: geoCodeToName[stateId].name,
          tilegramValue: dataset.find(d => d[0] === stateId)[1],
        },
      }
      return feature
    })
    const geoJsonObjects = {
      [OBJECT_ID]: {
        type: 'FeatureCollection',
        features,
      },
    }
    // Convert verbose GeoJSON to compressed TopoJSON format
    const topoJson = topology(geoJsonObjects, {
      'property-transform': feature => feature.properties,
      quantization: 1e10,
    })
    topoJson.properties = {
      tilegramMetricPerTile: metricPerTile,
      tilegramVersion: version,
      tilegramTileSize: gridGeometry.getTileDimensions(),
      tilegramGeography: geography,
    }
    return topoJson
  }

  toSvg(tiles, geography) {
    const geoCodeToName = geographyResource.getGeoCodeHash(geography)
    // create svg
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
    const canv = document.getElementById('canvas').getElementsByTagName('canvas')[0]
    const width = canv.width
    const height = canv.height
    svg.setAttribute('width', width)
    svg.setAttribute('height', height)
    const groupedTiles = nest()
      .key((d) => d.id)
      .entries(tiles)
    // add hexagons from tiles
    groupedTiles.forEach((group) => {
      // convert from hsl to hex string for illustrator
      const groupEl = document.createElementNS('http://www.w3.org/2000/svg', 'g')
      groupEl.setAttribute('id', geoCodeToName[group.key])
      const colorString = color(fipsColor(group.key)).toString()
      group.values.forEach((tile) => {
        const center = gridGeometry.tileCenterPoint({
          x: tile.position.x,
          y: tile.position.y,
        })
        const hexagonPoints = gridGeometry.getPointsAround(center, true)
        hexagonPoints.push(hexagonPoints[0]) // close the loop
        const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')
        const points = hexagonPoints.join(',')
        polygon.setAttributeNS(null, 'points', points)
        polygon.setAttributeNS(null, 'fill', colorString)
        groupEl.appendChild(polygon)
      })
      svg.appendChild(groupEl)
    })
    const header = '<?xml version="1.0" encoding="utf-8"?>'
    const svgSerialized = header + new XMLSerializer().serializeToString(svg)
    return svgSerialized
  }

  /** Format TopoJSON from GeoJSON */
  fromGeoJSON(geoJSON, objectId) {
    objectId = objectId || 'states'
    const arcs = []
    const topoJson = {
      type: 'Topology',
      transform: {
        scale: [1.0, 1.0],
        translate: [0.0, 0.0],
      },
      objects: {
        [objectId]: {
          type: 'GeometryCollection',
          geometries: geoJSON.features.map(feature => {
            const geometryArcIndices = []
            const hasMultiplePaths = feature.geometry.coordinates.length > 1
            feature.geometry.coordinates.forEach(path => {
              const points = hasMultiplePaths ? path[0] : path
              const arc = []
              points.forEach((point, pointIndex) => {
                if (pointIndex === 0) {
                  arc.push(point)
                } else {
                  arc.push([
                    points[pointIndex][0] - points[pointIndex - 1][0],
                    points[pointIndex][1] - points[pointIndex - 1][1],
                  ])
                }
              })
              arcs.push(arc)
              geometryArcIndices.push(arcs.length - 1)
            })
            return {
              type: hasMultiplePaths ? 'MultiPolygon' : 'Polygon',
              id: feature.id,
              arcs: hasMultiplePaths ?
                geometryArcIndices.map(index => [[index]]) :
                [geometryArcIndices],
            }
          }),
        },
      },
    }
    topoJson.arcs = arcs
    return topoJson
  }
}

export default new Exporter()
<commit_msg>fix exporter ids appearing as [Object object] instead of actual name<commit_after>/**
 * Exporter: output TopoJSON from hexagon grid
 *
 * Primary reference:
 * https://github.com/mbostock/topojson/wiki/Introduction
 *
 * For GeoJSON geometry specifications see:
 * http://geojson.org/geojson-spec.html#appendix-a-geometry-examples
 */

import {color} from 'd3-color'
import {nest} from 'd3-collection'
import {topology} from 'topojson/server.js'
import {version} from '../../package.json'
import gridGeometry from '../geometry/GridGeometry'
import {fipsColor} from '../utils'
import geographyResource from '../resources/GeographyResource'

export const OBJECT_ID = 'tiles'

class Exporter {
  /** Convert hexagon offset coordinates to TopoJSON */
  toTopoJson(tiles, dataset, metricPerTile, geography) {
    const maxTileY = tiles.reduce(
      (max, tile) => Math.max(max, tile.position.y),
      -Infinity
    )
    const geoCodeToName = geographyResource.getGeoCodeHash(geography)
    // Aggregate tiles by state
    const tilesByState = {}
    tiles.forEach(tile => {
      if (!tilesByState[tile.id]) {
        tilesByState[tile.id] = []
      }
      tilesByState[tile.id].push(tile)
    })
    dataset.forEach(d => {
      // even if no tiles, make sure all entries in dataset are added to object
      if (!tilesByState[d[0]]) { tilesByState[d[0]] = null }
    })

    const features = Object.keys(tilesByState).map(stateId => {
      const stateTiles = tilesByState[stateId]
      let tilesCoordinates = null
      let geometry = null
      if (stateTiles !== null) {
        // Generate feature Geometry
        tilesCoordinates = stateTiles.map(tile => {
          // if maxTileY is odd, then subtract one to maintain correct staggering
          const center = gridGeometry.tileCenterPoint({
            x: tile.position.x,
            y: (maxTileY - tile.position.y) - (maxTileY % 2),
          })
          const hexagonPoints = gridGeometry.getPointsAround(center, true)
          hexagonPoints.push([hexagonPoints[0][0], hexagonPoints[0][1]])
          return hexagonPoints
        })
        if (tilesCoordinates.length !== 1) {
          geometry = {
            type: 'MultiPolygon',
            coordinates: tilesCoordinates.map(t => [t]),
          }
        } else {
          geometry = {
            type: 'Polygon',
            coordinates: tilesCoordinates,
          }
        }
      }
      const feature = {
        type: 'Feature',
        geometry,
        id: stateId,
        properties: {
          name: geoCodeToName[stateId].name,
          tilegramValue: dataset.find(d => d[0] === stateId)[1],
        },
      }
      return feature
    })
    const geoJsonObjects = {
      [OBJECT_ID]: {
        type: 'FeatureCollection',
        features,
      },
    }
    // Convert verbose GeoJSON to compressed TopoJSON format
    const topoJson = topology(geoJsonObjects, {
      'property-transform': feature => feature.properties,
      quantization: 1e10,
    })
    topoJson.properties = {
      tilegramMetricPerTile: metricPerTile,
      tilegramVersion: version,
      tilegramTileSize: gridGeometry.getTileDimensions(),
      tilegramGeography: geography,
    }
    return topoJson
  }

  toSvg(tiles, geography) {
    const geoCodeToName = geographyResource.getGeoCodeHash(geography)
    // create svg
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
    const canv = document.getElementById('canvas').getElementsByTagName('canvas')[0]
    const width = canv.width
    const height = canv.height
    svg.setAttribute('width', width)
    svg.setAttribute('height', height)
    const groupedTiles = nest()
      .key((d) => d.id)
      .entries(tiles)
    // add hexagons from tiles
    groupedTiles.forEach((group) => {
      // convert from hsl to hex string for illustrator
      const groupEl = document.createElementNS('http://www.w3.org/2000/svg', 'g')
      groupEl.setAttribute('id', geoCodeToName[group.key].name)
      const colorString = color(fipsColor(group.key)).toString()
      group.values.forEach((tile) => {
        const center = gridGeometry.tileCenterPoint({
          x: tile.position.x,
          y: tile.position.y,
        })
        const hexagonPoints = gridGeometry.getPointsAround(center, true)
        hexagonPoints.push(hexagonPoints[0]) // close the loop
        const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')
        const points = hexagonPoints.join(',')
        polygon.setAttributeNS(null, 'points', points)
        polygon.setAttributeNS(null, 'fill', colorString)
        groupEl.appendChild(polygon)
      })
      svg.appendChild(groupEl)
    })
    const header = '<?xml version="1.0" encoding="utf-8"?>'
    const svgSerialized = header + new XMLSerializer().serializeToString(svg)
    return svgSerialized
  }

  /** Format TopoJSON from GeoJSON */
  fromGeoJSON(geoJSON, objectId) {
    objectId = objectId || 'states'
    const arcs = []
    const topoJson = {
      type: 'Topology',
      transform: {
        scale: [1.0, 1.0],
        translate: [0.0, 0.0],
      },
      objects: {
        [objectId]: {
          type: 'GeometryCollection',
          geometries: geoJSON.features.map(feature => {
            const geometryArcIndices = []
            const hasMultiplePaths = feature.geometry.coordinates.length > 1
            feature.geometry.coordinates.forEach(path => {
              const points = hasMultiplePaths ? path[0] : path
              const arc = []
              points.forEach((point, pointIndex) => {
                if (pointIndex === 0) {
                  arc.push(point)
                } else {
                  arc.push([
                    points[pointIndex][0] - points[pointIndex - 1][0],
                    points[pointIndex][1] - points[pointIndex - 1][1],
                  ])
                }
              })
              arcs.push(arc)
              geometryArcIndices.push(arcs.length - 1)
            })
            return {
              type: hasMultiplePaths ? 'MultiPolygon' : 'Polygon',
              id: feature.id,
              arcs: hasMultiplePaths ?
                geometryArcIndices.map(index => [[index]]) :
                [geometryArcIndices],
            }
          }),
        },
      },
    }
    topoJson.arcs = arcs
    return topoJson
  }
}

export default new Exporter()
<|endoftext|>


<commit_before>import {inject} from 'aurelia-framework';
import {Rest} from 'spoonx/aurelia-api';

@inject(Rest)
export class Repository {
  /**
   * Construct.
   *
   * @param {Rest} restClient
   *
   * @constructor
   */
  constructor(restClient) {
    this.api = restClient;
  }

  /**
   * Set an entity instance.
   * Used to harvest information such as the resource name.
   *
   * @param {string} resource
   * @return {Repository}
   */
  setResource(resource) {
    this.resource = resource;

    return this;
  }

  /**
   * Get the resource name of this repository instance's reference.
   *
   * @return {string|null}
   */
  getResource() {
    return this.resource;
  }

  /**
   * Perform a find query.
   *
   * @param {null|{}|Number} criteria Criteria to add to the query.
   * @param {boolean}        [raw]    Set to true to get a POJO in stead of populated entities.
   * @return {Promise}
   */
  find(criteria, raw) {
    return this.findPath(this.resource, criteria, raw);
  }

  /**
   * Perform a find query for `path`.
   *
   * @param {string}         path
     @param {null|{}|Number} criteria Criteria to add to the query.
   * @param {boolean}        [raw]    Set to true to get a POJO in stead of populated entities.
   * @return {Promise}
   */
  findPath(path, criteria, raw) {
    let findQuery = this.api.find(path, criteria);

    if (raw) {
      return findQuery;
    }

    return findQuery.then(x => this.populateEntities(x));
  }

  /**
   * Perform a count.
   *
   * @param {null|{}} criteria
   * @return {Promise}
   */
  count(criteria) {
    return this.api.find(this.resource + '/count', criteria);
  }

  /**
   * Populate entities based on supplied data.
   *
   * @param {{}} data
   * @return {*}
   */
  populateEntities(data) {
    if (!data) {
      return null;
    }

    if (!Array.isArray(data)) {
      return this.getPopulatedEntity(data);
    }

    let collection = [];

    data.forEach(source => {
      collection.push(this.getPopulatedEntity(source));
    });

    return collection;
  }

  /**
   * @param {{}} data
   *
   * @return {Entity}
   */
  getPopulatedEntity(data) {
    let entity         = this.getNewEntity();
    let entityMetadata = entity.getMeta();
    let populatedData  = {};
    let key;

    for (key in data) {
      if (!data.hasOwnProperty(key)) {
        continue;
      }

      let value = data[key];

      if (!entityMetadata.has('associations', key) || typeof value !== 'object') {
        // Not an association, or not an object. clean copy.
        populatedData[key] = value;

        continue;
      }

      let repository     = this.entityManager.getRepository(entityMetadata.fetch('associations', key));
      populatedData[key] = repository.populateEntities(value);
    }

    return entity.setData(populatedData);
  }

  /**
   * Get a new instance for entityReference.
   *
   * @return {Entity}
   */
  getNewEntity() {
    return this.entityManager.getEntity(this.resource);
  }

  /**
   * Populate a new entity, with the (empty) associations already set.
   *
   * @return {Entity}
   */
  getNewPopulatedEntity() {
    let entity       = this.getNewEntity();
    let associations = entity.getMeta().fetch('associations');

    for (let property in associations) {
      entity[property] = this.entityManager.getRepository(associations[property]).getNewEntity();
    }

    return entity;
  }
}
<commit_msg>refactor(repository): Mark populated entities as clean<commit_after>import {inject} from 'aurelia-framework';
import {Rest} from 'spoonx/aurelia-api';

@inject(Rest)
export class Repository {
  /**
   * Construct.
   *
   * @param {Rest} restClient
   *
   * @constructor
   */
  constructor(restClient) {
    this.api = restClient;
  }

  /**
   * Set an entity instance.
   * Used to harvest information such as the resource name.
   *
   * @param {string} resource
   * @return {Repository}
   */
  setResource(resource) {
    this.resource = resource;

    return this;
  }

  /**
   * Get the resource name of this repository instance's reference.
   *
   * @return {string|null}
   */
  getResource() {
    return this.resource;
  }

  /**
   * Perform a find query.
   *
   * @param {null|{}|Number} criteria Criteria to add to the query.
   * @param {boolean}        [raw]    Set to true to get a POJO in stead of populated entities.
   * @return {Promise}
   */
  find(criteria, raw) {
    return this.findPath(this.resource, criteria, raw);
  }

  /**
   * Perform a find query for `path`.
   *
   * @param {string}         path
     @param {null|{}|Number} criteria Criteria to add to the query.
   * @param {boolean}        [raw]    Set to true to get a POJO in stead of populated entities.
   * @return {Promise}
   */
  findPath(path, criteria, raw) {
    let findQuery = this.api.find(path, criteria);

    if (raw) {
      return findQuery;
    }

    return findQuery.then(x => this.populateEntities(x));
  }

  /**
   * Perform a count.
   *
   * @param {null|{}} criteria
   * @return {Promise}
   */
  count(criteria) {
    return this.api.find(this.resource + '/count', criteria);
  }

  /**
   * Populate entities based on supplied data.
   *
   * @param {{}} data
   * @return {*}
   */
  populateEntities(data) {
    if (!data) {
      return null;
    }

    if (!Array.isArray(data)) {
      return this.getPopulatedEntity(data);
    }

    let collection = [];

    data.forEach(source => {
      collection.push(this.getPopulatedEntity(source));
    });

    return collection;
  }

  /**
   * @param {{}} data
   *
   * @return {Entity}
   */
  getPopulatedEntity(data) {
    let entity         = this.getNewEntity();
    let entityMetadata = entity.getMeta();
    let populatedData  = {};
    let key;

    for (key in data) {
      if (!data.hasOwnProperty(key)) {
        continue;
      }

      let value = data[key];

      if (!entityMetadata.has('associations', key) || typeof value !== 'object') {
        // Not an association, or not an object. clean copy.
        populatedData[key] = value;

        continue;
      }

      let repository     = this.entityManager.getRepository(entityMetadata.fetch('associations', key));
      populatedData[key] = repository.populateEntities(value);
    }

    return entity.setData(populatedData).markClean();
  }

  /**
   * Get a new instance for entityReference.
   *
   * @return {Entity}
   */
  getNewEntity() {
    return this.entityManager.getEntity(this.resource);
  }

  /**
   * Populate a new entity, with the (empty) associations already set.
   *
   * @return {Entity}
   */
  getNewPopulatedEntity() {
    let entity       = this.getNewEntity();
    let associations = entity.getMeta().fetch('associations');

    for (let property in associations) {
      entity[property] = this.entityManager.getRepository(associations[property]).getNewEntity();
    }

    return entity;
  }
}
<|endoftext|>


<commit_before>/**
 * @author: Dennis Hernández
 * @webSite: http://djhvscf.github.io/Blog
 * @version: v1.0.0
 */

!function ($) {

    'use strict';

    var sprintf = function (str) {
        var args = arguments,
            flag = true,
            i = 1;

        str = str.replace(/%s/g, function () {
            var arg = args[i++];

            if (typeof arg === 'undefined') {
                flag = false;
                return '';
            }
            return arg;
        });
        return flag ? str : '';
    };

    var getFieldIndex = function (columns, field) {
        var index = -1;

        $.each(columns, function (i, column) {
            if (column.field === field) {
                index = i;
                return false;
            }
            return true;
        });
        return index;
    };

    var calculateObjectValue = function (self, name, args, defaultValue) {
        if (typeof name === 'string') {
            // support obj.func1.func2
            var names = name.split('.');

            if (names.length > 1) {
                name = window;
                $.each(names, function (i, f) {
                    name = name[f];
                });
            } else {
                name = window[name];
            }
        }
        if (typeof name === 'object') {
            return name;
        }
        if (typeof name === 'function') {
            return name.apply(self, args);
        }
        return defaultValue;
    };

    var addOptionToSelectControl = function (selectControl, value, text) {
        //selectControl = $(selectControl.get(0));
        if (existsOptionInSelectControl(selectControl, value)) {
            selectControl.append($("<option></option>")
                .attr("value", value)
                .text($('
').html(text).text()));
            // Sort it. Not overly efficient to do this here
            var $opts = selectControl.find('option:gt(0)');
            $opts.sort(function(a,b){
                a = $(a).text().toLowerCase();
                b = $(b).text().toLowerCase();
                if (a === undefined || a === null) { a = ''; }
                if (b === undefined || b === null) { b = ''; }
                if ($.isNumeric(a) && $.isNumeric(b)) {
                    // Convert numerical values from string to float.
                    a = parseFloat(a);
                    b = parseFloat(b);
                }
                return a > b ? 1 : a < b ? -1 : 0;
            });
            selectControl.find('option:gt(0)').remove();
            selectControl.append($opts);
        }
    };

    var existsOptionInSelectControl = function (selectControl, value) {
        var options = selectControl.get(0).options,
            iOpt = 0;

        for (; iOpt < options.length; iOpt++) {
            if (options[iOpt].value === value) {
                //The value is nor valid to add
                return false;
            }
        }

        //If we get here, the value is valid to add
        return true;
    };

    var fixHeaderCSS = function (that) {
        that.$tableHeader.css('height', '77px');
    };

    var getCurrentHeader = function (that) {
        var header = that.$header;
        if (that.options.height) {
            header = that.$tableHeader;
        }

        return header;
    };

    var getCurrentSearchControls = function (that) {
        var searchControls = 'select, input';
        if (that.options.height) {
            searchControls = 'table select, table input';
        }

        return searchControls;
    };

    var copyValues = function (that) {
        var header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        that.options.values = [];

        header.find(searchControls).each(function () {
            that.options.values.push(
                {
                    field: $(this).parent().parent().parent().data('field'),
                    value: $(this).val()
                });
        });
    };

    var setValues = function(that) {
        var field = null,
            result = [],
            header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        if (that.options.values.length > 0) {
            header.find(searchControls).each(function (index, ele) {
                field = $(this).parent().parent().parent().data('field');
                result = $.grep(that.options.values, function (valueObj) {
                    return valueObj.field === field;
                });

                if (result.length > 0) {
                    $(this).val(result[0].value);
                }
            });
        }
    };

    var createControls = function (that, header) {
        var addedFilterControl = false,
            isVisible,
            html,
            timeoutId = 0;

        $.each(that.options.columns, function (i, column) {
            isVisible = 'hidden';
            html = [];

            if (!column.visible) {
                return;
            }

            if (!column.filterControl) {
                html.push('

');
            } else {
                html.push('
');

                if (column.filterControl && column.searchable) {
                    addedFilterControl = true;
                    isVisible = 'visible'
                }
                switch (column.filterControl.toLowerCase()) {
                    case 'input' :
                        html.push(sprintf('<input type="text" class="form-control" style="width: 100%; visibility: %s">', isVisible));
                        break;
                    case 'select':
                        html.push(sprintf('<select class="%s form-control" style="width: 100%; visibility: %s"></select>',
                            column.field, isVisible))
                        break;
                    case 'datepicker':
                        html.push(sprintf('<input type="text" class="date-filter-control %s form-control" style="width: 100%; visibility: %s">',
                            column.field, isVisible));
                        break;
                }
            }

            $.each(header.children().children(), function (i, tr) {
                tr = $(tr);
                if (tr.data('field') === column.field) {
                    tr.find('.fht-cell').append(html.join(''));
                    return false;
                }
            });
            if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') {
                var filterDataType = column.filterData.substring(0, 3);
                var filterDataSource = column.filterData.substring(4, column.filterData.length);
                var selectControl = $('.' + column.field);
                addOptionToSelectControl(selectControl, '', '');

                switch (filterDataType) {
                    case 'url':
                        $.ajax({
                            url: filterDataSource,
                            dataType: 'json',
                            success: function (data) {
                                $.each(data, function (key, value) {
                                    addOptionToSelectControl(selectControl, key, value);
                                });
                            }
                        });
                        break;
                    case 'var':
                        var variableValues = window[filterDataSource];
                        for (var key in variableValues) {
                            addOptionToSelectControl(selectControl, key, variableValues[key]);
                        }
                        break;
                }
            }
        });

        if (addedFilterControl) {
            header.off('keyup', 'input').on('keyup', 'input', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            header.off('change', 'select').on('change', 'select', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            var datepickers = header.find('.date-filter-control');
            if (datepickers.length > 0) {
                $.each(that.options.columns, function (i, column) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') {
                        header.find('.date-filter-control.' + column.field).datepicker(column.filterDatepickerOptions)
                            .on('changeDate', function (e) {
                                //Fired the keyup event
                                $(e.currentTarget).keyup();
                            });
                    }
                });
            }
        } else {
            header.find('.filterControl').hide();
        }
    };

    $.extend($.fn.bootstrapTable.defaults, {
        filterControl: false,
        onColumnSearch: function (field, text) {
            return false;
        },
        //internal variables
        values: []
    });

    $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
        filterControl: undefined,
        filterData: undefined,
        filterDatepickerOptions: undefined
    });

    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
        'column-search.bs.table': 'onColumnSearch'
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _init = BootstrapTable.prototype.init,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _initSearch = BootstrapTable.prototype.initSearch;

    BootstrapTable.prototype.init = function () {
        //Make sure that the filtercontrol option is set
        if (this.options.filterControl) {
            var that = this;
            //Make sure that the internal variables are set correctly
            this.options.values = [];

            this.$el.on('reset-view.bs.table', function () {
                //Create controls on $tableHeader if the height is set
                if (!that.options.height) {
                    return;
                }

                //Avoid recreate the controls
                if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
                    return;
                }

                createControls(that, that.$tableHeader);
            }).on('post-header.bs.table', function () {
                setValues(that);
            }).on('post-body.bs.table', function () {
                if (that.options.height) {
                    fixHeaderCSS(that);
                }
            }).on('column-switch.bs.table', function(field, checked) {
                setValues(that);
            });
        }
        _init.apply(this, Array.prototype.slice.apply(arguments));
    };

    BootstrapTable.prototype.initHeader = function () {
        _initHeader.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.filterControl) {
            return;
        }
        createControls(this, this.$header);
    };

    BootstrapTable.prototype.initBody = function () {
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        var that = this,
            data = this.options.data,
            pageTo = this.pageTo < this.options.data.length ? this.options.data.length : this.pageTo;

        for (var i = this.pageFrom - 1; i < pageTo; i++) {
            var item = data[i];

            $.each(this.header.fields, function (j, field) {
                var value = item[field],
                    column = that.options.columns[getFieldIndex(that.options.columns, field)];

                value = calculateObjectValue(that.header, that.header.formatters[j], [value, item, i], value);

                if ((!column.checkbox) || (!column.radio)) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'select' && column.searchable) {
                        if (column.filterData === undefined || column.filterData.toLowerCase() === 'column') {
                            var selectControl = $('.' + column.field);
                            if (selectControl !== undefined && selectControl.length > 0) {
                                if (selectControl.get(0).options.length === 0) {
                                    //Added the default option
                                    addOptionToSelectControl(selectControl, '', '');
                                }

                                //Added a new value
                                addOptionToSelectControl(selectControl, value, value);
                            }
                        }
                    }
                }
            });
        }
    };

    BootstrapTable.prototype.initSearch = function () {
        _initSearch.apply(this, Array.prototype.slice.apply(arguments));

        var that = this;
        var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;

        //Check partial column filter
        this.data = fp ? $.grep(this.data, function (item, i) {
            for (var key in fp) {
                var fval = fp[key].toLowerCase();
                var value = item[key];
                value = calculateObjectValue(that.header,
                    that.header.formatters[$.inArray(key, that.header.fields)],
                    [value, item, i], value);

                if (!($.inArray(key, that.header.fields) !== -1 &&
                    (typeof value === 'string' || typeof value === 'number') &&
                    (value + '').toLowerCase().indexOf(fval) !== -1)) {
                    return false;
                }
            }
            return true;
        }) : this.data;
    };

    BootstrapTable.prototype.onColumnSearch = function (event) {
        copyValues(this);
        var text = $.trim($(event.currentTarget).val());
        var $field = $(event.currentTarget).parent().parent().parent().data('field')

        if ($.isEmptyObject(this.filterColumnsPartial)) {
            this.filterColumnsPartial = {};
        }
        if (text) {
            this.filterColumnsPartial[$field] = text;
        } else {
            delete this.filterColumnsPartial[$field];
        }

        this.options.pageNumber = 1;
        this.onSearch(event);
        this.updatePagination();
        this.trigger('column-search', $field, text);
    };
}(jQuery);
<commit_msg>Removed un-needed tests.<commit_after>/**
 * @author: Dennis Hernández
 * @webSite: http://djhvscf.github.io/Blog
 * @version: v1.0.0
 */

!function ($) {

    'use strict';

    var sprintf = function (str) {
        var args = arguments,
            flag = true,
            i = 1;

        str = str.replace(/%s/g, function () {
            var arg = args[i++];

            if (typeof arg === 'undefined') {
                flag = false;
                return '';
            }
            return arg;
        });
        return flag ? str : '';
    };

    var getFieldIndex = function (columns, field) {
        var index = -1;

        $.each(columns, function (i, column) {
            if (column.field === field) {
                index = i;
                return false;
            }
            return true;
        });
        return index;
    };

    var calculateObjectValue = function (self, name, args, defaultValue) {
        if (typeof name === 'string') {
            // support obj.func1.func2
            var names = name.split('.');

            if (names.length > 1) {
                name = window;
                $.each(names, function (i, f) {
                    name = name[f];
                });
            } else {
                name = window[name];
            }
        }
        if (typeof name === 'object') {
            return name;
        }
        if (typeof name === 'function') {
            return name.apply(self, args);
        }
        return defaultValue;
    };

    var addOptionToSelectControl = function (selectControl, value, text) {
        //selectControl = $(selectControl.get(0));
        if (existsOptionInSelectControl(selectControl, value)) {
            selectControl.append($("<option></option>")
                .attr("value", value)
                .text($('
').html(text).text()));
            // Sort it. Not overly efficient to do this here
            var $opts = selectControl.find('option:gt(0)');
            $opts.sort(function(a,b){
                a = $(a).text().toLowerCase();
                b = $(b).text().toLowerCase();
                if ($.isNumeric(a) && $.isNumeric(b)) {
                    // Convert numerical values from string to float.
                    a = parseFloat(a);
                    b = parseFloat(b);
                }
                return a > b ? 1 : a < b ? -1 : 0;
            });
            selectControl.find('option:gt(0)').remove();
            selectControl.append($opts);
        }
    };

    var existsOptionInSelectControl = function (selectControl, value) {
        var options = selectControl.get(0).options,
            iOpt = 0;

        for (; iOpt < options.length; iOpt++) {
            if (options[iOpt].value === value) {
                //The value is nor valid to add
                return false;
            }
        }

        //If we get here, the value is valid to add
        return true;
    };

    var fixHeaderCSS = function (that) {
        that.$tableHeader.css('height', '77px');
    };

    var getCurrentHeader = function (that) {
        var header = that.$header;
        if (that.options.height) {
            header = that.$tableHeader;
        }

        return header;
    };

    var getCurrentSearchControls = function (that) {
        var searchControls = 'select, input';
        if (that.options.height) {
            searchControls = 'table select, table input';
        }

        return searchControls;
    };

    var copyValues = function (that) {
        var header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        that.options.values = [];

        header.find(searchControls).each(function () {
            that.options.values.push(
                {
                    field: $(this).parent().parent().parent().data('field'),
                    value: $(this).val()
                });
        });
    };

    var setValues = function(that) {
        var field = null,
            result = [],
            header = getCurrentHeader(that),
            searchControls = getCurrentSearchControls(that);

        if (that.options.values.length > 0) {
            header.find(searchControls).each(function (index, ele) {
                field = $(this).parent().parent().parent().data('field');
                result = $.grep(that.options.values, function (valueObj) {
                    return valueObj.field === field;
                });

                if (result.length > 0) {
                    $(this).val(result[0].value);
                }
            });
        }
    };

    var createControls = function (that, header) {
        var addedFilterControl = false,
            isVisible,
            html,
            timeoutId = 0;

        $.each(that.options.columns, function (i, column) {
            isVisible = 'hidden';
            html = [];

            if (!column.visible) {
                return;
            }

            if (!column.filterControl) {
                html.push('

');
            } else {
                html.push('
');

                if (column.filterControl && column.searchable) {
                    addedFilterControl = true;
                    isVisible = 'visible'
                }
                switch (column.filterControl.toLowerCase()) {
                    case 'input' :
                        html.push(sprintf('<input type="text" class="form-control" style="width: 100%; visibility: %s">', isVisible));
                        break;
                    case 'select':
                        html.push(sprintf('<select class="%s form-control" style="width: 100%; visibility: %s"></select>',
                            column.field, isVisible))
                        break;
                    case 'datepicker':
                        html.push(sprintf('<input type="text" class="date-filter-control %s form-control" style="width: 100%; visibility: %s">',
                            column.field, isVisible));
                        break;
                }
            }

            $.each(header.children().children(), function (i, tr) {
                tr = $(tr);
                if (tr.data('field') === column.field) {
                    tr.find('.fht-cell').append(html.join(''));
                    return false;
                }
            });
            if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') {
                var filterDataType = column.filterData.substring(0, 3);
                var filterDataSource = column.filterData.substring(4, column.filterData.length);
                var selectControl = $('.' + column.field);
                addOptionToSelectControl(selectControl, '', '');

                switch (filterDataType) {
                    case 'url':
                        $.ajax({
                            url: filterDataSource,
                            dataType: 'json',
                            success: function (data) {
                                $.each(data, function (key, value) {
                                    addOptionToSelectControl(selectControl, key, value);
                                });
                            }
                        });
                        break;
                    case 'var':
                        var variableValues = window[filterDataSource];
                        for (var key in variableValues) {
                            addOptionToSelectControl(selectControl, key, variableValues[key]);
                        }
                        break;
                }
            }
        });

        if (addedFilterControl) {
            header.off('keyup', 'input').on('keyup', 'input', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            header.off('change', 'select').on('change', 'select', function (event) {
                clearTimeout(timeoutId);
                timeoutId = setTimeout(function () {
                    that.onColumnSearch(event);
                }, that.options.searchTimeOut);
            });

            var datepickers = header.find('.date-filter-control');
            if (datepickers.length > 0) {
                $.each(that.options.columns, function (i, column) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') {
                        header.find('.date-filter-control.' + column.field).datepicker(column.filterDatepickerOptions)
                            .on('changeDate', function (e) {
                                //Fired the keyup event
                                $(e.currentTarget).keyup();
                            });
                    }
                });
            }
        } else {
            header.find('.filterControl').hide();
        }
    };

    $.extend($.fn.bootstrapTable.defaults, {
        filterControl: false,
        onColumnSearch: function (field, text) {
            return false;
        },
        //internal variables
        values: []
    });

    $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
        filterControl: undefined,
        filterData: undefined,
        filterDatepickerOptions: undefined
    });

    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
        'column-search.bs.table': 'onColumnSearch'
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _init = BootstrapTable.prototype.init,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _initSearch = BootstrapTable.prototype.initSearch;

    BootstrapTable.prototype.init = function () {
        //Make sure that the filtercontrol option is set
        if (this.options.filterControl) {
            var that = this;
            //Make sure that the internal variables are set correctly
            this.options.values = [];

            this.$el.on('reset-view.bs.table', function () {
                //Create controls on $tableHeader if the height is set
                if (!that.options.height) {
                    return;
                }

                //Avoid recreate the controls
                if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
                    return;
                }

                createControls(that, that.$tableHeader);
            }).on('post-header.bs.table', function () {
                setValues(that);
            }).on('post-body.bs.table', function () {
                if (that.options.height) {
                    fixHeaderCSS(that);
                }
            }).on('column-switch.bs.table', function(field, checked) {
                setValues(that);
            });
        }
        _init.apply(this, Array.prototype.slice.apply(arguments));
    };

    BootstrapTable.prototype.initHeader = function () {
        _initHeader.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.filterControl) {
            return;
        }
        createControls(this, this.$header);
    };

    BootstrapTable.prototype.initBody = function () {
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        var that = this,
            data = this.options.data,
            pageTo = this.pageTo < this.options.data.length ? this.options.data.length : this.pageTo;

        for (var i = this.pageFrom - 1; i < pageTo; i++) {
            var item = data[i];

            $.each(this.header.fields, function (j, field) {
                var value = item[field],
                    column = that.options.columns[getFieldIndex(that.options.columns, field)];

                value = calculateObjectValue(that.header, that.header.formatters[j], [value, item, i], value);

                if ((!column.checkbox) || (!column.radio)) {
                    if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'select' && column.searchable) {
                        if (column.filterData === undefined || column.filterData.toLowerCase() === 'column') {
                            var selectControl = $('.' + column.field);
                            if (selectControl !== undefined && selectControl.length > 0) {
                                if (selectControl.get(0).options.length === 0) {
                                    //Added the default option
                                    addOptionToSelectControl(selectControl, '', '');
                                }

                                //Added a new value
                                addOptionToSelectControl(selectControl, value, value);
                            }
                        }
                    }
                }
            });
        }
    };

    BootstrapTable.prototype.initSearch = function () {
        _initSearch.apply(this, Array.prototype.slice.apply(arguments));

        var that = this;
        var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;

        //Check partial column filter
        this.data = fp ? $.grep(this.data, function (item, i) {
            for (var key in fp) {
                var fval = fp[key].toLowerCase();
                var value = item[key];
                value = calculateObjectValue(that.header,
                    that.header.formatters[$.inArray(key, that.header.fields)],
                    [value, item, i], value);

                if (!($.inArray(key, that.header.fields) !== -1 &&
                    (typeof value === 'string' || typeof value === 'number') &&
                    (value + '').toLowerCase().indexOf(fval) !== -1)) {
                    return false;
                }
            }
            return true;
        }) : this.data;
    };

    BootstrapTable.prototype.onColumnSearch = function (event) {
        copyValues(this);
        var text = $.trim($(event.currentTarget).val());
        var $field = $(event.currentTarget).parent().parent().parent().data('field')

        if ($.isEmptyObject(this.filterColumnsPartial)) {
            this.filterColumnsPartial = {};
        }
        if (text) {
            this.filterColumnsPartial[$field] = text;
        } else {
            delete this.filterColumnsPartial[$field];
        }

        this.options.pageNumber = 1;
        this.onSearch(event);
        this.updatePagination();
        this.trigger('column-search', $field, text);
    };
}(jQuery);
<|endoftext|>


<commit_before>/*global document:true, requirejs:true */
'use strict';

requirejs.config({
  baseUrl: '/javascripts/',
  enforceDefine: true,
  paths: {
    jquery: '/javascripts/jquery'
  }
});

define(['jquery', 'appnet', 'friends', 'user', 'jquery.caret'],
  function($, appnet, friends, user) {
  var body = $('body');
  var url = body.data('url');
  var tabs = body.find('ol.tabs');
  var messages = body.find('ol.messages');
  var write = body.find('.write form');
  var userInfo = body.find('.user-info');
  var overlay = body.find('#overlay');
  var charLimit = body.find('.counter');
  var suggestions = body.find('ol.suggestions');
  var notifications = body.find('#notifications-preview');
  var notificationIcon = body.find('#notifications');
  var win = $(window);
  var doc = $(document);
  var csrf = write.find('input[name="_csrf"]').val();
  var postLoad = false;
  var notificationsDisplay = false;
  var currentMessage;

  var CHAR_MAX = parseInt(body.data('charlimit'), 10);
  var TAB_KEYCODE = 9;
  var ESCAPE_KEYCODE = 27;
  var MESSAGE_LIMIT = 99;

  var resetNotificationDisplay = function() {
    notificationIcon
      .removeClass('on')
      .text(0);
    document.title = 'NoodleApp';
    appnet.resetUnread();
  };

  var resetTab = function(self, callback) {
    self.siblings().removeClass('selected');
    self.addClass('selected');
    callback();
  };

  var checkUrl = function() {
    if (document.location.hash.indexOf('/post/') > -1) {
      var postArray = document.location.hash.split('/post/')[1].split('/');
      var postId = postArray[0];
      appnet.showPost(postId);
      body.addClass('fixed');
    }
  };

  // Length of actual text that will be posted to app.net
  var getEffectiveLength = function(text) {
    // Same as in markdown-to-entities, but global
    var markdownLinkRegex = /\[([^\]]+)\]\((\S+(?=\)))\)/g;
    return text.replace(markdownLinkRegex, '$1').length;
  };

  var checkCharLimit = function(text) {
    var textLength = getEffectiveLength(text);
    write.find('button').toggleClass('disabled', textLength === 0);
    if (textLength > CHAR_MAX) {
      charLimit.addClass('over');
      charLimit.text('- ' + (textLength - CHAR_MAX));
    } else {
      charLimit.removeClass('over');
      charLimit.text(CHAR_MAX - textLength);
    }
  };

  var updateFeed = function(self, callback) {
    messages.empty();
    resetTab(self, function() {
      callback();
    });
  };

  var closeOverlay = function() {
    window.history.pushState('', '', document.location.href.split('#')[0]);
    overlay.find('.inner-overlay').html('');
    overlay.find('textarea').val('');
    overlay.slideUp(function() {
      body.removeClass('fixed');
    });
  };

  var saveSettings = function(self) {
    var directedFeed = false;
    var mediaOn = false;
    var charLimit = false;

    if (self.hasClass('on')) {
      self.removeClass('on');
    } else {
      self.addClass('on');
    }

    if (overlay.find('#directed-feed').hasClass('on')) {
      directedFeed = true;
    }

    if (overlay.find('#media-on').hasClass('on')) {
      mediaOn = true;
    }

    if (overlay.find('#charlimit').hasClass('on')) {
      charLimit = true;
    }

    user.saveSettings(directedFeed, mediaOn, charLimit, write.find('input[name="_csrf"]').val());
  };

  /* Feed functionality */
  tabs.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('global-feed'):
        updateFeed(self, function() {
          appnet.getGlobalFeed();
        });
        break;

      case self.hasClass('user-posts'):
        updateFeed(self, function() {
          appnet.getUserPosts();
        });
        break;

      case self.hasClass('user-mentions'):
        updateFeed(self, function() {
          appnet.getUserMentions();
        });
        break;

      case self.hasClass('user-starred'):
        updateFeed(self, function() {
          appnet.getUserStarred();
        });
        break;

      default:
        updateFeed(self, function() {
          appnet.getMyFeed();
        });
        break;
    }
  });

  /* Automatic feed loader */
  tabs.find('.selected').click();

  /* Message functionality */
  messages.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('reply'):
        self.closest('.message-item').find('time').click();
        break;

      case self.hasClass('quote'):
        var textarea = self.closest('.dashboard-content').find('textarea');
        textarea.focus();
        write.find('.form-action-wrapper').slideDown('fast');
        textarea.val('"' + self.closest('.message-item').data('original') + '"');
        break;

      case self.hasClass('delete'):
        appnet.deleteMessage(self.closest('.message-item').data('id'), csrf);
        self.closest('li.message-item').fadeOut();
        break;

      case self.hasClass('thread'):
        appnet.showThread(self.closest('.message-item').data('id'));
        body.addClass('fixed');
        break;

      case self.is('#paginated'):
        appnet.getOlderPosts(self.prev().data('id'));
        break;
    }
  });

  var windowHasScrolled = false;
  var scollCheck = setInterval(function () {
    if (windowHasScrolled) {
      var nearBottom = win.scrollTop() > doc.height() - win.height() - 200;
      if (nearBottom) {
        $('#paginated').click();
      }
    }
    windowHasScrolled = false;
  }, 100);

  win.on('scroll', function () {
    windowHasScrolled = true;
  });

  // Overlay functionality
  overlay.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('reply'):
        var messageItem = self.closest('.message-item');
        var mentions = (messageItem.data('mentions') !== '') ? messageItem.data('mentions') + ' ' : '';

        overlay.find('textarea')
          .focus()
          .val('@' + messageItem.data('username') + ' ' + mentions);

        if (parseInt(messageItem.data('replyto'), 10) > 0) {
          overlay.find('.reply_to').val(messageItem.data('replyto'));

        } else {
         overlay.find('.reply_to').val(messageItem.data('id'));
        }
        break;

      case self.hasClass('thread'):
        appnet.showThread(self.closest('.message-item').data('id'));
        body.addClass('fixed');
        break;

      case self.hasClass('close'):
        closeOverlay();
        break;

      case self.hasClass('quote'):
        var textarea = overlay.find('textarea');
        textarea.focus();
        overlay.find('.form-action-wrapper').slideDown('fast');
        textarea.val('"' + self.closest('.message-item').data('original') + '"');
        break;
    }
  });

  // General activity for all shared functionality
  body.on('click', function(ev) {
    var self = $(ev.target);

    if (!self.hasClass('writeable')) {
      suggestions.empty();
    }

    if (notificationsDisplay &&
      !(self.is('#notifications-preview') ||
      self.is('#notifications') ||
      self.closest('#notifications-preview').length)) {

      notifications.slideUp();
      notificationsDisplay = false;
    }

    switch (true) {
      case self.hasClass('star'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.find('span').text('Star');
          appnet.unstarMessage(self.closest('.message-item').data('id'), csrf);
        } else {
          self.addClass('on');
          self.find('span').text('Unstar');
          appnet.starMessage(self.closest('.message-item').data('id'), csrf);
        }
        break;

      case self.hasClass('repost'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.find('span').text('Repost');
          appnet.unrepostMessage(self.closest('.message-item').data('id'), csrf);
        } else {
          self.addClass('on');
          self.find('span').text('Unrepost');
          appnet.repostMessage(self.closest('.message-item').data('id'), csrf);
        }
        break;

      case self.is('time'):
        var textarea = overlay.find('textarea');
        var closest = self.closest('.message-item');

        document.location.hash = '/post/' + closest.data('id') +
          '/' + closest.data('username');
        break;

      case self.is('#notifications'):
        var self = $(this);
        resetNotificationDisplay();

        if (!notificationsDisplay) {
          notifications.slideDown();
          notificationsDisplay = true;

        } else {
          notifications.slideUp();
          notificationsDisplay = false;
        }
        break;

      case self.hasClass('tags'):
        ev.preventDefault();
        appnet.showTagged(self.attr('href').split('/tagged/')[1]);
        body.addClass('fixed');
        break;

      case self.is('#settings-link'):
        user.getSettings();
        break;

      case self.is('#charlimit'):
      case self.is('#media-on'):
      case self.is('#directed-feed'):
        saveSettings(self);
        break;

      case self.parent().is('#unread-messages'):
        appnet.clearUnread(self.parent());
        resetNotificationDisplay();
        break;
    }
  });

  var keyCodeToClassName = {
    84: '.thread', // t
    83: '.star', // s
    82: '.reply', // r
    80: '.repost', // p
    81: '.quote'  // q
  };

  var actionKeyCodes = $.map(keyCodeToClassName, function  (val, key) { return (+ key); });

  body.on('keydown', function(ev) {
    var self = $(ev.target);

    if (ev.keyCode === ESCAPE_KEYCODE && body.hasClass('fixed')) {
      closeOverlay();
    }

    switch (true) {
      case self.hasClass('submittable'):
        if (ev.keyCode === 9 || ev.keyCode === 32) {
          // Pressing TAB/space autocompletes to the first user listed.
          var userLi = suggestions.find('li:first');
          if (userLi.length) {
            friends.setUser(userLi, self, self.getCursorPosition());
            suggestions.empty();
            return false;
          }

        } else if (ev.keyCode === 13 && (ev.ctrlKey || ev.metaKey)) {
          self.closest('form').submit();
        }
    }

    if (!self.is('textarea')) {
      if (ev.keyCode === 75 || ev.keyCode === 74) {
        currentMessage = body.find('.message-item.hover');
        if (!currentMessage.length) {
          currentMessage = body.find('.message-item:first');
          currentMessage.addClass('hover');
          return;
        }

        var next;
        if (ev.keyCode === 75) {
          next = currentMessage.prev('.message-item');
        } else {
          next = currentMessage.next('.message-item');
        }

        if (next.length) {
          currentMessage.removeClass('hover');
          next.addClass('hover');
          $(window).scrollTop(next.position().top - 40);
          currentMessage = next;
        }
      }

      if (currentMessage && $.inArray(ev.keyCode, actionKeyCodes) !== -1){
        currentMessage.find(keyCodeToClassName[ev.keyCode]).click();
      }
    }
  });

  /* User functionality */
  userInfo.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('followers'):
        appnet.showFollowers();
        body.addClass('fixed');
        break;

      case self.hasClass('following'):
        appnet.showFollowing();
        body.addClass('fixed');
        break;

      case self.hasClass('follow'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.text('Follow');
          appnet.unfollow(self.parent().data('userid'), self.parent().data('username'), csrf);

        } else {
          self.addClass('on');
          self.text('Unfollow');
          appnet.follow(self.parent().data('userid'), self.parent().data('username'), csrf);
        }
        break;

      case self.hasClass('mute'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.text('Mute');
          appnet.unmute(self.parent().data('userid'), self.parent().data('username'), csrf);

        } else {
          self.addClass('on');
          self.text('Unmute');
          appnet.mute(self.parent().data('userid'), self.parent().data('username'), csrf);
        }
        break;
    }
  });

  checkUrl();

  $(window).bind('hashchange', function() {
    checkUrl();
  });

  /* Notifications */
  notifications.on('click', '.notification-item', function(ev) {
    ev.preventDefault();
    var self = $(this);

    notifications.slideUp();
    appnet.showPost(self.data('postid'));
    body.addClass('fixed');
  });

  /* Write functionality */

  checkCharLimit(write.find('textarea').val());

  write.find('textarea').focus(function() {
    var self = $(this);
    self.addClass('on');
    charLimit.addClass('on');
    checkCharLimit(self.val());
    self.closest('form').find('.form-action-wrapper').slideDown('fast');
  });

  write.find('textarea').blur(function() {
    var self = $(this);

    charLimit.removeClass('on');

    if (self.val().replace(/\s/, '').length < 1) {
      self.removeClass('on');
      self.closest('form').find('.form-action-wrapper').slideUp('fast');
    }
  });

  // Clear the reply_to id if this is empty
  write.find('textarea').keyup(function(evt) {
    var self = $(this);
    checkCharLimit(self.val());
    friends.getBFFs(self, self.getCursorPosition());
    if (self.val().trim().length === 0) {
      write.find('.reply_to').val('');
    }

  })

  write.submit(function(ev) {
    ev.preventDefault();

    var self = $(this);
    appnet.postMessage(self);
    url = '/my/feed';
    self.find('textarea').val('');
    charLimit.text(CHAR_MAX);
    write.find('.reply_to').val('');
    return false;
  });

  suggestions.on('click', 'li', function() {
    var self = $(this);
    var textarea = self.closest('form').find('textarea');
    friends.setUser(self, textarea, textarea.getCursorPosition());
  });
});
<commit_msg>Fix Issue #85: Implement /tagged hashtag view.<commit_after>/*global document:true, requirejs:true */
'use strict';

requirejs.config({
  baseUrl: '/javascripts/',
  enforceDefine: true,
  paths: {
    jquery: '/javascripts/jquery'
  }
});

define(['jquery', 'appnet', 'friends', 'user', 'jquery.caret'],
  function($, appnet, friends, user) {
  var body = $('body');
  var url = body.data('url');
  var tabs = body.find('ol.tabs');
  var messages = body.find('ol.messages');
  var write = body.find('.write form');
  var userInfo = body.find('.user-info');
  var overlay = body.find('#overlay');
  var charLimit = body.find('.counter');
  var suggestions = body.find('ol.suggestions');
  var notifications = body.find('#notifications-preview');
  var notificationIcon = body.find('#notifications');
  var win = $(window);
  var doc = $(document);
  var csrf = write.find('input[name="_csrf"]').val();
  var postLoad = false;
  var notificationsDisplay = false;
  var currentMessage;

  var CHAR_MAX = parseInt(body.data('charlimit'), 10);
  var TAB_KEYCODE = 9;
  var ESCAPE_KEYCODE = 27;
  var MESSAGE_LIMIT = 99;

  var resetNotificationDisplay = function() {
    notificationIcon
      .removeClass('on')
      .text(0);
    document.title = 'NoodleApp';
    appnet.resetUnread();
  };

  var resetTab = function(self, callback) {
    self.siblings().removeClass('selected');
    self.addClass('selected');
    callback();
  };

  var checkUrl = function() {
    var hash = document.location.hash;

    if (hash.indexOf('/post/') > -1) {
      var postArray = hash.split('/post/')[1].split('/');
      var postId = postArray[0];
      appnet.showPost(postId);
      body.addClass('fixed');
    } else if (hash.indexOf('/tagged/') > -1) {
      appnet.showTagged(hash.split('/tagged/')[1]);
      body.addClass('fixed');
    } else if (hash.length < 3 && body.hasClass('fixed')) {
       closeOverlay();
    }
  };

  // Length of actual text that will be posted to app.net
  var getEffectiveLength = function(text) {
    // Same as in markdown-to-entities, but global
    var markdownLinkRegex = /\[([^\]]+)\]\((\S+(?=\)))\)/g;
    return text.replace(markdownLinkRegex, '$1').length;
  };

  var checkCharLimit = function(text) {
    var textLength = getEffectiveLength(text);
    write.find('button').toggleClass('disabled', textLength === 0);
    if (textLength > CHAR_MAX) {
      charLimit.addClass('over');
      charLimit.text('- ' + (textLength - CHAR_MAX));
    } else {
      charLimit.removeClass('over');
      charLimit.text(CHAR_MAX - textLength);
    }
  };

  var updateFeed = function(self, callback) {
    messages.empty();
    resetTab(self, function() {
      callback();
    });
  };

  var closeOverlay = function() {
    window.history.pushState('', '', document.location.href.split('#')[0]);
    overlay.find('.inner-overlay').html('');
    overlay.find('textarea').val('');
    overlay.slideUp(function() {
      body.removeClass('fixed');
    });
  };

  var saveSettings = function(self) {
    var directedFeed = false;
    var mediaOn = false;
    var charLimit = false;

    if (self.hasClass('on')) {
      self.removeClass('on');
    } else {
      self.addClass('on');
    }

    if (overlay.find('#directed-feed').hasClass('on')) {
      directedFeed = true;
    }

    if (overlay.find('#media-on').hasClass('on')) {
      mediaOn = true;
    }

    if (overlay.find('#charlimit').hasClass('on')) {
      charLimit = true;
    }

    user.saveSettings(directedFeed, mediaOn, charLimit, write.find('input[name="_csrf"]').val());
  };

  /* Feed functionality */
  tabs.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('global-feed'):
        updateFeed(self, function() {
          appnet.getGlobalFeed();
        });
        break;

      case self.hasClass('user-posts'):
        updateFeed(self, function() {
          appnet.getUserPosts();
        });
        break;

      case self.hasClass('user-mentions'):
        updateFeed(self, function() {
          appnet.getUserMentions();
        });
        break;

      case self.hasClass('user-starred'):
        updateFeed(self, function() {
          appnet.getUserStarred();
        });
        break;

      default:
        updateFeed(self, function() {
          appnet.getMyFeed();
        });
        break;
    }
  });

  /* Automatic feed loader */
  tabs.find('.selected').click();

  /* Message functionality */
  messages.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('reply'):
        self.closest('.message-item').find('time').click();
        break;

      case self.hasClass('quote'):
        var textarea = self.closest('.dashboard-content').find('textarea');
        textarea.focus();
        write.find('.form-action-wrapper').slideDown('fast');
        textarea.val('"' + self.closest('.message-item').data('original') + '"');
        break;

      case self.hasClass('delete'):
        appnet.deleteMessage(self.closest('.message-item').data('id'), csrf);
        self.closest('li.message-item').fadeOut();
        break;

      case self.hasClass('thread'):
        appnet.showThread(self.closest('.message-item').data('id'));
        body.addClass('fixed');
        break;

      case self.is('#paginated'):
        appnet.getOlderPosts(self.prev().data('id'));
        break;
    }
  });

  var windowHasScrolled = false;
  var scollCheck = setInterval(function () {
    if (windowHasScrolled) {
      var nearBottom = win.scrollTop() > doc.height() - win.height() - 200;
      if (nearBottom) {
        $('#paginated').click();
      }
    }
    windowHasScrolled = false;
  }, 100);

  win.on('scroll', function () {
    windowHasScrolled = true;
  });

  // Overlay functionality
  overlay.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('reply'):
        var messageItem = self.closest('.message-item');
        var mentions = (messageItem.data('mentions') !== '') ? messageItem.data('mentions') + ' ' : '';

        overlay.find('textarea')
          .focus()
          .val('@' + messageItem.data('username') + ' ' + mentions);

        if (parseInt(messageItem.data('replyto'), 10) > 0) {
          overlay.find('.reply_to').val(messageItem.data('replyto'));

        } else {
         overlay.find('.reply_to').val(messageItem.data('id'));
        }
        break;

      case self.hasClass('thread'):
        appnet.showThread(self.closest('.message-item').data('id'));
        body.addClass('fixed');
        break;

      case self.hasClass('close'):
        closeOverlay();
        break;

      case self.hasClass('quote'):
        var textarea = overlay.find('textarea');
        textarea.focus();
        overlay.find('.form-action-wrapper').slideDown('fast');
        textarea.val('"' + self.closest('.message-item').data('original') + '"');
        break;
    }
  });

  // General activity for all shared functionality
  body.on('click', function(ev) {
    var self = $(ev.target);

    if (!self.hasClass('writeable')) {
      suggestions.empty();
    }

    if (notificationsDisplay &&
      !(self.is('#notifications-preview') ||
      self.is('#notifications') ||
      self.closest('#notifications-preview').length)) {

      notifications.slideUp();
      notificationsDisplay = false;
    }

    switch (true) {
      case self.hasClass('star'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.find('span').text('Star');
          appnet.unstarMessage(self.closest('.message-item').data('id'), csrf);
        } else {
          self.addClass('on');
          self.find('span').text('Unstar');
          appnet.starMessage(self.closest('.message-item').data('id'), csrf);
        }
        break;

      case self.hasClass('repost'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.find('span').text('Repost');
          appnet.unrepostMessage(self.closest('.message-item').data('id'), csrf);
        } else {
          self.addClass('on');
          self.find('span').text('Unrepost');
          appnet.repostMessage(self.closest('.message-item').data('id'), csrf);
        }
        break;

      case self.is('time'):
        var textarea = overlay.find('textarea');
        var closest = self.closest('.message-item');

        document.location.hash = '/post/' + closest.data('id') +
          '/' + closest.data('username');
        break;

      case self.is('#notifications'):
        var self = $(this);
        resetNotificationDisplay();

        if (!notificationsDisplay) {
          notifications.slideDown();
          notificationsDisplay = true;

        } else {
          notifications.slideUp();
          notificationsDisplay = false;
        }
        break;

      case self.hasClass('tags'):
        ev.preventDefault();
        document.location.hash = self.attr('href');
        break;

      case self.is('#settings-link'):
        user.getSettings();
        break;

      case self.is('#charlimit'):
      case self.is('#media-on'):
      case self.is('#directed-feed'):
        saveSettings(self);
        break;

      case self.parent().is('#unread-messages'):
        appnet.clearUnread(self.parent());
        resetNotificationDisplay();
        break;
    }
  });

  var keyCodeToClassName = {
    84: '.thread', // t
    83: '.star', // s
    82: '.reply', // r
    80: '.repost', // p
    81: '.quote'  // q
  };

  var actionKeyCodes = $.map(keyCodeToClassName, function  (val, key) { return (+ key); });

  body.on('keydown', function(ev) {
    var self = $(ev.target);

    if (ev.keyCode === ESCAPE_KEYCODE && body.hasClass('fixed')) {
      closeOverlay();
    }

    switch (true) {
      case self.hasClass('submittable'):
        if (ev.keyCode === 9 || ev.keyCode === 32) {
          // Pressing TAB/space autocompletes to the first user listed.
          var userLi = suggestions.find('li:first');
          if (userLi.length) {
            friends.setUser(userLi, self, self.getCursorPosition());
            suggestions.empty();
            return false;
          }

        } else if (ev.keyCode === 13 && (ev.ctrlKey || ev.metaKey)) {
          self.closest('form').submit();
        }
    }

    if (!self.is('textarea')) {
      if (ev.keyCode === 75 || ev.keyCode === 74) {
        currentMessage = body.find('.message-item.hover');
        if (!currentMessage.length) {
          currentMessage = body.find('.message-item:first');
          currentMessage.addClass('hover');
          return;
        }

        var next;
        if (ev.keyCode === 75) {
          next = currentMessage.prev('.message-item');
        } else {
          next = currentMessage.next('.message-item');
        }

        if (next.length) {
          currentMessage.removeClass('hover');
          next.addClass('hover');
          $(window).scrollTop(next.position().top - 40);
          currentMessage = next;
        }
      }

      if (currentMessage && $.inArray(ev.keyCode, actionKeyCodes) !== -1){
        currentMessage.find(keyCodeToClassName[ev.keyCode]).click();
      }
    }
  });

  /* User functionality */
  userInfo.on('click', function(ev) {
    var self = $(ev.target);

    switch (true) {
      case self.hasClass('followers'):
        appnet.showFollowers();
        body.addClass('fixed');
        break;

      case self.hasClass('following'):
        appnet.showFollowing();
        body.addClass('fixed');
        break;

      case self.hasClass('follow'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.text('Follow');
          appnet.unfollow(self.parent().data('userid'), self.parent().data('username'), csrf);

        } else {
          self.addClass('on');
          self.text('Unfollow');
          appnet.follow(self.parent().data('userid'), self.parent().data('username'), csrf);
        }
        break;

      case self.hasClass('mute'):
        if (self.hasClass('on')) {
          self.removeClass('on');
          self.text('Mute');
          appnet.unmute(self.parent().data('userid'), self.parent().data('username'), csrf);

        } else {
          self.addClass('on');
          self.text('Unmute');
          appnet.mute(self.parent().data('userid'), self.parent().data('username'), csrf);
        }
        break;
    }
  });

  checkUrl();

  $(window).bind('hashchange', function() {
    checkUrl();
  });

  /* Notifications */
  notifications.on('click', '.notification-item', function(ev) {
    ev.preventDefault();
    var self = $(this);

    notifications.slideUp();
    appnet.showPost(self.data('postid'));
    body.addClass('fixed');
  });

  /* Write functionality */

  checkCharLimit(write.find('textarea').val());

  write.find('textarea').focus(function() {
    var self = $(this);
    self.addClass('on');
    charLimit.addClass('on');
    checkCharLimit(self.val());
    self.closest('form').find('.form-action-wrapper').slideDown('fast');
  });

  write.find('textarea').blur(function() {
    var self = $(this);

    charLimit.removeClass('on');

    if (self.val().replace(/\s/, '').length < 1) {
      self.removeClass('on');
      self.closest('form').find('.form-action-wrapper').slideUp('fast');
    }
  });

  // Clear the reply_to id if this is empty
  write.find('textarea').keyup(function(evt) {
    var self = $(this);
    checkCharLimit(self.val());
    friends.getBFFs(self, self.getCursorPosition());
    if (self.val().trim().length === 0) {
      write.find('.reply_to').val('');
    }

  })

  write.submit(function(ev) {
    ev.preventDefault();

    var self = $(this);
    appnet.postMessage(self);
    url = '/my/feed';
    self.find('textarea').val('');
    charLimit.text(CHAR_MAX);
    write.find('.reply_to').val('');
    return false;
  });

  suggestions.on('click', 'li', function() {
    var self = $(this);
    var textarea = self.closest('form').find('textarea');
    friends.setUser(self, textarea, textarea.getCursorPosition());
  });
});
<|endoftext|>