<commit_before>(function() {

var GameComponent = {};
GameComponent.controller = function () {
  return {
    game: new Game({
      ai: false,
      grid: new Grid({
        columnCount: 7,
        rowCount: 6,
        chipSize: 50,
        chipMargin: 6
      })
    })
  };
};
GameComponent.view = function (ctrl) {
  return m(GridComponent, ctrl.game);
};

function Game(args) {
  this.grid = args.grid;
  this.players = [
    // Player 1 (a human)
    new Player({color: 'red'}),
    // Player 2 (another human or the AI)
    new Player({color: 'blue'})
  ];
  // The current player is null when a game is not in progress
  this.currentPlayer = null;
}

function Grid(args) {
  this.columnCount = args.columnCount;
  this.rowCount = args.rowCount;
  this.chipSize = args.chipSize;
  this.chipMargin = args.chipMargin;
  // The columns array where columns containing placed chips are stored
  this.columns = [];
  this.resetGrid();
}
// Reset the grid by removing all placed chips
Grid.prototype.resetGrid = function () {
  this.columns.length = 0;
  for (var c = 0; c < this.columnCount; c += 1) {
    this.columns.push([]);
  }
};

function Chip(args) {
  this.player = args.player;
}

function Player(args) {
  this.color = args.color;
}

var GridComponent = {};
GridComponent.view = function (ctrl, game) {
  var grid = game.grid;
  return m('div', {id: 'grid'}, [
    // Bottom grid of chip placeholders (indicating space chips can occupy)
    m('div', {id: 'chip-placeholders'}, _.times(grid.columnCount, function (c) {
      return m('div', {class: 'grid-column'}, _.times(grid.rowCount, function (r) {
        return m('div', {class: 'chip-placeholder'});
      }));
    })),
    // Top grid of placed chips
    m('div', {id: 'chips'}, _.times(grid.columnCount, function (c) {
      return m('div', {class: 'grid-column'}, _.map(grid.columns[c], function (chip, r) {
        return m('div', {class: ['chip', chip.player.color].join(' ')});
      }));
    }))
  ]);
};

m.mount(document.getElementById('game'), GameComponent);

}());
<commit_msg>Remove obsolete chipSize and chipMargin properties<commit_after>(function() {

var GameComponent = {};
GameComponent.controller = function () {
  return {
    game: new Game({
      ai: false,
      grid: new Grid({
        columnCount: 7,
        rowCount: 6
      })
    })
  };
};
GameComponent.view = function (ctrl) {
  return m(GridComponent, ctrl.game);
};

function Game(args) {
  this.grid = args.grid;
  this.players = [
    // Player 1 (a human)
    new Player({color: 'red'}),
    // Player 2 (another human or the AI)
    new Player({color: 'blue'})
  ];
  // The current player is null when a game is not in progress
  this.currentPlayer = null;
}

function Grid(args) {
  this.columnCount = args.columnCount;
  this.rowCount = args.rowCount;
  // The columns array where columns containing placed chips are stored
  this.columns = [];
  this.resetGrid();
}
// Reset the grid by removing all placed chips
Grid.prototype.resetGrid = function () {
  this.columns.length = 0;
  for (var c = 0; c < this.columnCount; c += 1) {
    this.columns.push([]);
  }
};

function Chip(args) {
  this.player = args.player;
}

function Player(args) {
  this.color = args.color;
}

var GridComponent = {};
GridComponent.view = function (ctrl, game) {
  var grid = game.grid;
  return m('div', {id: 'grid'}, [
    // Bottom grid of chip placeholders (indicating space chips can occupy)
    m('div', {id: 'chip-placeholders'}, _.times(grid.columnCount, function (c) {
      return m('div', {class: 'grid-column'}, _.times(grid.rowCount, function (r) {
        return m('div', {class: 'chip-placeholder'});
      }));
    })),
    // Top grid of placed chips
    m('div', {id: 'chips'}, _.times(grid.columnCount, function (c) {
      return m('div', {class: 'grid-column'}, _.map(grid.columns[c], function (chip, r) {
        return m('div', {class: ['chip', chip.player.color].join(' ')});
      }));
    }))
  ]);
};

m.mount(document.getElementById('game'), GameComponent);

}());
<|endoftext|>


<commit_before>jQuery(function() {
    jQuery('[data-star-rating]').each(function(index, element) {
        element = $(element);

        var data = element.data('star-rating'),
            globalOptions = window.StarRatingsOptions ? window.StarRatingsOptions : {},
            options = jQuery.extend(data.options, globalOptions, {
                callback: function(currentRating, element) {
                    $.post(data.uri, { id: data.id, rating: currentRating })
                     .done(function() {
                        console.log('success');
                     })
                     .fail(function() {
                        console.log('fail');
                     });
                }
            });

        if (options.readOnly) {
            element.addClass('disabled');
        }
        element.starRating(options);
    });
});
<commit_msg>Disable voting if already voted<commit_after>jQuery(function() {
    jQuery('[data-star-rating]').each(function(index, element) {
        element = $(element);

        var data = element.data('star-rating'),
            globalOptions = window.StarRatingsOptions ? window.StarRatingsOptions : {},
            options = jQuery.extend(data.options, globalOptions, {
                callback: function(currentRating, element) {
                    $.post(data.uri, { id: data.id, rating: currentRating })
                     .done(function() {
                        console.log('success');
                     })
                     .fail(function() {
                        console.log('fail');
                     });
                }
            });

        if (element.data('voted')) {
            options.readOnly = true;
        }

        if (options.readOnly) {
            element.addClass('disabled');
        }

        element.starRating(options);
    });
});
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicity call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
        document.addEventListener("menubutton", menuKeyDown, false);
        document.addEventListener("backbutton", backKeyDown, false);
    },

    // Update DOM on a Received Event
    receivedEvent: function(id) {
        //var parentElement = document.getElementById(id);
        //var listeningElement = parentElement.querySelector('.listening');
        //var receivedElement = parentElement.querySelector('.received');

        //listeningElement.setAttribute('style', 'display:none;');
        //receivedElement.setAttribute('style', 'display:block;');
        //console.log('Received Event: ' + id);
    }
};

function menuKeyDown() {
	if ($('#menu').is(':visible')) { // hide menu if it is showing
		$('#menu').slideUp('fast');
	}
	else { // show menu
		$('#menu').slideDown('fast');
	}
	return false;
}

function backKeyDown() {
	var exit = true;
	if ($('#menu').is(':visible')) {
		$('#menu').hide();
		exit = false;
	}
	if ($('#aboutDialog').is(':visible')) {
		window.history.back();
		exit = false;
	}
	if (!exit) {
		return false; // back button was used to exit a menu, don't exit app
	}

	return exitApp();
}

function exitApp() {
	navigator.app.exitApp();
	return true;
}

var MILLISECONDS_WAIT = 5000;
var NUM_ENTRIES_PER_COLUMN = 4;
var feedList = [{name: 'Dear Prudence', url: 'http://www.slate.com/articles/life/dear_prudence.fulltext.all.10.rss', image: 'prudie.jpg'},
                {name: 'Ask Amy', url: 'http://amydickinson.com/rss', image: 'amy_dickenson.jpg', filter: {category: 'Ask Amy'}},
	            {name: 'Carolyn Hax', url: 'http://feeds.washingtonpost.com/rss/linksets/lifestyle/carolyn-hax', image: 'hax.jpg'},
	            {name: 'Savage Love', url: 'http://www.thestranger.com/gyrobase/Rss.xml?category=258', image: 'savage.jpg'},
	            {name: "Annie's Mailbox", url: 'http://www.creators.com/advice/annies-mailbox.rss', image: 'annies.jpg'},
	            {name: 'Dear Margo', url: 'http://www.creators.com/advice/dear-margo.rss', image: 'margo.jpg'},
	            {name: 'Miss Information', url: 'http://www.nerve.com/taxonomy/term/95215/all/feed', image: 'miss_information.jpg'},
	            {name: 'Questionable Advice', url: 'http://www.creators.com/advice/questionable-advice.rss', image: 'jessica_leigh.jpg'},
	            {name: 'The Advice Goddess', url: 'http://www.creators.com/advice/advice-goddess-amy-alkon.rss', image: 'amy_alkon.jpg'},
	            {name: 'Tales From The Front', url: 'http://www.creators.com/advice/tales-from-the-front.rss', image: 'cheryl_lavin.jpg'},
	            {name: 'At Work', url: 'http://www.creators.com/advice/at-work-lindsey-novak.rss', image: 'lindsey_novak.jpg'},
	            {name: 'The Awl', url: 'http://feeds.feedburner.com/TheAwl?format=xml', image: 'the_awl.png', filter: {category: 'Advice'}}];

function displayAbout() {
	$('#menu').hide();
	$.mobile.changePage("#aboutDialog");
	return false;
}

function refreshColumns() {
	$('#menu').hide();
	loadColumns();
	$('html, body').animate({ scrollTop: 0 }, 'fast');
	return false;
}

function loadColumns() {
	$('#loadingImage').show('slow');
	for (i = 0; i < feedList.length; i++) {
		displayColumnEntries(feedList[i]['name'], feedList[i]['url'], feedList[i]['image'], feedList[i]['filter']);
	}
	window.setTimeout('cleanupOldColumnEntries()', MILLISECONDS_WAIT + 1500);
}

function loadAboutColumnNames() {
	$('#aboutList').append('
Columns: ' + buildColumnNames() + '
');
}

function cleanupOldColumnEntries() {
	var maxEntries = feedList.length * NUM_ENTRIES_PER_COLUMN;
	//console.log('maxEntries: ', maxEntries);
	$("#adviceList li").each(function( index ) {
		  //console.log(index, ": ", $(this).text());
		  if (index > maxEntries) {
			  //console.log('removing ', index);
			  $(this).remove();
		  }
	});
}

function displayColumnEntries(name, url, image, filter) {
	$.jGFeed(url, function(feeds) {
	    // Check for errors
	    if (!feeds || feeds === undefined) {
	    	alert('Sorry - there has been an error receiving the advice column feeds.');
	    	return false;
	    }

		for (var j=0; j<feeds.entries.length; j++) {
	        var entry = feeds.entries[j];
	        if (shouldIncludeEntry(entry, filter)) {
		        if ($('#adviceList li[data-entry-id="' + entry.link + '"]').length === 0) {
		        	$('#adviceList').append(buildEntryString(name, entry, image));
		        }
	        }
		}

		// sort 1 time(s), 5 seconds total wait
		window.setTimeout('sortElements(' + 1 + ')', MILLISECONDS_WAIT);
	}, // if there is a filter, increase number of entries to examine, since most entries may not qualify
	filter === undefined ? NUM_ENTRIES_PER_COLUMN : NUM_ENTRIES_PER_COLUMN * 5);
}

function shouldIncludeEntry(entry, filter) {
	if (filter === undefined) {
		return true; // no filter - include entry by default
	}

	if (filter['category'] !== undefined) { // filtering is by category
		if (entry.categories && entry.categories.length > 0) {
			for (i = 0; i < entry.categories.length; i++) {
				//console.log('for entry', entry.title, 'checking filter', filter.category, 'vs:', entry.categories[i]);
				if (filter.category === entry.categories[i]) {
					//console.log('Found it - returning true');
					return true;
				}
			}
		}
	}

	// there was a filter, but we couldn't match it
	//console.log('Did not find a category match - returning false');
	return false;
}

function sortElements(marker) {
	$('#adviceList li').sort(function(a, b) {
	    return a.dataset.timestamp > b.dataset.timestamp ? -1 : 1;
	}).appendTo('#adviceList');

	if (--marker < 1) {
		$('#loadingImage').fadeOut('slow');
		return;
	}
	window.setTimeout('sortElements(' + marker + ')', MILLISECONDS_WAIT);
}

function buildEntryString(name, entry, image) {
	var dateArr = entry.publishedDate.split(' ');
	var title = fixTitle(name, entry.title);

	return '
'
	  + '
' + dateArr[0] + ' ' + dateArr[2] + ' ' + dateArr[1] + ', ' + dateArr[3] + '
'
	  + '
' + title + '
'
	  + '
' + (typeof image !== 'undefined' ? '' : '')
	  + fixContent(entry.content) + '
'
	  + '
Read
'
	  + '
';
}

function buildColumnNames() {
	var html = '';
	for (i = 0; i < feedList.length; i++) {
		html += feedList[i].name;
		if (i < feedList.length - 1) {
			html += ', ';
		}
	}
	return html;
}

function fixTitle(name, title) {
	// remove the "for MM/DD/YYY" from the end of creator.com titles
	var titleMatch = title.match(/.*(\sfor\s\d\d\/\d\d\/\d\d\d\d)$/);
	if (titleMatch) { // check if the input string matched the pattern
	     title = title.substring(0, title.search(titleMatch[1])); // get the capturing group
	}

	if (name !== 'The Awl' && title.search(name) < 0) {
		title = name + ': ' + title;
	}

	return title;
}

function fixContent(content) {
	// replace "a href" links with in app browser links
	var regex = new RegExp("()", 'g');
	if (content.match(regex)) {
	    content = content.replace(regex, "$1" + '"javascript:void(0);" onclick="openLink(\'' + "$2" + '\');"' + "$3");
	}
	return content;
}

function openLink(link) {
	navigator.app.loadUrl(link, { openExternal: true });
	//window.plugins.childBrowser.showWebPage(link, { showLocationBar: true });
}<commit_msg>accurately counting and enforcing max number of entries per column<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicity call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
        document.addEventListener("menubutton", menuKeyDown, false);
        document.addEventListener("backbutton", backKeyDown, false);
    },

    // Update DOM on a Received Event
    receivedEvent: function(id) {
        //var parentElement = document.getElementById(id);
        //var listeningElement = parentElement.querySelector('.listening');
        //var receivedElement = parentElement.querySelector('.received');

        //listeningElement.setAttribute('style', 'display:none;');
        //receivedElement.setAttribute('style', 'display:block;');
        //console.log('Received Event: ' + id);
    }
};

function menuKeyDown() {
	if ($('#menu').is(':visible')) { // hide menu if it is showing
		$('#menu').slideUp('fast');
	}
	else { // show menu
		$('#menu').slideDown('fast');
	}
	return false;
}

function backKeyDown() {
	var exit = true;
	if ($('#menu').is(':visible')) {
		$('#menu').hide();
		exit = false;
	}
	if ($('#aboutDialog').is(':visible')) {
		window.history.back();
		exit = false;
	}
	if (!exit) {
		return false; // back button was used to exit a menu, don't exit app
	}

	return exitApp();
}

function exitApp() {
	navigator.app.exitApp();
	return true;
}

var MILLISECONDS_WAIT = 5000;
var NUM_ENTRIES_PER_COLUMN = 3;
var feedList = [{name: 'Dear Prudence', url: 'http://www.slate.com/articles/life/dear_prudence.fulltext.all.10.rss', image: 'prudie.jpg'},
                {name: 'Ask Amy', url: 'http://amydickinson.com/rss', image: 'amy_dickenson.jpg', filter: {category: 'Ask Amy'}},
	            {name: 'Carolyn Hax', url: 'http://feeds.washingtonpost.com/rss/linksets/lifestyle/carolyn-hax', image: 'hax.jpg'},
	            {name: 'Savage Love', url: 'http://www.thestranger.com/gyrobase/Rss.xml?category=258', image: 'savage.jpg'},
	            {name: "Annie's Mailbox", url: 'http://www.creators.com/advice/annies-mailbox.rss', image: 'annies.jpg'},
	            {name: 'Dear Margo', url: 'http://www.creators.com/advice/dear-margo.rss', image: 'margo.jpg'},
	            {name: 'Miss Information', url: 'http://www.nerve.com/taxonomy/term/95215/all/feed', image: 'miss_information.jpg'},
	            {name: 'Questionable Advice', url: 'http://www.creators.com/advice/questionable-advice.rss', image: 'jessica_leigh.jpg'},
	            {name: 'The Advice Goddess', url: 'http://www.creators.com/advice/advice-goddess-amy-alkon.rss', image: 'amy_alkon.jpg'},
	            {name: 'Tales From The Front', url: 'http://www.creators.com/advice/tales-from-the-front.rss', image: 'cheryl_lavin.jpg'},
	            {name: 'At Work', url: 'http://www.creators.com/advice/at-work-lindsey-novak.rss', image: 'lindsey_novak.jpg'},
	            {name: 'The Awl', url: 'http://feeds.feedburner.com/TheAwl?format=xml', image: 'the_awl.png', filter: {category: 'Advice'}}];

function displayAbout() {
	$('#menu').hide();
	$.mobile.changePage("#aboutDialog");
	return false;
}

function refreshColumns() {
	$('#menu').hide();
	loadColumns();
	$('html, body').animate({ scrollTop: 0 }, 'fast');
	return false;
}

function loadColumns() {
	$('#loadingImage').show('slow');
	for (i = 0; i < feedList.length; i++) {
		displayColumnEntries(feedList[i]['name'], feedList[i]['url'], feedList[i]['image'], feedList[i]['filter']);
	}
	window.setTimeout('cleanupOldColumnEntries()', MILLISECONDS_WAIT + 1500);
}

function loadAboutColumnNames() {
	$('#aboutList').append('
Columns: ' + buildColumnNames() + '
');
}

function cleanupOldColumnEntries() {
	var maxEntries = feedList.length * NUM_ENTRIES_PER_COLUMN;
	//console.log('maxEntries: ', maxEntries);
	$("#adviceList li").each(function( index ) {
		  //console.log(index, ": ", $(this).text());
		  if (index > maxEntries) {
			  //console.log('removing ', index);
			  $(this).remove();
		  }
	});
}

function displayColumnEntries(name, url, image, filter) {
	$.jGFeed(url, function(feeds) {
	    // Check for errors
	    if (!feeds || feeds === undefined) {
	    	alert('Sorry - there has been an error receiving the advice column feeds.');
	    	return false;
	    }

	    var numEntries = 0; // because of filters, we need to keep track of how many entries actually included
		for (var j=0; j < feeds.entries.length && numEntries < NUM_ENTRIES_PER_COLUMN; j++) {
	        var entry = feeds.entries[j];
	        if (shouldIncludeEntry(entry, filter)) {
		        if ($('#adviceList li[data-entry-id="' + entry.link + '"]').length === 0) {
		        	$('#adviceList').append(buildEntryString(name, entry, image));
		        	numEntries++;
		        }
	        }
		}

		// sort 1 time(s), 5 seconds total wait
		window.setTimeout('sortElements(' + 1 + ')', MILLISECONDS_WAIT);
	}, // if there is a filter, increase number of entries to examine, since most entries may not qualify
	filter === undefined ? NUM_ENTRIES_PER_COLUMN : NUM_ENTRIES_PER_COLUMN * 10);
}

function shouldIncludeEntry(entry, filter) {
	if (filter === undefined) {
		return true; // no filter - include entry by default
	}

	if (filter['category'] !== undefined) { // filtering is by category
		if (entry.categories && entry.categories.length > 0) {
			for (i = 0; i < entry.categories.length; i++) {
				//console.log('for entry', entry.title, 'checking filter', filter.category, 'vs:', entry.categories[i]);
				if (filter.category === entry.categories[i]) {
					//console.log('Found it - returning true');
					return true;
				}
			}
		}
	}

	// there was a filter, but we couldn't match it
	//console.log('Did not find a category match - returning false');
	return false;
}

function sortElements(marker) {
	$('#adviceList li').sort(function(a, b) {
	    return a.dataset.timestamp > b.dataset.timestamp ? -1 : 1;
	}).appendTo('#adviceList');

	if (--marker < 1) {
		$('#loadingImage').fadeOut('slow');
		return;
	}
	window.setTimeout('sortElements(' + marker + ')', MILLISECONDS_WAIT);
}

function buildEntryString(name, entry, image) {
	var dateArr = entry.publishedDate.split(' ');
	var title = fixTitle(name, entry.title);

	return '
'
	  + '
' + dateArr[0] + ' ' + dateArr[2] + ' ' + dateArr[1] + ', ' + dateArr[3] + '
'
	  + '
' + title + '
'
	  + '
' + (typeof image !== 'undefined' ? '' : '')
	  + fixContent(entry.content) + '
'
	  + '
Read
'
	  + '
';
}

function buildColumnNames() {
	var html = '';
	for (i = 0; i < feedList.length; i++) {
		html += feedList[i].name;
		if (i < feedList.length - 1) {
			html += ', ';
		}
	}
	return html;
}

function fixTitle(name, title) {
	// remove the "for MM/DD/YYY" from the end of creator.com titles
	var titleMatch = title.match(/.*(\sfor\s\d\d\/\d\d\/\d\d\d\d)$/);
	if (titleMatch) { // check if the input string matched the pattern
	     title = title.substring(0, title.search(titleMatch[1])); // get the capturing group
	}

	if (name !== 'The Awl' && title.search(name) < 0) {
		title = name + ': ' + title;
	}

	return title;
}

function fixContent(content) {
	// replace "a href" links with in app browser links
	var regex = new RegExp("()", 'g');
	if (content.match(regex)) {
	    content = content.replace(regex, "$1" + '"javascript:void(0);" onclick="openLink(\'' + "$2" + '\');"' + "$3");
	}
	return content;
}

function openLink(link) {
	navigator.app.loadUrl(link, { openExternal: true });
	//window.plugins.childBrowser.showWebPage(link, { showLocationBar: true });
}<|endoftext|>


<commit_before>import Endpoint from './endpoint';

/**
 * Creates an endpoint hash.
 * The hash is a combination of the url and the method parameters. Guarantees uniqueness
 * for distinct inputs.
 *
 * @param  {String} url    The url to hash.
 * @param  {String} method The method to hash.
 * @return {String}        Returned unique hash.
 */
function createEndpointHash(url, method) {
  return `url:${url}|method:${method}`;
}

/**
 * This class represents a fake server. The server serves endpoints registered with it.
 *
 * @class PseudoServer
 */
export default class Server {

  /**
   * Constructor.
   *
   * Takes a config-parameter which is the config of the server.
   *
   * @param  {Object} config A url-config associated with this pseudo-server.
   */
  constructor(config) {
    this.config = config;
    this.paths = {};
  }

  /**
   * Registers a new route with the server. The method simply creates a new
   * PseudoEndpoint-instance, and adds it to the paths-dict of this server.
   *
   * @param  {String} url     The url of the endpoint.
   * @param  {String} method  The method it accepts requests to. Typically GET/POST etc.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  route(url, method) {
    const endpoint = new PseudoEndpoint(url, method);
    const hash = createEndpointHash(url, method);
    if (hash in this.paths) {
      console.warn(`Overwriting ${hash}`);
    }

    this.paths[createEndpointHash(url, method)] = endpoint;
    return endpoint;
  }

  /**
   * Alias for route(url, 'GET').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  get(url) {
    return route(url, 'GET');
  }

  /**
   * Alias for route(url, 'POSt').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  post(url) {
    return route(url, 'POST');
  }

  /**
   * Alias for route(url, 'PATCH').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  patch(url) {
    return route(url, 'PATCH');
  }

  /**
   * Alias for route(url, 'PUT').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  put(url) {
    return route(url, 'PUT');
  }

  /**
   * Alias for route(url, 'DELETE').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  delete(url) {
    return route(url, 'DELETE');
  }

  /**
   * Alias for route(url, 'OPTIONS').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  options(url) {
    return route(url, 'OPTIONS');
  }

  /**
   * Alias for route(url, 'HEAD').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  head(url) {
    return route(url, 'HEAD');
  }
}
<commit_msg>Implemented the missing _call method in server<commit_after>import Endpoint from './endpoint';

/**
 * Creates an endpoint hash.
 * The hash is a combination of the url and the method parameters. Guarantees uniqueness
 * for distinct inputs.
 *
 * @param  {String} url    The url to hash.
 * @param  {String} method The method to hash.
 * @return {String}        Returned unique hash.
 */
function createEndpointHash(url, method) {
  return `url:${url}|method:${method}`;
}

/**
 * This class represents a fake server. The server serves endpoints registered with it.
 *
 * @class PseudoServer
 */
export default class Server {

  /**
   * Constructor.
   *
   * Takes a config-parameter which is the config of the server.
   *
   * @param  {Object} config A url-config associated with this pseudo-server.
   */
  constructor(config) {
    this.config = config;
    this.paths = {};
  }

  /**
   * Registers a new route with the server. The method simply creates a new
   * PseudoEndpoint-instance, and adds it to the paths-dict of this server.
   *
   * @param  {String} url     The url of the endpoint.
   * @param  {String} method  The method it accepts requests to. Typically GET/POST etc.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  route(url, method) {
    const endpoint = new PseudoEndpoint(url, method);
    const hash = createEndpointHash(url, method);
    if (hash in this.paths) {
      console.warn(`Overwriting ${hash}`);
    }

    this.paths[createEndpointHash(url, method)] = endpoint;
    return endpoint;
  }

  /**
   * Alias for route(url, 'GET').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  get(url) {
    return route(url, 'GET');
  }

  /**
   * Alias for route(url, 'POSt').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  post(url) {
    return route(url, 'POST');
  }

  /**
   * Alias for route(url, 'PATCH').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  patch(url) {
    return route(url, 'PATCH');
  }

  /**
   * Alias for route(url, 'PUT').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  put(url) {
    return route(url, 'PUT');
  }

  /**
   * Alias for route(url, 'DELETE').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  delete(url) {
    return route(url, 'DELETE');
  }

  /**
   * Alias for route(url, 'OPTIONS').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  options(url) {
    return route(url, 'OPTIONS');
  }

  /**
   * Alias for route(url, 'HEAD').
   *
   * @param  {String} url     The url of the endpoint.
   * @return {PseudoEndpoint} The resulting endpoint.
   */
  head(url) {
  _call(url='', config={}) {
    const endpointHash = createEndpointHash(url, config.method || 'GET');
    const endpoint = this.endpoints[endpointHash];
    if (!endpoint) {
      return new Promise((resolve, reject) => {
        reject(new Response({status: 404}));
      });
    }
    return endpoint._call(url, config);
  }
}
<|endoftext|>


<commit_before>import $$observable from 'symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * @param {Function} [enhancer] The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */
export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   *
   * @returns {any} The current state tree of your application.
   */
  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

  /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See http://redux.js.org/docs/api/Store.html#subscribe for more details.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See http://redux.js.org/docs/api/Store.html#subscribe for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param {Object} action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns {Object} For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  /**
   * Replaces the reducer currently used by the store to calculate the state.
   *
   * You might need this if your app implements code splitting and you want to
   * load some of the reducers dynamically. You might also need this if you
   * implement a hot reloading mechanism for Redux.
   *
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  /**
   * Interoperability point for observable/reactive libraries.
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/tc39/proposal-observable
   */
  function observable() {
    const outerSubscribe = subscribe
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
<commit_msg>Updated deprecated link for store subscribe listener (#2932)<commit_after>import $$observable from 'symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * @param {Function} [enhancer] The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */
export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   *
   * @returns {any} The current state tree of your application.
   */
  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

  /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param {Object} action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns {Object} For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  /**
   * Replaces the reducer currently used by the store to calculate the state.
   *
   * You might need this if your app implements code splitting and you want to
   * load some of the reducers dynamically. You might also need this if you
   * implement a hot reloading mechanism for Redux.
   *
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  /**
   * Interoperability point for observable/reactive libraries.
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/tc39/proposal-observable
   */
  function observable() {
    const outerSubscribe = subscribe
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
<|endoftext|>


<commit_before>var App = Ember.Application.create({
    LOG_TRANSITIONS: true
});

App.Router.map(function() {
    //this.resource('layers');
    this.resource('createMap');
    this.resource('projects', function() {
        this.resource('project', { path: '/:project_id' });
    });

});

// Controllers

App.CreateMapController = Ember.ArrayController.extend({
    sortProperties: ['layer_type', 'name'],

    actions : {

        createProject: function() {
            var project = this.store.createRecord('project', {
                name: (new Date()).toTimeString(),
                status: 'UNSAVED'
            });
            project.save()
        }
    }

});

App.ProjectController = Ember.Controller.extend({
    showLayers: function() {
        return this.get('model.layer_ids')

    }.property('model.layer_ids.@each'),

    getLayers: function(){
        loaded_layers = this.get("model.layer_ids").content.content.length;
        var i = this.incrementProperty('i'),
            c = loadCount.get("count");

        if(i !== c ){
          this.get("model").reload();
        }

        loadCount.set("count",i);

    }.property("model"),

    actions: {
      reload: function() {
        this.get('model').reload().then(function(model) {
          // do something with the reloaded model
          console.log(model.layer_ids);
        });
      }
    }
});

App.AddLayerModalController = Ember.ArrayController.extend({
   sortProperties: ['layer_type', 'name'],
});

// Routers

App.IndexRoute = Ember.Route.extend({});

App.AddLayerModalRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('layer');
    },
});

App.ProjectsRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('project');
    }
})

App.ProjectRoute = Ember.Route.extend({
    actions: {
        addLayer: function(layer, model) {
            var layerID = layer.get('id');
            var _this = this;
            var projectID = _this.get('controller.model.id');
            // console.log(projectID, layerID)

            var projectlayer = this.store.createRecord('projectlayer', {
                project_id: projectID,
                layer_id: layerID
            });
            projectlayer.save().then(function(){
              _this.get("controller.model").reload();
            });
        },

        removeLayer: function(layer, model) {
            var layerID = layer.get('id');
            var _this = this;
            var projectID = _this.get('controller.model.id');
            console.log(projectID, layerID)
            var projectLayer = this.store.find('projectlayer', { layer_id: layerID, project_id: projectID });
            var projectLayer = DS.PromiseObject.create({
                promise: this.store.find('projectlayer', { layer_id: layerID, project_id: projectID })
            });

            projectLayer.then(function() {
                var projectLayerID = projectLayer.get('content.content.0.id');
                console.log(projectLayerID)

                App.Projectlayer.store.find('projectlayer', projectLayerID).then(function(projectlayer){
                    projectlayer.destroyRecord().then(function(){
                        _this.get("controller.model").reload();
                    });
                });

            });

            //this.store.find('projectlayer', { layer_id: layerID, project_id: projectID }).then(function (projectlayer) {
            //    projectlayer.destroyRecord().then(function(){
            //        _this.get("controller.model").reload();
            //    });
            //});
        },

        // Modal
        showModal: function(name) {
            var content = this.store.find('layer');
            this.controllerFor(name).set('content', content);
            this.render(name, {
                into: 'application',
                outlet: 'modal'
            });
        },
        removeModal: function() {
            this.disconnectOutlet({
                outlet: 'modal',
                parentView: 'application'
            });
        }
    },

    didInsertElement: function() {
        $('#ex1').slider({
            formatter: function(value) {
                return 'Current value: ' + value;
            }
        });
    }

});

var loadCount = Ember.Object.create({
  count: 0
});

App.CreateMapRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('layer');
    },

    actions: {
        addLayer: function(layer) {
            var slug = layer.get('layer');
            if ($("."+slug).length!==1){
                var map = store.get('map');
                var tile = L.tileLayer('http://static.library.gsu.edu/ATLmaps/tiles/' + layer.get('layer') + '/{z}/{x}/{y}.png', {
                    layer: layer.get('layer'),
                    tms: true,
                    minZoom: 13,
                    maxZoom: 19,
                    //attribution: 'GSU'
                }).addTo(map).getContainer();

                $(tile).addClass(slug);
            }
            //else{
            //    $("."+slug).fadeOut( 500, function() {
            //        $(this).remove();
            //    });
            //}

        },

        // Modal
        showModal: function(name, content) {
            console.log(content)
            this.controllerFor(name).set('content', content);
            this.render(name, {
                into: 'application',
                outlet: 'modal'
            });
        },
        removeModal: function() {
            this.disconnectOutlet({
                outlet: 'modal',
                parentView: 'application'
            });
        }

    }
});

App.Map = Ember.Object.extend();
var store = App.Map.create();

// Views

// Components

App.BaseMapComponent = Ember.Component.extend({
    didInsertElement: function() {
        var map = L.map('map', {zoomControl:false}).setView([33.7489954,-84.3879824], 14);
        L.control.zoom({ position: 'topright' }).addTo(map);
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '© OpenStreetMap contributors',
        }).addTo(map);

        store.set('map', map);
        // save map instance
        this.controller.set('map', map);
    },
});

App.OpacitySliderComponent = Ember.Component.extend({
    opacityslider: function() {

                var layer = DS.PromiseObject.create({
                    promise: App.Layer.store.find('layer', this.layerID)
                });

                layer.then(function() {

                    var layerName = layer.get('layer');
                    // console.log(layerName)
                    var slider = $("input.slider, input ."+layerName).slider({
                                //precision: 2,
                                value: 10,
                              });

                });
    }.property(),

    actions: {
        opacityChange: function() {
            // console.log(this.layer);
            var layerName = this.layer
            value = $("input."+layerName).val();
            var opacity = value / 10;
            // console.log(opacity);
            $("div."+layerName+",img."+layerName).css({'opacity': opacity});
        }
    }
});

App.AddRemoveLayerButtonComponent = Ember.Component.extend({
    actions: {
        buttonToggle: function() {
            this.toggleProperty('layerAdded');
            console.log(this.get('param'));
            this.sendAction('action', this.get('param'));
        },
    }
});

App.RemoveLayerButtonComponent = Ember.Component.extend({
    actions: {
        removeLayer: function() {
            console.log('hi');
            this.sendAction('action', this.get('param'));
        }
    }
});

App.ListAddedLayersComponent = Ember.Component.extend({

    model: function() {
        return this.store.find('project');
    },

});

App.MyModalComponent = Ember.Component.extend({
    actions: {
        ok: function() {
            this.$('.modal').modal('hide');
            this.sendAction('ok');
        }
    },
    show: function() {
        this.$('.modal').modal().on('hidden.bs.modal', function() {
            this.sendAction('close');
        }.bind(this));
    }.on('didInsertElement')
});

App.MapLayersComponent = Ember.Component.extend({
    mappedLayers: function() {

        var mappedLayer = DS.PromiseObject.create({
            promise: App.Layer.store.find('layer', this.layerID)
        });

        mappedLayer.then(function() {

            var slug = mappedLayer.get('layer');
            var map = store.get('map');

            switch(mappedLayer.get('layer_type')) {
                case 'planningatlanta':
                    if ($("."+slug).length!==1){
                        var tile = L.tileLayer('http://static.library.gsu.edu/ATLmaps/tiles/' + mappedLayer.get('layer') + '/{z}/{x}/{y}.png', {
                            layer: mappedLayer.get('layer'),
                            tms: true,
                            minZoom: 13,
                            maxZoom: 19,
                            //attribution: 'GSU'
                        }).addTo(map).getContainer();

                        $(tile).addClass(slug);
                    }
                    else{
                        $("."+slug).fadeOut( 500, function() {
                            $(this).remove();
                        });
                    }
                    break;

                case 'geojson':
                    var slug = mappedLayer.get('layer')
                    function viewData(feature, layer) {
                        var popupContent = "
"+feature.properties.name+"
";
                        layer.bindPopup(popupContent);
                    }
                    function setIcon(url, class_name){
                      return iconObj = L.icon({
                                            iconUrl: url,
                                            //iconSize: [50, 65],
                                            iconAnchor: [16, 37],
                                            popupAnchor: [0, -28],
                                            className: class_name
                                          });
                    }

                    if(mappedLayer.get('url')){
                      var points = new L.GeoJSON.AJAX(mappedLayer.get('url'), {
                          pointToLayer: function (feature, latlng) {
                            // console.log("slug",slug);
                            var marker = L.marker(latlng, {icon: setIcon("images/marker2.png", slug)});
                            return marker
                          },
                          onEachFeature: viewData,
                      }).addTo(map);
                      break;
                    }
            }

            shuffle.init();
        });
        //return mappedLayer
    }.property(),

    actions: {

    }
});

// Adapter

App.ApplicationAdapter = DS.RESTAdapter.extend({
    host: 'http://api.atlmaps-dev.com:7000',
    namespace: 'v1',
    suffix: '.json',
    buildURL: function(record, suffix) {
      var s = this._super(record, suffix);
      return s + this.get('suffix');
    }
});

App.Store = DS.Store.extend({
  adapter: App.ApplicationAdapter
});

// Models
App.Layer = DS.Model.extend({
    name: DS.attr('string'),
    slug: DS.attr('string'),
    keywords: DS.attr('string'),
    description: DS.attr('string'),
    url: DS.attr('string'),
    layer: DS.attr('string'),
    date: DS.attr('date'),
    layer_type: DS.attr('string'),
    zoomlevels: DS.attr('string'),
    minx: DS.attr('number'),
    miny: DS.attr('number'),
    maxx: DS.attr('number'),
    maxy: DS.attr('number'),
    project_ids: DS.hasMany('project', {async: true})
});

App.Project = DS.Model.extend({
    name: DS.attr('string'),
    description: DS.attr('string'),
    status: DS.attr('string'),
    //user: DS.attr('number'),
    layer_ids: DS.hasMany('layer', {async: true}),
})

App.Projectlayer = DS.Model.extend({
    layer_id: DS.attr(),
    project_id: DS.attr()
});

$(document).ready(function(){
  $.material.ripples(".btn, .navbar a");
  (function(){
  // init on shuffle items
    shuffle.init();
  })()

  // document events
  $(document).on('click','#hide-layer-options',function(){
    $(".layer-controls").animate({"left":"-100%"},500,"easeInQuint",function(){
      $("#show-layer-options").fadeIn(500);
    });
  })
  .on('click','#show-layer-options',function(){
    $(".layer-controls").animate({"left":"0%"},500,"easeOutQuint");
    $("#show-layer-options").fadeOut(500);
  })
  .on('click','.shuffle-items li.item',function(){
    shuffle.click(this);
  })

})
<commit_msg>Now the layer is removed from the map when removed from the porject.<commit_after>var App = Ember.Application.create({
    LOG_TRANSITIONS: true
});

App.Router.map(function() {
    //this.resource('layers');
    this.resource('createMap');
    this.resource('projects', function() {
        this.resource('project', { path: '/:project_id' });
    });

});

// Controllers

App.CreateMapController = Ember.ArrayController.extend({
    sortProperties: ['layer_type', 'name'],

    actions : {

        createProject: function() {
            var project = this.store.createRecord('project', {
                name: (new Date()).toTimeString(),
                status: 'UNSAVED'
            });
            project.save()
        }
    }

});

App.ProjectController = Ember.Controller.extend({
    showLayers: function() {
        return this.get('model.layer_ids')

    }.property('model.layer_ids.@each'),

    getLayers: function(){
        loaded_layers = this.get("model.layer_ids").content.content.length;
        var i = this.incrementProperty('i'),
            c = loadCount.get("count");

        if(i !== c ){
          this.get("model").reload();
        }

        loadCount.set("count",i);

    }.property("model"),

    actions: {
      reload: function() {
        this.get('model').reload().then(function(model) {
          // do something with the reloaded model
          console.log(model.layer_ids);
        });
      }
    }
});

App.AddLayerModalController = Ember.ArrayController.extend({
   sortProperties: ['layer_type', 'name'],
});

// Routers

App.IndexRoute = Ember.Route.extend({});

App.AddLayerModalRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('layer');
    },
});

App.ProjectsRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('project');
    }
})

App.ProjectRoute = Ember.Route.extend({
    actions: {
        addLayer: function(layer, model) {
            var layerID = layer.get('id');
            var _this = this;
            var projectID = _this.get('controller.model.id');
            // console.log(projectID, layerID)

            var projectlayer = this.store.createRecord('projectlayer', {
                project_id: projectID,
                layer_id: layerID
            });
            projectlayer.save().then(function(){
              _this.get("controller.model").reload();
            });
        },

        removeLayer: function(layer, model) {
            var layerID = layer.get('id');
            var layerClass = layer.get('layer');
            var _this = this;
            var projectID = _this.get('controller.model.id');
            console.log(projectID, layerID)
            var projectLayer = this.store.find('projectlayer', { layer_id: layerID, project_id: projectID });
            var projectLayer = DS.PromiseObject.create({
                promise: this.store.find('projectlayer', { layer_id: layerID, project_id: projectID })
            });

            projectLayer.then(function() {
                var projectLayerID = projectLayer.get('content.content.0.id');
                console.log(projectLayerID)

                App.Projectlayer.store.find('projectlayer', projectLayerID).then(function(projectlayer){
                    projectlayer.destroyRecord().then(function(){
                        _this.get("controller.model").reload();
                    });
                });

            });

             $("."+layerClass).fadeOut( 500, function() {
                            $(this).remove();
                        });

            //this.store.find('projectlayer', { layer_id: layerID, project_id: projectID }).then(function (projectlayer) {
            //    projectlayer.destroyRecord().then(function(){
            //        _this.get("controller.model").reload();
            //    });
            //});
        },

        // Modal
        showModal: function(name) {
            var content = this.store.find('layer');
            this.controllerFor(name).set('content', content);
            this.render(name, {
                into: 'application',
                outlet: 'modal'
            });
        },
        removeModal: function() {
            this.disconnectOutlet({
                outlet: 'modal',
                parentView: 'application'
            });
        }
    },

    didInsertElement: function() {
        $('#ex1').slider({
            formatter: function(value) {
                return 'Current value: ' + value;
            }
        });
    }

});

var loadCount = Ember.Object.create({
  count: 0
});

App.CreateMapRoute = Ember.Route.extend({
    model: function() {
        return this.store.find('layer');
    },

    actions: {
        addLayer: function(layer) {
            var slug = layer.get('layer');
            if ($("."+slug).length!==1){
                var map = store.get('map');
                var tile = L.tileLayer('http://static.library.gsu.edu/ATLmaps/tiles/' + layer.get('layer') + '/{z}/{x}/{y}.png', {
                    layer: layer.get('layer'),
                    tms: true,
                    minZoom: 13,
                    maxZoom: 19,
                    //attribution: 'GSU'
                }).addTo(map).getContainer();

                $(tile).addClass(slug);
            }
            //else{
            //    $("."+slug).fadeOut( 500, function() {
            //        $(this).remove();
            //    });
            //}

        },

        // Modal
        showModal: function(name, content) {
            console.log(content)
            this.controllerFor(name).set('content', content);
            this.render(name, {
                into: 'application',
                outlet: 'modal'
            });
        },
        removeModal: function() {
            this.disconnectOutlet({
                outlet: 'modal',
                parentView: 'application'
            });
        }

    }
});

App.Map = Ember.Object.extend();
var store = App.Map.create();

// Views

// Components

App.BaseMapComponent = Ember.Component.extend({
    didInsertElement: function() {
        var map = L.map('map', {zoomControl:false}).setView([33.7489954,-84.3879824], 14);
        L.control.zoom({ position: 'topright' }).addTo(map);
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '© OpenStreetMap contributors',
        }).addTo(map);

        store.set('map', map);
        // save map instance
        this.controller.set('map', map);
    },
});

App.OpacitySliderComponent = Ember.Component.extend({
    opacityslider: function() {

                var layer = DS.PromiseObject.create({
                    promise: App.Layer.store.find('layer', this.layerID)
                });

                layer.then(function() {

                    var layerName = layer.get('layer');
                    // console.log(layerName)
                    var slider = $("input.slider, input ."+layerName).slider({
                                //precision: 2,
                                value: 10,
                              });

                });
    }.property(),

    actions: {
        opacityChange: function() {
            // console.log(this.layer);
            var layerName = this.layer
            value = $("input."+layerName).val();
            var opacity = value / 10;
            // console.log(opacity);
            $("div."+layerName+",img."+layerName).css({'opacity': opacity});
        }
    }
});

App.AddRemoveLayerButtonComponent = Ember.Component.extend({
    actions: {
        buttonToggle: function() {
            this.toggleProperty('layerAdded');
            console.log(this.get('param'));
            this.sendAction('action', this.get('param'));
        },
    }
});

App.RemoveLayerButtonComponent = Ember.Component.extend({
    actions: {
        removeLayer: function() {
            console.log('hi');
            this.sendAction('action', this.get('param'));
        }
    }
});

App.ListAddedLayersComponent = Ember.Component.extend({

    model: function() {
        return this.store.find('project');
    },

});

App.MyModalComponent = Ember.Component.extend({
    actions: {
        ok: function() {
            this.$('.modal').modal('hide');
            this.sendAction('ok');
        }
    },
    show: function() {
        this.$('.modal').modal().on('hidden.bs.modal', function() {
            this.sendAction('close');
        }.bind(this));
    }.on('didInsertElement')
});

App.MapLayersComponent = Ember.Component.extend({
    mappedLayers: function() {

        var mappedLayer = DS.PromiseObject.create({
            promise: App.Layer.store.find('layer', this.layerID)
        });

        mappedLayer.then(function() {

            var slug = mappedLayer.get('layer');
            var map = store.get('map');

            switch(mappedLayer.get('layer_type')) {
                case 'planningatlanta':
                    if ($("."+slug).length!==1){
                        var tile = L.tileLayer('http://static.library.gsu.edu/ATLmaps/tiles/' + mappedLayer.get('layer') + '/{z}/{x}/{y}.png', {
                            layer: mappedLayer.get('layer'),
                            tms: true,
                            minZoom: 13,
                            maxZoom: 19,
                            //attribution: 'GSU'
                        }).addTo(map).getContainer();

                        $(tile).addClass(slug);
                    }
                    else{
                        $("."+slug).fadeOut( 500, function() {
                            $(this).remove();
                        });
                    }
                    break;

                case 'geojson':
                    var slug = mappedLayer.get('layer')
                    function viewData(feature, layer) {
                        var popupContent = "
"+feature.properties.name+"
";
                        layer.bindPopup(popupContent);
                    }
                    function setIcon(url, class_name){
                      return iconObj = L.icon({
                                            iconUrl: url,
                                            //iconSize: [50, 65],
                                            iconAnchor: [16, 37],
                                            popupAnchor: [0, -28],
                                            className: class_name
                                          });
                    }

                    if(mappedLayer.get('url')){
                      var points = new L.GeoJSON.AJAX(mappedLayer.get('url'), {
                          pointToLayer: function (feature, latlng) {
                            // console.log("slug",slug);
                            var marker = L.marker(latlng, {icon: setIcon("images/marker2.png", slug)});
                            return marker
                          },
                          onEachFeature: viewData,
                      }).addTo(map);
                      break;
                    }
            }

            shuffle.init();
        });
        //return mappedLayer
    }.property(),

    actions: {

    }
});

// Adapter

App.ApplicationAdapter = DS.RESTAdapter.extend({
    host: 'http://api.atlmaps-dev.com:7000',
    namespace: 'v1',
    suffix: '.json',
    buildURL: function(record, suffix) {
      var s = this._super(record, suffix);
      return s + this.get('suffix');
    }
});

App.Store = DS.Store.extend({
  adapter: App.ApplicationAdapter
});

// Models
App.Layer = DS.Model.extend({
    name: DS.attr('string'),
    slug: DS.attr('string'),
    keywords: DS.attr('string'),
    description: DS.attr('string'),
    url: DS.attr('string'),
    layer: DS.attr('string'),
    date: DS.attr('date'),
    layer_type: DS.attr('string'),
    zoomlevels: DS.attr('string'),
    minx: DS.attr('number'),
    miny: DS.attr('number'),
    maxx: DS.attr('number'),
    maxy: DS.attr('number'),
    project_ids: DS.hasMany('project', {async: true})
});

App.Project = DS.Model.extend({
    name: DS.attr('string'),
    description: DS.attr('string'),
    status: DS.attr('string'),
    //user: DS.attr('number'),
    layer_ids: DS.hasMany('layer', {async: true}),
})

App.Projectlayer = DS.Model.extend({
    layer_id: DS.attr(),
    project_id: DS.attr()
});

$(document).ready(function(){
  $.material.ripples(".btn, .navbar a");
  (function(){
  // init on shuffle items
    shuffle.init();
  })()

  // document events
  $(document).on('click','#hide-layer-options',function(){
    $(".layer-controls").animate({"left":"-100%"},500,"easeInQuint",function(){
      $("#show-layer-options").fadeIn(500);
    });
  })
  .on('click','#show-layer-options',function(){
    $(".layer-controls").animate({"left":"0%"},500,"easeOutQuint");
    $("#show-layer-options").fadeOut(500);
  })
  .on('click','.shuffle-items li.item',function(){
    shuffle.click(this);
  })

})
<|endoftext|>


<commit_before>
define( function() {

	var defaults = {

		key: "update",
		cssClass: "slick-update"
	}

	, Update = function( $G, settings ) {

		var handler = new Slick.EventHandler()

		, updateds = {};

		$.extend( this, {

			init: function() {

				handler
					.subscribe( $G.onBeforeEditCell, function( e, args ) {

						var node = $( $G.getCellNode( args.row, args.cell ) );

						node.is( "[data-original]" )
							|| node.attr( "data-original", node.text().replace( /^\s+|\s+$/g, "" ) );
					} )

					.subscribe( $G.onCellChange, function( e, args ) {

						var node, value, adds = $G .getAddRowsIndexes ? $G .getAddRowsIndexes() : [];

						if ( adds.indexOf( args.row ) > -1 ) { return; }

						node = $( $G.getCellNode( args.row, args.cell ) );
						value = $G.getCellEditor().getValue();

						updateds[ args.row ] = updateds[ args.row ] || {};

						if ( node.attr( "data-original" ) === value ) {

							delete updateds[ args.row ][ args.column.id ];

							--updateds[ args.row ][ "_length_" ] || delete updateds[ args.row ];
						} else {

							updateds[ args.row ][ args.column.id ] = settings.cssClass;
							updateds[ args.row ][ "_length_" ] = (updateds[ args.row ][ "_length_" ] || 0) + 1;
						}

						$G.setCellCssStyles( settings.key,
							/** Beak the refrence */
							$.extend( true, {}, updateds ) );
					} );
			},

			destroy: function() {
				handler.unsubscribeAll();
			}
		} );

		$.extend( $G, {

			getUpdateRows: function() {

				var deleteds = $G.getDeletedRows ? $G.getDeletedRows() : []

				, rows = [];

				for ( var row in updateds ) {

					deleteds.indexOf( row ) === -1 && rows.push( $G.getDataItem( row ) );
				}

				return rows;
			},

			getUpdateRowsIndexes: function() {

				return updateds;
			},

			setUpdateRows: function( hash ) {

				$G.setCellCssStyles( settings.key, updateds = hash );
			}
		} );
	};

	return function( $G, options ) {

		var settings = $.extend( {}, defaults, options || {} )

		, plugin = new Update( $G, settings );

		$G.registerPlugin( plugin );

		return plugin;
	};
} );
<commit_msg>Update Update.js<commit_after>
define( function() {

	var defaults = {

		key: "update",
		cssClass: "slick-update"
	}

	, Update = function( $G, settings ) {

		var handler = new Slick.EventHandler()

		, updateds = {};

		$.extend( this, {

			init: function() {

				handler
					.subscribe( $G.onBeforeEditCell, function( e, args ) {

						var node = $( $G.getCellNode( args.row, args.cell ) );

						node.is( "[data-original]" )
							|| node.attr( "data-original", node.text().replace( /^\s+|\s+$/g, "" ) );
					} )

					.subscribe( $G.onCellChange, function( e, args ) {

						var node, value, adds = $G .getAddRowsIndexes ? $G .getAddRowsIndexes() : [];

						if ( adds.indexOf( args.row ) > -1 ) { return; }

						node = $( $G.getCellNode( args.row, args.cell ) );
						value = $G.getCellEditor().getValue();

						updateds[ args.row ] = updateds[ args.row ] || {};

						if ( node.attr( "data-original" ) === value ) {

							delete updateds[ args.row ][ args.column.rr ];

							--updateds[ args.row ][ "_length_" ] || delete updateds[ args.row ];
						} else {

							updateds[ args.row ][ args.column.id ] = settings.cssClass;
							updateds[ args.row ][ "_length_" ] = (updateds[ args.row ][ "_length_" ] || 0) + 1;
						}

						$G.setCellCssStyles( settings.key,
							/** Beak the refrence */
							$.extend( true, {}, updateds ) );
					} );
			},

			destroy: function() {
				handler.unsubscribeAll();
			}
		} );

		$.extend( $G, {

			getUpdateRows: function() {

				var deleteds = $G.getDeletedRows ? $G.getDeletedRows() : []

				, rows = [], item;

				for ( var row in updateds ) {

					if ( deleteds.indexOf( row ) === -1 ) {

						item = $G.getDataItem( row );

						item[ "grid_action" ] = "update";

						rows.push( item );
					}
				}

				return rows;
			},

			getUpdateRowsIndexes: function() {

				return updateds;
			},

			setUpdateRows: function( hash ) {

				$G.setCellCssStyles( settings.key, updateds = hash );
			}
		} );
	};

	return function( $G, options ) {

		var settings = $.extend( {}, defaults, options || {} )

		, plugin = new Update( $G, settings );

		$G.registerPlugin( plugin );

		return plugin;
	};
} );
<|endoftext|>


<commit_before>/*
 * The routing data for the application.
 *
 * A route object has some subset of the following properties:
 *
 *   - path (String, required): the path associated with the route
 *   - component (React class, required): the page component to render
 *     for the route
 *   - title (String, required): the <title /> for the page
 *   - navbarTitle (string, defaults to null): if present, the string
 *     that should be used for the link in the navbar; if absent,
 *     indicates that the route should not appear in the navbar
 *   - isIndex (boolean, defaults to false): true only for the home page
 *
 * The route data is exported as `routeData`. This can be converted to
 * react-router routes with `createRoutes()`. For convenience, the
 * `path` properties of the routes are projected into `staticPaths`.
 */

import React from 'react';
import {IndexRoute, Redirect, Route} from 'react-router';

import Page from '../components/Page';
import posts from '../pages/posts/posts';

import HomePage from '../pages/home/HomePage';
import SkillsPage from '../pages/skills/SkillsPage';
import ProjectsPage from '../pages/projects/ProjectsPage';
import AufbauProjectPage from '../pages/projects/AufbauProjectPage';
import PhysBAMProjectPage from '../pages/projects/PhysBAMProjectPage';
import KioskProjectPage from '../pages/projects/KioskProjectPage';
import JGameProjectPage from '../pages/projects/JGameProjectPage';
import CMU15214ProjectPage from '../pages/projects/CMU15214ProjectPage';
import ExperiencePage from '../pages/experience/ExperiencePage';
import EducationPage from '../pages/education/EducationPage';
import PostsIndexPage from '../pages/posts/PostsIndexPage';

/*
 * Create a route object, filling in default properties.
 *
 * This raises an error if any required properties are absent.
 *
 * @param {object} data the data for the route object
 * @return {object} the data object with defaults filled in
 */
function route(data) {
    if (!data.path) {
        throw new Error("route missing path");
    }
    if (!!data.redirectTo === !!data.component) {
        throw new Error("route must have exactly one of component, redirectTo");
    }
    if (!data.title) {
        throw new Error("route missing title");
    }
    const defaults = {
        isIndex: false,
        navbarTitle: null,
    };
    return {
        ...defaults,
        ...data,
    };
}

export const routeData = [
    route({
        path: '/',
        component: HomePage,
        title: "William Chargin",
        navbarTitle: "Home",
        isIndex: true,
    }),
    route({
        path: '/skills',
        component: SkillsPage,
        title: "Skills",
        navbarTitle: "Skills",
    }),
    route({
        path: '/projects',
        component: ProjectsPage,
        title: "Projects",
        navbarTitle: "Projects",
    }),
    route({
        path: '/projects/aufbau',
        component: AufbauProjectPage,
        title: "Automated grading",
    }),
    route({
        path: '/projects/physbam',
        component: PhysBAMProjectPage,
        title: "Physics simulations",
    }),
    route({
        path: '/projects/kiosk',
        component: KioskProjectPage,
        title: "Kiosk",
    }),
    route({
        path: '/projects/jgame',
        component: JGameProjectPage,
        title: "JGame",
    }),
    route({
        path: '/projects/cmu-15-214',
        component: CMU15214ProjectPage,
        title: "CMU 15-214 projects",
    }),
    route({
        path: '/experience',
        component: ExperiencePage,
        title: "Experience",
        navbarTitle: "Experience",
    }),
    route({
        path: '/education',
        component: EducationPage,
        title: "Education",
        navbarTitle: "Education",
    }),
    route({
        path: '/posts',
        component: PostsIndexPage,
        title: "Posts",
        navbarTitle: "Posts",
    }),
    ...posts.map(post => route({
        path: post.path,
        component: post.pageComponent,
        title: post.title,
    })),
    //
    // "Posts" used to be known as "thoughts"; preserve those
    // permalinks.
    route({
        path: '/thoughts',
        redirectTo: '/posts',
        title: "Posts",
    }),
    ...posts.map(post => route({
        path: post.path.replace(/^\/posts\//, '/thoughts/'),
        redirectTo: post.path,
        title: post.title,
    })),
];

export function createRoutes() {
    return <Route path="/" component={Page}>
        {routeData.map(({redirectTo, isIndex, path, component}, i) => {
            if (redirectTo !== undefined) {
                return <Redirect key={i} from={path} to={redirectTo} />;
            } else if (isIndex) {
                return <IndexRoute key={i} component={component} />;
            } else {
                return <Route key={i} path={path} component={component} />;
            }
        })}
    </Route>;
}

export function resolveRouteFromPath(path) {
    const matches = (candidateRoute) => {
        const candidatePath = candidateRoute.path;
        const start = path.substring(0, candidatePath.length);
        const end = path.substring(candidatePath.length);
        return start === candidatePath && (end.length === 0 || end === '/');
    };
    return routeData.filter(matches)[0] || null;
}

export function resolveTitleFromPath(path) {
    const route = resolveRouteFromPath(path);
    const fallback = "William Chargin";
    return (route && route.title) || fallback;
}

export const staticPaths = routeData.map(x => x.path);
<commit_msg>Hide “Skills”, “Projects”, “Experience”, and “Education”<commit_after>/*
 * The routing data for the application.
 *
 * A route object has some subset of the following properties:
 *
 *   - path (String, required): the path associated with the route
 *   - component (React class, required): the page component to render
 *     for the route
 *   - title (String, required): the <title /> for the page
 *   - navbarTitle (string, defaults to null): if present, the string
 *     that should be used for the link in the navbar; if absent,
 *     indicates that the route should not appear in the navbar
 *   - isIndex (boolean, defaults to false): true only for the home page
 *
 * The route data is exported as `routeData`. This can be converted to
 * react-router routes with `createRoutes()`. For convenience, the
 * `path` properties of the routes are projected into `staticPaths`.
 */

import React from 'react';
import {IndexRoute, Redirect, Route} from 'react-router';

import Page from '../components/Page';
import posts from '../pages/posts/posts';

import HomePage from '../pages/home/HomePage';
import SkillsPage from '../pages/skills/SkillsPage';
import ProjectsPage from '../pages/projects/ProjectsPage';
import AufbauProjectPage from '../pages/projects/AufbauProjectPage';
import PhysBAMProjectPage from '../pages/projects/PhysBAMProjectPage';
import KioskProjectPage from '../pages/projects/KioskProjectPage';
import JGameProjectPage from '../pages/projects/JGameProjectPage';
import CMU15214ProjectPage from '../pages/projects/CMU15214ProjectPage';
import ExperiencePage from '../pages/experience/ExperiencePage';
import EducationPage from '../pages/education/EducationPage';
import PostsIndexPage from '../pages/posts/PostsIndexPage';

/*
 * Create a route object, filling in default properties.
 *
 * This raises an error if any required properties are absent.
 *
 * @param {object} data the data for the route object
 * @return {object} the data object with defaults filled in
 */
function route(data) {
    if (!data.path) {
        throw new Error("route missing path");
    }
    if (!!data.redirectTo === !!data.component) {
        throw new Error("route must have exactly one of component, redirectTo");
    }
    if (!data.title) {
        throw new Error("route missing title");
    }
    const defaults = {
        isIndex: false,
        navbarTitle: null,
    };
    return {
        ...defaults,
        ...data,
    };
}

export const routeData = [
    route({
        path: '/',
        component: HomePage,
        title: "William Chargin",
        navbarTitle: "Home",
        isIndex: true,
    }),
    route({
        path: '/skills',
        component: SkillsPage,
        title: "Skills",
    }),
    route({
        path: '/projects',
        component: ProjectsPage,
        title: "Projects",
    }),
    route({
        path: '/projects/aufbau',
        component: AufbauProjectPage,
        title: "Automated grading",
    }),
    route({
        path: '/projects/physbam',
        component: PhysBAMProjectPage,
        title: "Physics simulations",
    }),
    route({
        path: '/projects/kiosk',
        component: KioskProjectPage,
        title: "Kiosk",
    }),
    route({
        path: '/projects/jgame',
        component: JGameProjectPage,
        title: "JGame",
    }),
    route({
        path: '/projects/cmu-15-214',
        component: CMU15214ProjectPage,
        title: "CMU 15-214 projects",
    }),
    route({
        path: '/experience',
        component: ExperiencePage,
        title: "Experience",
    }),
    route({
        path: '/education',
        component: EducationPage,
        title: "Education",
    }),
    route({
        path: '/posts',
        component: PostsIndexPage,
        title: "Posts",
        navbarTitle: "Posts",
    }),
    ...posts.map(post => route({
        path: post.path,
        component: post.pageComponent,
        title: post.title,
    })),
    //
    // "Posts" used to be known as "thoughts"; preserve those
    // permalinks.
    route({
        path: '/thoughts',
        redirectTo: '/posts',
        title: "Posts",
    }),
    ...posts.map(post => route({
        path: post.path.replace(/^\/posts\//, '/thoughts/'),
        redirectTo: post.path,
        title: post.title,
    })),
];

export function createRoutes() {
    return <Route path="/" component={Page}>
        {routeData.map(({redirectTo, isIndex, path, component}, i) => {
            if (redirectTo !== undefined) {
                return <Redirect key={i} from={path} to={redirectTo} />;
            } else if (isIndex) {
                return <IndexRoute key={i} component={component} />;
            } else {
                return <Route key={i} path={path} component={component} />;
            }
        })}
    </Route>;
}

export function resolveRouteFromPath(path) {
    const matches = (candidateRoute) => {
        const candidatePath = candidateRoute.path;
        const start = path.substring(0, candidatePath.length);
        const end = path.substring(candidatePath.length);
        return start === candidatePath && (end.length === 0 || end === '/');
    };
    return routeData.filter(matches)[0] || null;
}

export function resolveTitleFromPath(path) {
    const route = resolveRouteFromPath(path);
    const fallback = "William Chargin";
    return (route && route.title) || fallback;
}

export const staticPaths = routeData.map(x => x.path);
<|endoftext|>


<commit_before>var Query   = require("./query")
  , Utils   = require("../../utils")

module.exports = (function() {
  var ConnectorManager = function(sequelize, config) {
    var pgModule = config.dialectModulePath || 'pg'

    this.sequelize = sequelize
    this.client         = null
    this.config         = config || {}
    this.config.port    = this.config.port || 5432
    this.pooling        = (!!this.config.pool && (this.config.pool.maxConnections > 0))
    this.pg             = this.config.native ? require(pgModule).native : require(pgModule)
    this.poolIdentifier = null

    // Better support for BigInts
    // https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935
    this.pg.types.setTypeParser(20, String);

    // set pooling parameters if specified
    if (this.pooling) {
      this.pg.defaults.poolSize           = this.config.pool.maxConnections
      this.pg.defaults.poolIdleTimeout    = this.config.pool.maxIdleTime
      this.pg.defaults.reapIntervalMillis = this.config.pool.reapInterval || 1000
    }

    this.disconnectTimeoutId = null
    this.pendingQueries = 0
    this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)

    process.on('exit', function() {
      this.disconnect()
    }.bind(this))
  }
  Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)

  ConnectorManager.prototype.endQuery = function() {
    var self = this

    self.pendingQueries--

    if (!self.pooling && self.pendingQueries === 0) {
      setTimeout(function() {
        self.pendingQueries === 0 && self.disconnect.call(self)
      }, 100)
    }
  }

  ConnectorManager.prototype.query = function(sql, callee, options) {
    var self = this

    self.pendingQueries++

    return new Utils.CustomEventEmitter(function(emitter) {
      self.connect()
      .on('error', function(err) {
        emitter.emit('error', err)
      })
      .on('success', function(done) {
        var query = new Query(self.client, self.sequelize, callee, options || {})

        return query.run(sql)
          .complete(function(err) { done && done(err) })
          .success(function(results) { self.endQuery.call(self) })
          .error(function(err) { self.endQuery.call(self) })
          .proxy(emitter)
      })
    }).run()
  }

  ConnectorManager.prototype.connect = function(callback) {
    var self = this
    var emitter = new (require('events').EventEmitter)()

    // in case database is slow to connect, prevent orphaning the client
    // TODO: We really need some sort of queue/flush/drain mechanism
    if (this.isConnecting && !this.pooling && this.client === null) {
      emitter.emit('success', null)
      return emitter
    }

    this.isConnecting = true
    this.isConnected  = false

    var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config)

    var connectCallback = function(err, client, done) {
      self.isConnecting = false

      if (!!err) {
        // release the pool immediately, very important.
        done && done(err)
        self.client = null

        if (err.code) {
          switch(err.code) {
          case 'ECONNREFUSED':
            emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
            break
          case 'ENOTFOUND':
          case 'EHOSTUNREACH':
          case 'EINVAL':
            emitter.emit('error', new Error("Failed to find PostgresSQL server. Please double check your settings."))
            break
          default:
            emitter.emit('error', err)
            break
          }
        }
      } else if (client) {
        client.query("SET TIME ZONE 'UTC'").on('end', function() {
          self.isConnected = true
          self.client = client
          emitter.emit('success', done)
        })
      } else {
        done && done()
        self.client = null
        emitter.emit('success', done)
      }
    }

    if (this.pooling) {
      // acquire client from pool
      this.pg.connect(uri, connectCallback)
    } else {
      if (!!this.client && this.client.readyForQuery === true) {
        connectCallback(null, this.client)
      } else {
        //create one-off client
        this.client = new this.pg.Client(uri)
        this.client.connect(connectCallback)
      }
    }

    return emitter
  }

  ConnectorManager.prototype.disconnect = function() {
    if (this.poolIdentifier) {
      this.poolIdentifier.destroyAllNow()
    }

    if (this.client) {
      this.client.end()
    }

    this.client = null
    this.isConnecting = false
    this.isConnected  = false
  }

  return ConnectorManager
})()<commit_msg>Postgres single connections will no longer leak<commit_after>var Query   = require("./query")
  , Utils   = require("../../utils")

module.exports = (function() {
  var ConnectorManager = function(sequelize, config) {
    var pgModule = config.dialectModulePath || 'pg'

    this.sequelize = sequelize
    this.client         = null
    this.config         = config || {}
    this.config.port    = this.config.port || 5432
    this.pooling        = (!!this.config.pool && (this.config.pool.maxConnections > 0))
    this.pg             = this.config.native ? require(pgModule).native : require(pgModule)
    this.poolIdentifier = null

    // Better support for BigInts
    // https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935
    this.pg.types.setTypeParser(20, String);

    // set pooling parameters if specified
    if (this.pooling) {
      this.pg.defaults.poolSize           = this.config.pool.maxConnections
      this.pg.defaults.poolIdleTimeout    = this.config.pool.maxIdleTime
      this.pg.defaults.reapIntervalMillis = this.config.pool.reapInterval || 1000
    }

    this.disconnectTimeoutId = null
    this.pendingQueries = 0
    this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)

    process.on('exit', function() {
      this.disconnect()
    }.bind(this))
  }
  Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)

  ConnectorManager.prototype.endQuery = function() {
    var self = this

    self.pendingQueries--

    if (!self.pooling && self.pendingQueries === 0) {
      setTimeout(function() {
        self.pendingQueries === 0 && self.disconnect.call(self)
      }, 100)
    }
  }

  ConnectorManager.prototype.query = function(sql, callee, options) {
    var self = this

    self.pendingQueries++

    return new Utils.CustomEventEmitter(function(emitter) {
      self.connect()
      .on('error', function(err) {
        emitter.emit('error', err)
      })
      .on('success', function(done) {
        var query = new Query(self.client, self.sequelize, callee, options || {})

        return query.run(sql)
          .complete(function(err) { done && done(err) })
          .success(function(results) { self.endQuery.call(self) })
          .error(function(err) { self.endQuery.call(self) })
          .proxy(emitter)
      })
    }).run()
  }

  ConnectorManager.prototype.connect = function(callback) {
    var self = this
    var emitter = new (require('events').EventEmitter)()

    // in case database is slow to connect, prevent orphaning the client
    // TODO: We really need some sort of queue/flush/drain mechanism
    if (this.isConnecting && !this.pooling && this.client === null) {
      emitter.emit('success', null)
      return emitter
    }

    this.isConnecting = true
    this.isConnected  = false

    var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config)

    var connectCallback = function(err, client, done) {
      self.isConnecting = false

      if (!!err) {
        // release the pool immediately, very important.
        done && done(err)
        self.client = null

        if (err.code) {
          switch(err.code) {
          case 'ECONNREFUSED':
            emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
            break
          case 'ENOTFOUND':
          case 'EHOSTUNREACH':
          case 'EINVAL':
            emitter.emit('error', new Error("Failed to find PostgresSQL server. Please double check your settings."))
            break
          default:
            emitter.emit('error', err)
            break
          }
        }
      } else if (client) {
        client.query("SET TIME ZONE 'UTC'").on('end', function() {
          self.isConnected = true
          self.client = client
          emitter.emit('success', done)
        })
      } else {
        done && done()
        self.client = null
        emitter.emit('success')
      }
    }

    if (this.pooling) {
      // acquire client from pool
      this.pg.connect(uri, connectCallback)
    } else {
      if (!!this.client) {
        connectCallback(null, this.client)
      } else {
        //create one-off client
        this.client = new this.pg.Client(uri)
        this.client.connect(connectCallback)
      }
    }

    return emitter
  }

  ConnectorManager.prototype.disconnect = function() {
    if (this.poolIdentifier) {
      this.poolIdentifier.destroyAllNow()
    }

    if (this.client) {
      this.client.end.bind(this.client)
    }

    this.isConnecting = false
    this.isConnected  = false
  }

  return ConnectorManager
})()<|endoftext|>