<commit_before>(function(exports) {

	/*!
	 * Canvas
	 *
	 */

	exports.Canvas = Canvas;

	function Canvas(canvas) {
		this.canvas = canvas;
		// 2D Canvas Context
		this.ctx = canvas.getContext('2d');

		this.centerX = canvas.width / 2;
		this.centerY = canvas.height / 2;

		this.width = canvas.width;
		this.height = canvas.height;

//		console.log(canvas.width);
//		console.log(canvas.height);
	};

	Canvas.prototype = {

		constructor: Canvas,

		drawOval: function (settings) {

			// We keep the center fixed
			var x = settings.x / settings.scale_x;
			var y = settings.y / settings.scale_y;

			var radius = settings.radius || 100;
			//console.log(settings);
			//console.log('X,Y(' + x + ', ' + y + '); Radius: ' + radius + ', Scale: ' + settings.scale_x + ',' + settings.scale_y);

			this.ctx.lineWidth = settings.lineWidth || 1;
			this.ctx.strokeStyle = settings.color || '#000000';

			this.ctx.save();
			this.ctx.scale(settings.scale_x, settings.scale_y);
			this.ctx.beginPath();
			this.ctx.arc(x, y, radius, 0, Math.PI*2, false);
			this.ctx.stroke();
			this.ctx.closePath();
			this.ctx.restore();
		},

		drawLine: function (settings) {

			var from_x = settings.x;
			var from_y = settings.y;

			var length = settings.length;
			var angle = settings.angle;

			// Rotation
			var to_x = - Math.cos(angle) * length + settings.x;
			var to_y =  Math.sin(angle) * length + settings.y;
			//console.log('aa ' + to_x + ' ' + to_y);

			//console.log('From (' + from_x + ', ' + from_y + ') To (' + to_x + ', ' + to_y + ')');
			//console.log('Length: ' + length + ', Angle: ' + angle );

			this.ctx.lineWidth = settings.lineWidth || 1;
			this.ctx.strokeStyle = settings.color || '#000000';

			this.ctx.save();
			this.ctx.beginPath();
			this.ctx.moveTo(from_x,from_y);
			this.ctx.lineTo(to_x,to_y);
			this.ctx.stroke();
			this.ctx.closePath();
			this.ctx.restore();
		},

		scale: function (x,y) {
			this.ctx.scale(x,y);
			this.centerX = this.canvas.width / 2 / x;
			this.centerY = this.canvas.height / 2 / y;
		},

		clear: function() {
			this.ctx.clearRect(0, 0, this.width, this.height);
			// For IE
			var w = this.canvas.width;
			this.canvas.width = 1;
			this.canvas.width = w;
		}

	};
})(node.window);<commit_msg>Random CF faces working. Minor fixes to do remain<commit_after>(function(exports) {

	/*!
	 * Canvas
	 *
	 */

	exports.Canvas = Canvas;

	function Canvas(canvas) {

		this.canvas = canvas;
		// 2D Canvas Context
		this.ctx = canvas.getContext('2d');

		this.centerX = canvas.width / 2;
		this.centerY = canvas.height / 2;

		this.width = canvas.width;
		this.height = canvas.height;

//		console.log(canvas.width);
//		console.log(canvas.height);
	};

	Canvas.prototype = {

		constructor: Canvas,

		drawOval: function (settings) {

			// We keep the center fixed
			var x = settings.x / settings.scale_x;
			var y = settings.y / settings.scale_y;

			var radius = settings.radius || 100;
			//console.log(settings);
			//console.log('X,Y(' + x + ', ' + y + '); Radius: ' + radius + ', Scale: ' + settings.scale_x + ',' + settings.scale_y);

			this.ctx.lineWidth = settings.lineWidth || 1;
			this.ctx.strokeStyle = settings.color || '#000000';

			this.ctx.save();
			this.ctx.scale(settings.scale_x, settings.scale_y);
			this.ctx.beginPath();
			this.ctx.arc(x, y, radius, 0, Math.PI*2, false);
			this.ctx.stroke();
			this.ctx.closePath();
			this.ctx.restore();
		},

		drawLine: function (settings) {

			var from_x = settings.x;
			var from_y = settings.y;

			var length = settings.length;
			var angle = settings.angle;

			// Rotation
			var to_x = - Math.cos(angle) * length + settings.x;
			var to_y =  Math.sin(angle) * length + settings.y;
			//console.log('aa ' + to_x + ' ' + to_y);

			//console.log('From (' + from_x + ', ' + from_y + ') To (' + to_x + ', ' + to_y + ')');
			//console.log('Length: ' + length + ', Angle: ' + angle );

			this.ctx.lineWidth = settings.lineWidth || 1;
			this.ctx.strokeStyle = settings.color || '#000000';

			this.ctx.save();
			this.ctx.beginPath();
			this.ctx.moveTo(from_x,from_y);
			this.ctx.lineTo(to_x,to_y);
			this.ctx.stroke();
			this.ctx.closePath();
			this.ctx.restore();
		},

		scale: function (x,y) {
			this.ctx.scale(x,y);
			this.centerX = this.canvas.width / 2 / x;
			this.centerY = this.canvas.height / 2 / y;
		},

		clear: function() {
			this.ctx.clearRect(0, 0, this.width, this.height);
			// For IE
			var w = this.canvas.width;
			this.canvas.width = 1;
			this.canvas.width = w;
		}

	};
})(node.window);<|endoftext|>


<commit_before>/* Patches from box-js */
	window = this;

	Date.prototype.getYear = function() {
		return new Date().getFullYear();
	};

	Array.prototype.Count = function() {
		return this.length;
	};

	function Error(message, description) {
		const e = new Error(message);
		e.description = description;
		return e;
	}

	_OriginalFunction = Function;
	Function = function(...args) {
		const originalSource = args.pop();
		const source = `/* Function arguments: ${JSON.stringify(args)} */\n` + rewrite(originalSource);
		logJS(source);
		return new _OriginalFunction(...args, source);
	}
/* End patches */<commit_msg>Patch Date.toString<commit_after>/* Patches from box-js */
	window = this;

	let fullYearGetter = Date.prototype.getFullYear;
	Date.prototype.getFullYear = function() {
		console.log("Warning: the script tried to read the current date.");
		console.log("If it doesn't work correctly (eg. fails to decrypt a string,");
		console.log("try editing patch.js with a different year.");

		// return 2017;
		return fullYearGetter.call(this);
	};
	Date.prototype.getYear = function() {
		return this.getFullYear();
	};
	Date.prototype.toString = function() {
		// Example format: Thu Aug 24 18:17:18 UTC+0200 2017
		const dayName = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.getDay()];
		const monName = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][this.getMonth()];
		return [
			dayName, monName, this.getUTCDay(),
			this.getUTCHours() + ":" + this.getUTCMinutes() + ":" + this.getUTCSeconds(),
			"UTC-0500", // New York timezone
			this.getFullYear()
		].join(" ");
	}

	Array.prototype.Count = function() {
		return this.length;
	};

	function Error(message, description) {
		const e = new Error(message);
		e.description = description;
		return e;
	}

	let _OriginalFunction = Function;
	Function = function(...args) {
		const originalSource = args.pop();
		const source = `/* Function arguments: ${JSON.stringify(args)} */\n` + rewrite(originalSource);
		logJS(source);
		return new _OriginalFunction(...args, source);
	}
/* End patches */<|endoftext|>


<commit_before>// oldIE-userDataStorage provides storage for Internet Explorer
// versions 6 and 7, where no localStorage, sessionStorage, etc
// is available.

var util = require('../src/util')
var Global = util.Global

module.exports = {
	name: 'oldIE-userDataStorage',
	write: write,
	read: read,
	each: each,
	remove: remove,
	clearAll: clearAll,
}

var storageName = 'storejs'
var doc = Global.document
var _withStorageEl = _makeIEStorageElFunction()
var disable = (Global.navigator ? Global.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./) // MSIE 9.x, MSIE 10.x

function write(unfixedKey, data) {
	if (disable) { return }
	var fixedKey = fixKey(unfixedKey)
	_withStorageEl(function(storageEl) {
		storageEl.setAttribute(fixedKey, data)
		storageEl.save(storageName)
	})
}

function read(unfixedKey) {
	if (disable) { return }
	var fixedKey = fixKey(unfixedKey)
	var res = null
	_withStorageEl(function(storageEl) {
		res = storageEl.getAttribute(fixedKey)
	})
	return res
}

function each(callback) {
	_withStorageEl(function(storageEl) {
		var attributes = storageEl.XMLDocument.documentElement.attributes
		for (var i=attributes.length-1; i>=0; i--) {
			var attr = attributes[i]
			callback(storageEl.getAttribute(attr.name), attr.name)
		}
	})
}

function remove(unfixedKey) {
	var fixedKey = fixKey(unfixedKey)
	_withStorageEl(function(storageEl) {
		storageEl.removeAttribute(fixedKey)
		storageEl.save(storageName)
	})
}

function clearAll() {
	_withStorageEl(function(storageEl) {
		var attributes = storageEl.XMLDocument.documentElement.attributes
		storageEl.load(storageName)
		for (var i=attributes.length-1; i>=0; i--) {
			storageEl.removeAttribute(attributes[i].name)
		}
		storageEl.save(storageName)
	})
}

// Helpers
//////////

// In IE7, keys cannot start with a digit or contain certain chars.
// See https://github.com/marcuswestin/store.js/issues/40
// See https://github.com/marcuswestin/store.js/issues/83
var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
function fixKey(key) {
	return key.replace(/^d/, '___$&').replace(forbiddenCharsRegex, '___')
}

function _makeIEStorageElFunction() {
	if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
		return null
	}
	var scriptTag = 'script',
		storageOwner,
		storageContainer,
		storageEl

	// Since #userData storage applies only to specific paths, we need to
	// somehow link our data to a specific path.  We choose /favicon.ico
	// as a pretty safe option, since all browsers already make a request to
	// this URL anyway and being a 404 will not hurt us here.  We wrap an
	// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
	// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
	// since the iframe access rules appear to allow direct access and
	// manipulation of the document element, even for a 404 page.  This
	// document can be used instead of the current document (which would
	// have been limited to the current path) to perform #userData storage.
	try {
		/* global ActiveXObject */
		storageContainer = new ActiveXObject('htmlfile')
		storageContainer.open()
		storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'>')
		storageContainer.close()
		storageOwner = storageContainer.w.frames[0].document
		storageEl = storageOwner.createElement('div')
	} catch(e) {
		// somehow ActiveXObject instantiation failed (perhaps some special
		// security settings or otherwse), fall back to per-path storage
		storageEl = doc.createElement('div')
		storageOwner = doc.body
	}

	return function(storeFunction) {
		var args = [].slice.call(arguments, 0)
		args.unshift(storageEl)
		// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
		// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
		storageOwner.appendChild(storageEl)
		storageEl.addBehavior('#default#userData')
		storageEl.load(storageName)
		storeFunction.apply(this, args)
		storageOwner.removeChild(storageEl)
		return
	}
}
<commit_msg>Fix oldIE-userDataStorage for keys that start with a digit.<commit_after>// oldIE-userDataStorage provides storage for Internet Explorer
// versions 6 and 7, where no localStorage, sessionStorage, etc
// is available.

var util = require('../src/util')
var Global = util.Global

module.exports = {
	name: 'oldIE-userDataStorage',
	write: write,
	read: read,
	each: each,
	remove: remove,
	clearAll: clearAll,
}

var storageName = 'storejs'
var doc = Global.document
var _withStorageEl = _makeIEStorageElFunction()
var disable = (Global.navigator ? Global.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./) // MSIE 9.x, MSIE 10.x

function write(unfixedKey, data) {
	if (disable) { return }
	var fixedKey = fixKey(unfixedKey)
	_withStorageEl(function(storageEl) {
		storageEl.setAttribute(fixedKey, data)
		storageEl.save(storageName)
	})
}

function read(unfixedKey) {
	if (disable) { return }
	var fixedKey = fixKey(unfixedKey)
	var res = null
	_withStorageEl(function(storageEl) {
		res = storageEl.getAttribute(fixedKey)
	})
	return res
}

function each(callback) {
	_withStorageEl(function(storageEl) {
		var attributes = storageEl.XMLDocument.documentElement.attributes
		for (var i=attributes.length-1; i>=0; i--) {
			var attr = attributes[i]
			callback(storageEl.getAttribute(attr.name), attr.name)
		}
	})
}

function remove(unfixedKey) {
	var fixedKey = fixKey(unfixedKey)
	_withStorageEl(function(storageEl) {
		storageEl.removeAttribute(fixedKey)
		storageEl.save(storageName)
	})
}

function clearAll() {
	_withStorageEl(function(storageEl) {
		var attributes = storageEl.XMLDocument.documentElement.attributes
		storageEl.load(storageName)
		for (var i=attributes.length-1; i>=0; i--) {
			storageEl.removeAttribute(attributes[i].name)
		}
		storageEl.save(storageName)
	})
}

// Helpers
//////////

// In IE7, keys cannot start with a digit or contain certain chars.
// See https://github.com/marcuswestin/store.js/issues/40
// See https://github.com/marcuswestin/store.js/issues/83
var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
function fixKey(key) {
	return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
}

function _makeIEStorageElFunction() {
	if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
		return null
	}
	var scriptTag = 'script',
		storageOwner,
		storageContainer,
		storageEl

	// Since #userData storage applies only to specific paths, we need to
	// somehow link our data to a specific path.  We choose /favicon.ico
	// as a pretty safe option, since all browsers already make a request to
	// this URL anyway and being a 404 will not hurt us here.  We wrap an
	// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
	// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
	// since the iframe access rules appear to allow direct access and
	// manipulation of the document element, even for a 404 page.  This
	// document can be used instead of the current document (which would
	// have been limited to the current path) to perform #userData storage.
	try {
		/* global ActiveXObject */
		storageContainer = new ActiveXObject('htmlfile')
		storageContainer.open()
		storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'>')
		storageContainer.close()
		storageOwner = storageContainer.w.frames[0].document
		storageEl = storageOwner.createElement('div')
	} catch(e) {
		// somehow ActiveXObject instantiation failed (perhaps some special
		// security settings or otherwse), fall back to per-path storage
		storageEl = doc.createElement('div')
		storageOwner = doc.body
	}

	return function(storeFunction) {
		var args = [].slice.call(arguments, 0)
		args.unshift(storageEl)
		// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
		// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
		storageOwner.appendChild(storageEl)
		storageEl.addBehavior('#default#userData')
		storageEl.load(storageName)
		storeFunction.apply(this, args)
		storageOwner.removeChild(storageEl)
		return
	}
}
<|endoftext|>


<commit_before>Parse.initialize("s32blhuMK0fzjARmluinIoP7GMNCsWXPsREdsFhD", "geSjnD0tu528nZjZHMKPCQ9XB9EbGKCoaUGdXf56");

var Target = Parse.Object.extend("Target");
var Badmouth = Parse.Object.extend("Badmouth");

function submit_badmouth(target_name, badmouth_text){
    var query = new Parse.Query(Target);
    query.equalTo("name", target_name);
    query.find({
        success: function(results) {
            if (results.length == 0) {
                var target = new Target();
                target.set("name", target_name);
                target.save(null, {
                    success: function(target) {
                        insert_badmouth(target.id, badmouth_text);
                    },
                    error: function(error) {
                    console.log(error);
                    }
                })
            }
            else {
                insert_badmouth(results[0].id, badmouth_text);
            }
        },
        error: function(error) {
        console.log(error);
        }

    })

}

function insert_badmouth(target_id, badmouth_text){
	var Target = Parse.Object.extend("Target");
	var target = new Target();
	target.id = target_id;

	var Badmouth = Parse.Object.extend("Badmouth");
	var badmouth = new Badmouth();
	badmouth.set("text", badmouth_text);
	badmouth.set("parent", target);

	badmouth.save(null, {
		success: function(badmouth) {
    		alert('New object created with objectId: ' + badmouth.id);
    		target.fetch({
		    	success: function(target) {
		    		target.increment("bmcount");
		    		var relation = target.relation("badmouths");
		    		relation.add(badmouth);
		    		target.save(null, {
						success: function(target) {
		    				alert('New object created with objectId: ' + target.id);
						},
						error: function(target, error) {
		    				alert('Failed to create new object, with error code: ' + error.message);
						}

					});
		    		console.log("here");
				},
				error: function(target, error) {
					console.log(error);
				}
			});
		},
		error: function(badmouth, error) {
    		alert('Failed to create new object, with error code: ' + error.message);
		}
	});

}

function get_target_promise(){
    var query = new Parse.Query(Target);
    var d = new Date();
    var time = (5 * 24 * 3600 * 1000);
    var expirationDate = new Date(d.getTime() - (time));
    query.greaterThan("createdAt",  expirationDate);
    query.descending("bmcount");
    return query.find();
}

function populate_table(){
promise = get_target_promise();
promise.then(function(results) {
  console.log(results);
})
}

function get_badmouths(target_id) {
    var query = new Parse.Query(Target);
    query.equalTo("objectId",target_id);
    return query.find();
}

function init_badmouths(){
promise = get_target_promise();
promise.then(function(results) {
    for (i = 0; i < results.length; i++) {
        results[i].relation("badmouths").query().find().then(function (results2) {
            console.log(results2[0].get("text")); });
    }
})
}<commit_msg>Backend works?1<commit_after>Parse.initialize("s32blhuMK0fzjARmluinIoP7GMNCsWXPsREdsFhD", "geSjnD0tu528nZjZHMKPCQ9XB9EbGKCoaUGdXf56");

var Target = Parse.Object.extend("Target");
var Badmouth = Parse.Object.extend("Badmouth");

function submit_badmouth(target_name, badmouth_text){
    var query = new Parse.Query(Target);
    query.equalTo("name", target_name);
    query.find({
        success: function(results) {
            if (results.length == 0) {
                var target = new Target();
                target.set("name", target_name);
                target.save(null, {
                    success: function(target) {
                        insert_badmouth(target.id, badmouth_text);
                    },
                    error: function(error) {
                    console.log(error);
                    }
                })
            }
            else {
                insert_badmouth(results[0].id, badmouth_text);
            }
        },
        error: function(error) {
        console.log(error);
        }

    })

}

function insert_badmouth(target_id, badmouth_text){
	var Target = Parse.Object.extend("Target");
	var target = new Target();
	target.id = target_id;

	var Badmouth = Parse.Object.extend("Badmouth");
	var badmouth = new Badmouth();
	badmouth.set("text", badmouth_text);
	badmouth.set("parent", target);

	badmouth.save(null, {
		success: function(badmouth) {
    		alert('New object created with objectId: ' + badmouth.id);
    		target.fetch({
		    	success: function(target) {
		    		target.increment("bmcount");
		    		var relation = target.relation("badmouths");
		    		relation.add(badmouth);
		    		target.save(null, {
						success: function(target) {
						},
						error: function(target, error) {
		    				console.log('Failed to create new object, with error code: ' + error.message);
						}

					});
		    		console.log("here");
				},
				error: function(target, error) {
					console.log(error);
				}
			});
		},
		error: function(badmouth, error) {
    		console.log('Failed to create new object, with error code: ' + error.message);
		}
	});

}

function get_target_promise(){
    var query = new Parse.Query(Target);
    var d = new Date();
    var time = (5 * 24 * 3600 * 1000);
    var expirationDate = new Date(d.getTime() - (time));
    query.greaterThan("createdAt",  expirationDate);
    query.descending("bmcount");
    return query.find();
}

function populate_table(){
promise = get_target_promise();
promise.then(function(results) {
  console.log(results);
})
}

function get_badmouths(target_id) {
    var query = new Parse.Query(Target);
    query.equalTo("objectId",target_id);
    return query.find();
}

function init_badmouths(){
promise = get_target_promise();
promise.then(function(results) {
    for (i = 0; i < results.length; i++) {
        results[i].relation("badmouths").query().find().then(function (results2) {
            console.log(results2[0].get("text")); });
    }
})
}<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');

var themeFunctions = require('theme/functions') || {};

var MdHelper = require('./elements/MdHelper');

function allPosts() {
  var returnObj = {};

  var posts = _.map(postReq().keys(), function(name) {
    return [
      name,
      postReq()(name),
    ];
  });

  // Include drafts if we're not in prod
  var drafts = [];
  if(process.env.NODE_ENV !== 'production') {
    var req = draftReq();

    drafts = _.map(req.keys(), function(name) {
      return [
        name,
        _.assign({draft: true}, draftReq()(name)),
      ];
    });
  }

  // Build some nice objects from the files
  _.each(posts.concat(drafts), function(fileArr) {
    // Name is on format ./YYYY-MM-DD-url_title.md
    // TODO: Configurable file name standard
    var processedFile = processPost(
      fileArr[1],
      fileArr[0].slice(2) // remove the './'
    );

    returnObj[processedFile.url] = processedFile;
  });

  return returnObj;
}
exports.allPosts = allPosts;

function allPages() {
  // TODO: allow hooks on page processing
  var req = pageReq();
  var pages = {};

  _.each(req.keys(), function(name) {
    // name is format ./url_title.ext
    var file = req(name); // require the file
    var fileName = name.slice(2); // remove the './'

    var content = renderContent(file);

    // url is filename minus extension
    var url = _.kebabCase(fileName.split('.')[0]);

    // title is the capitalized url
    var title = _.capitalize(url.replace(/\-/g, ' '));

    // rewrite the index file
    if(url === 'index') {
      url = '/';
    }

    pages[url] = {
      url: url,
      fileName: fileName,
      title: title,
      content: content,
    };
  });

  return pages;
}
exports.allPages = allPages;

function postForPath(path) {
  return allPosts()[path];
}
exports.postForPath = postForPath;

function pageForPath(path) {
  return allPages()[path];
}
exports.pageForPath = pageForPath;

function pageReq() {
  return require.context('pages', false);
}
exports.pageReq = pageReq;

function postReq() {
  return require.context('posts', false, /^\.\/.*\.md$/);
}
exports.postReq = postReq;

function draftReq() {
  try {
    return require.context('drafts', false, /^\.\/.*\.md$/);
  }
  catch(e) {}
}
exports.draftReq = draftReq;

function renderContent(content) {
  return MdHelper.render(content);
}
exports.renderContent = renderContent;

function processPost(file, fileName) {
  // TODO: implement nicer hooks to configurable functions

  // clean the filename to get the url
  var url;
  if(themeFunctions.url) {
    url = themeFunctions.url(file, fileName);
  }
  else {
    // XXXXX: use a path opt to cut the ext instead?
    url = fileName.slice(0, fileName.length - 3);
  }

  // get the date from the file name if it's not in the frontmatter
  var date;
  if(themeFunctions.date) {
    date = themeFunctions.date(file, fileName);
  }
  else {
    date = file.date || fileName.slice(0, 10);
  }

  // get the content
  var content = MdHelper.render(file.__content);

  // generate the preview
  var preview;
  if(themeFunctions.preview) {
    preview = themeFunctions.preview(file, fileName);
  }
  else {
    preview = file.preview || MdHelper.getContentPreview(file.__content);
  }

  return _.assign({}, file, {
    url: url,
    content: content,
    date: date,
    preview: preview
  });
}
<commit_msg>Generate next/prev references for posts and drafts<commit_after>'use strict';
var _ = require('lodash');

var themeFunctions = require('theme/functions') || {};

var MdHelper = require('./elements/MdHelper');

function allPosts() {
  var returnObj = {};

  var posts = _.map(postReq().keys(), function(name) {
    return [
      name,
      postReq()(name),
    ];
  });

  // Include drafts if we're not in prod
  var drafts = [];
  if(process.env.NODE_ENV !== 'production') {
    var req = draftReq();

    drafts = _.map(req.keys(), function(name) {
      return [
        name,
        _.assign({draft: true}, draftReq()(name)),
      ];
    });
  }

  // TODO: build plugin arch here
  posts = generateNextPrev(posts);
  drafts = generateNextPrev(drafts);

  // Build some nice objects from the files
  _.each(posts.concat(drafts), function(fileArr) {
    var post = fileArr[1];
    var fileName = fileArr[0].slice(2); // remove the './'

    // Name is on format ./YYYY-MM-DD-url_title.md
    // TODO: Configurable file name standard
    var processedFile = processPost(
      post,
      fileName
    );

    returnObj[processedFile.url] = processedFile;
  });

  return returnObj;
}
exports.allPosts = allPosts;

// TODO: push into a plugin
function generateNextPrev(posts) {
  var len = posts.length;

  return posts.map(function(fileArr, i) {
    var post = fileArr[1];

    if(i > 0) {
      post.prev = posts[1][i - 1];
    }
    if(i < len - 2) {
      post.next = posts[1][i + 1];
    }

    return fileArr;
  });
}

function allPages() {
  // TODO: allow hooks on page processing
  var req = pageReq();
  var pages = {};

  _.each(req.keys(), function(name) {
    // name is format ./url_title.ext
    var file = req(name); // require the file
    var fileName = name.slice(2); // remove the './'

    var content = renderContent(file);

    // url is filename minus extension
    var url = _.kebabCase(fileName.split('.')[0]);

    // title is the capitalized url
    var title = _.capitalize(url.replace(/\-/g, ' '));

    // rewrite the index file
    if(url === 'index') {
      url = '/';
    }

    pages[url] = {
      url: url,
      fileName: fileName,
      title: title,
      content: content,
    };
  });

  return pages;
}
exports.allPages = allPages;

function postForPath(path) {
  return allPosts()[path];
}
exports.postForPath = postForPath;

function pageForPath(path) {
  return allPages()[path];
}
exports.pageForPath = pageForPath;

function pageReq() {
  return require.context('pages', false);
}
exports.pageReq = pageReq;

function postReq() {
  return require.context('posts', false, /^\.\/.*\.md$/);
}
exports.postReq = postReq;

function draftReq() {
  try {
    return require.context('drafts', false, /^\.\/.*\.md$/);
  }
  catch(e) {}
}
exports.draftReq = draftReq;

function renderContent(content) {
  return MdHelper.render(content);
}
exports.renderContent = renderContent;

function processPost(file, fileName) {
  // TODO: implement nicer hooks to configurable functions

  // clean the filename to get the url
  var url;
  if(themeFunctions.url) {
    url = themeFunctions.url(file, fileName);
  }
  else {
    // XXXXX: use a path opt to cut the ext instead?
    url = fileName.slice(0, fileName.length - 3);
  }

  // get the date from the file name if it's not in the frontmatter
  var date;
  if(themeFunctions.date) {
    date = themeFunctions.date(file, fileName);
  }
  else {
    date = file.date || fileName.slice(0, 10);
  }

  // get the content
  var content = MdHelper.render(file.__content);

  // generate the preview
  var preview;
  if(themeFunctions.preview) {
    preview = themeFunctions.preview(file, fileName);
  }
  else {
    preview = file.preview || MdHelper.getContentPreview(file.__content);
  }

  return _.assign({}, file, {
    url: url,
    content: content,
    date: date,
    preview: preview
  });
}
<|endoftext|>


<commit_before>var game = new Phaser.Game(1200, 600, Phaser.AUTO, 'game', { preload: preload, create: create, update: update, render: render });
var player;
var cursors;
var trees;
var seeds = [];
var anchor;

var tileSize = 32;
var stepSize = tileSize / 4;

var seaTiles = 10;

var terrainIterations = 6;
var islandTiles;

var islandWidth;
var seaWidth = seaTiles * tileSize;

var seaLevel = game.height - (game.height / 4);

var worldHeight = 1200;

var lines = [];

var gravity = 300;

var horizontalDrag = 500;

var terrainFrames = {
	slope1: [3, 7, 19, 23],
	slope2: [2, 6, 18, 22],
	slope3: [1, 5, 17, 21],
	slope4: [0, 4, 16, 20],
	full: [8, 12, 24, 28],
};

var terrainVariations = 4;

var treeVariations = 2;

var maxPlayerDistance = 0;
// 1 in n chance for a bush
var bushInvChance = 24;
var bushVariations = 24;

// the height is how tall we treat it as in-game, NOT how tall the sprite is
var treeHeight = 530;
// width is the same as sprite width, though
var treeWidth = 72;

var music;
var seedSound;

var treeGroup;
var islandGroup;
var islandNoPhysGroup;
var bushGroup;
var playerGroup;

function preload() {
	game.load.image('seed', 'assets/seed.png');
	game.load.spritesheet('player', 'assets/player.png', 64, 64);
	game.load.spritesheet('tree', 'assets/tree.png', treeWidth, 544);
	game.load.spritesheet('tree2', 'assets/tree2.png', treeWidth, 544);
	game.load.spritesheet('bush', 'assets/bush.png', 32, 32);
	game.load.spritesheet('terrain', 'assets/terrain.png', tileSize, tileSize);

	// invisible sprite just so we can get collision working
	game.load.image('collider', 'assets/physics_collider.png');

	game.load.audio('seed', ['assets/audio/seed.mp3', 'assets/audio/seed.ogg']);
	game.load.audio('bgm', ['assets/audio/bgm.mp3', 'assets/audio/bgm.ogg']);
}

function create() {
	trees = [];

	game.world.setBounds(-seaWidth, game.height - worldHeight, islandWidth + 2 * seaWidth, worldHeight);
	game.physics.startSystem(Phaser.Physics.ARCADE);

	game.stage.backgroundColor = 'rgb(0,0,255)';

	treeGroup = game.add.group();

	islandGroup = game.add.group();
	islandGroup.enableBody = true;

	islandNoPhysGroup = game.add.group();
	bushGroup = game.add.group();

	playerGroup = game.add.group();

	makeTerrain();

	player = playerGroup.create(0, 0, 'player');
	player.anchor.setTo(0.5, 0.5);
	player.scale.x = -1;
	player.animations.add('spin', [0,1,2,3,4,5,6,7], 10, true);

	game.physics.arcade.enable(player);
	player.body.gravity.y = gravity;
	player.body.collideWorldBounds = true;

	cursors = game.input.keyboard.createCursorKeys();

	game.input.mouse.mouseDownCallback = function(e) {
		if (e.button === Phaser.Mouse.RIGHT_BUTTON) {
			throwSeed();
		} else {
			console.log(game.input.mouse.button);
		}
	}
	document.getElementById('game').addEventListener('contextmenu', function(e) {
		e.preventDefault();
	});

	game.camera.follow(player);

	music = game.add.audio('bgm');
	music.loop = true;
	music.play();

	seedSound = game.add.audio('seed');
}

function makeTree(x, y) {
	var tree;
	switch (Math.floor(Math.random() * treeVariations)) {
			case 0:
				tree = treeGroup.create(x, y, 'tree');
				break;
			case 1:
				tree = treeGroup.create(x, y, 'tree2');
				break;
		}
	tree.animations.add('grow', [0, 1, 2, 3, 4, 5, 6], 4, false);
	tree.animations.play('grow');
	var center = tree.x + tree.width / 2;
	tree.trunk = new Phaser.Line(center, tree.y, center, game.height);
	trees.push(tree);

}

function throwSeed() {
	if (!seedSound.isPlaying) {
		seedSound.play();
	}

	var throwStrength = 500;

	// seeds are drawn at the player's z-depth
	var seed = playerGroup.create(player.body.x, player.body.y, 'seed');
	game.physics.arcade.enable(seed);
	seed.body.gravity.y = gravity;

	seed.body.velocity = new Phaser.Point(game.input.activePointer.worldX, game.input.activePointer.worldY).subtract(player.body.position.x, player.body.position.y)
		.setMagnitude(throwStrength).add(player.body.velocity.x, player.body.velocity.y);
	seeds.push(seed);
}

function update() {
	// iterate backwards because we'll be removing elements
	for (var i = seeds.length - 1; i >= 0; --i) {
		if (game.physics.arcade.overlap(seeds[i], islandGroup)) {
			var seed = seeds[i];
			makeTree(seed.body.position.x - treeWidth / 2, heightAt(seed.body.position.x) - treeHeight);
			seed.destroy();
			seeds.splice(i, 1);
		}
	}

	// we need to collide first, and THEN give the velocity a kick away from the collision site
	if (game.physics.arcade.collide(player, islandGroup)) {
		player.body.drag.x = horizontalDrag;
		if (!anchor) {
			player.animations.stop();
		}
	} else {
		player.body.drag.x = 0;
	}

	if (game.input.activePointer.leftButton.isDown) {
		console.log("pressed left");
		// check whether we've already got an anchor point
		if (anchor) {
			grapple();
		} else {
			var ray = new Phaser.Line(player.body.position.x, player.body.position.y, game.input.activePointer.worldX, game.input.activePointer.worldY);
			trees.forEach(function(tree) {
				var p = ray.intersects(tree.trunk, true); //returns intercept point
				if (p && (!anchor || p.distance(getMouseWorldPos()) < anchor.distance(getMouseWorldPos()))) {
					anchor = p;
				}
			});
			if (anchor) {
				grapple();
				player.animations.play('spin');
			}
		}
	} else {
		anchor = null;
	}

	if (player.x > maxPlayerDistance) {
		for (var x = maxPlayerDistance; x < player.x; ++x) {
			if (Math.random() * bushInvChance < 1) {
				spawnBush(x);
			}
		}
		maxPlayerDistance = player.x;
	}

	if (player.body.velocity.x < -1) {
		player.scale.x = 1;
	} else if (player.body.velocity.x > 1) {
		player.scale.x = -1;
	}
}

function spawnBush(x) {
	var height = heightAt(x);
	if (height === undefined) {
		return;
	}
	var bush = bushGroup.create(x, height + 10, 'bush');
	var variation = Math.floor(Math.random() * bushVariations);
	bush.frame = variation;

	var rotation = Math.floor(Math.random() * 4) * 90;
	bush.anchor.setTo(0.5, 0.5);
	bush.angle = rotation;

	var size = 4;
	bush.scale.x = size;
	bush.scale.y = size;
}

function grapple() {
	var lineStrength = 0.1;
	var airResistance = 0.005;

	var offset = anchor.clone().subtract(player.body.position.x, player.body.position.y);
	offset.multiply(lineStrength, lineStrength);
	player.body.velocity.add(offset.x, offset.y);
	player.body.velocity.multiply(1 - airResistance, 1 - airResistance);
}

function render() {
	lines.forEach(function(l) {
		game.debug.geom(l);
	});

	trees.forEach(function(t) {
		//game.debug.geom(t.trunk);
	});

	if (anchor) {
		game.debug.geom(new Phaser.Line(player.body.x, player.body.y, anchor.x, anchor.y));
	}
}
<commit_msg>Make tree variations easier to add<commit_after>var game = new Phaser.Game(1200, 600, Phaser.AUTO, 'game', { preload: preload, create: create, update: update, render: render });
var player;
var cursors;
var trees;
var seeds = [];
var anchor;

var tileSize = 32;
var stepSize = tileSize / 4;

var seaTiles = 10;

var terrainIterations = 6;
var islandTiles;

var islandWidth;
var seaWidth = seaTiles * tileSize;

var seaLevel = game.height - (game.height / 4);

var worldHeight = 1200;

var lines = [];

var gravity = 300;

var horizontalDrag = 500;

var terrainFrames = {
	slope1: [3, 7, 19, 23],
	slope2: [2, 6, 18, 22],
	slope3: [1, 5, 17, 21],
	slope4: [0, 4, 16, 20],
	full: [8, 12, 24, 28],
};

var terrainVariations = 4;

var treeVariations = 2;

var maxPlayerDistance = 0;
// 1 in n chance for a bush
var bushInvChance = 24;
var bushVariations = 24;

// the height is how tall we treat it as in-game, NOT how tall the sprite is
var treeHeight = 530;
// width is the same as sprite width, though
var treeWidth = 72;

var music;
var seedSound;

var treeGroup;
var islandGroup;
var islandNoPhysGroup;
var bushGroup;
var playerGroup;

function preload() {
	game.load.image('seed', 'assets/seed.png');
	game.load.spritesheet('player', 'assets/player.png', 64, 64);
	game.load.spritesheet('tree1', 'assets/tree.png', treeWidth, 544);
	game.load.spritesheet('tree2', 'assets/tree2.png', treeWidth, 544);
	game.load.spritesheet('bush', 'assets/bush.png', 32, 32);
	game.load.spritesheet('terrain', 'assets/terrain.png', tileSize, tileSize);

	// invisible sprite just so we can get collision working
	game.load.image('collider', 'assets/physics_collider.png');

	game.load.audio('seed', ['assets/audio/seed.mp3', 'assets/audio/seed.ogg']);
	game.load.audio('bgm', ['assets/audio/bgm.mp3', 'assets/audio/bgm.ogg']);
}

function create() {
	trees = [];

	game.world.setBounds(-seaWidth, game.height - worldHeight, islandWidth + 2 * seaWidth, worldHeight);
	game.physics.startSystem(Phaser.Physics.ARCADE);

	game.stage.backgroundColor = 'rgb(0,0,255)';

	treeGroup = game.add.group();

	islandGroup = game.add.group();
	islandGroup.enableBody = true;

	islandNoPhysGroup = game.add.group();
	bushGroup = game.add.group();

	playerGroup = game.add.group();

	makeTerrain();

	player = playerGroup.create(0, 0, 'player');
	player.anchor.setTo(0.5, 0.5);
	player.scale.x = -1;
	player.animations.add('spin', [0,1,2,3,4,5,6,7], 10, true);

	game.physics.arcade.enable(player);
	player.body.gravity.y = gravity;
	player.body.collideWorldBounds = true;

	cursors = game.input.keyboard.createCursorKeys();

	game.input.mouse.mouseDownCallback = function(e) {
		if (e.button === Phaser.Mouse.RIGHT_BUTTON) {
			throwSeed();
		} else {
			console.log(game.input.mouse.button);
		}
	}
	document.getElementById('game').addEventListener('contextmenu', function(e) {
		e.preventDefault();
	});

	game.camera.follow(player);

	music = game.add.audio('bgm');
	music.loop = true;
	music.play();

	seedSound = game.add.audio('seed');
}

function makeTree(x, y) {
	var variation = Math.floor(Math.random() * treeVariations) + 1;
	var tree = treeGroup.create(x, y, 'tree' + variation);
	tree.animations.add('grow', [0, 1, 2, 3, 4, 5, 6], 4, false);
	tree.animations.play('grow');
	var center = tree.x + tree.width / 2;
	tree.trunk = new Phaser.Line(center, tree.y, center, game.height);
	trees.push(tree);

}

function throwSeed() {
	if (!seedSound.isPlaying) {
		seedSound.play();
	}

	var throwStrength = 500;

	// seeds are drawn at the player's z-depth
	var seed = playerGroup.create(player.body.x, player.body.y, 'seed');
	game.physics.arcade.enable(seed);
	seed.body.gravity.y = gravity;

	seed.body.velocity = new Phaser.Point(game.input.activePointer.worldX, game.input.activePointer.worldY).subtract(player.body.position.x, player.body.position.y)
		.setMagnitude(throwStrength).add(player.body.velocity.x, player.body.velocity.y);
	seeds.push(seed);
}

function update() {
	// iterate backwards because we'll be removing elements
	for (var i = seeds.length - 1; i >= 0; --i) {
		if (game.physics.arcade.overlap(seeds[i], islandGroup)) {
			var seed = seeds[i];
			makeTree(seed.body.position.x - treeWidth / 2, heightAt(seed.body.position.x) - treeHeight);
			seed.destroy();
			seeds.splice(i, 1);
		}
	}

	// we need to collide first, and THEN give the velocity a kick away from the collision site
	if (game.physics.arcade.collide(player, islandGroup)) {
		player.body.drag.x = horizontalDrag;
		if (!anchor) {
			player.animations.stop();
		}
	} else {
		player.body.drag.x = 0;
	}

	if (game.input.activePointer.leftButton.isDown) {
		console.log("pressed left");
		// check whether we've already got an anchor point
		if (anchor) {
			grapple();
		} else {
			var ray = new Phaser.Line(player.body.position.x, player.body.position.y, game.input.activePointer.worldX, game.input.activePointer.worldY);
			trees.forEach(function(tree) {
				var p = ray.intersects(tree.trunk, true); //returns intercept point
				if (p && (!anchor || p.distance(getMouseWorldPos()) < anchor.distance(getMouseWorldPos()))) {
					anchor = p;
				}
			});
			if (anchor) {
				grapple();
				player.animations.play('spin');
			}
		}
	} else {
		anchor = null;
	}

	if (player.x > maxPlayerDistance) {
		for (var x = maxPlayerDistance; x < player.x; ++x) {
			if (Math.random() * bushInvChance < 1) {
				spawnBush(x);
			}
		}
		maxPlayerDistance = player.x;
	}

	if (player.body.velocity.x < -1) {
		player.scale.x = 1;
	} else if (player.body.velocity.x > 1) {
		player.scale.x = -1;
	}
}

function spawnBush(x) {
	var height = heightAt(x);
	if (height === undefined) {
		return;
	}
	var bush = bushGroup.create(x, height + 10, 'bush');
	var variation = Math.floor(Math.random() * bushVariations);
	bush.frame = variation;

	var rotation = Math.floor(Math.random() * 4) * 90;
	bush.anchor.setTo(0.5, 0.5);
	bush.angle = rotation;

	var size = 2;
	bush.scale.x = size;
	bush.scale.y = size;
}

function grapple() {
	var lineStrength = 0.1;
	var airResistance = 0.005;

	var offset = anchor.clone().subtract(player.body.position.x, player.body.position.y);
	offset.multiply(lineStrength, lineStrength);
	player.body.velocity.add(offset.x, offset.y);
	player.body.velocity.multiply(1 - airResistance, 1 - airResistance);
}

function render() {
	lines.forEach(function(l) {
		game.debug.geom(l);
	});

	trees.forEach(function(t) {
		//game.debug.geom(t.trunk);
	});

	if (anchor) {
		game.debug.geom(new Phaser.Line(player.body.x, player.body.y, anchor.x, anchor.y));
	}
}
<|endoftext|>


<commit_before>var os = require('os');
var path = require('path');
var async = require('async');
var methods = require('./lib');
var Cache = require('./lib/util/Cache');

function RegistryClient(config, logger) {
    config = config || {};
    this._config = config;
    this._logger = logger;

    // Parse config
    // Registry
    config.registry = config.registry || 'https://bower.herokuapp.com';
    if (typeof config.registry === 'string') {
        config.registry = {
            search: [config.registry],
            register: config.registry,
            publish: config.registry
        };
    } else if (!Array.isArray(config.registry.search)) {
        config.registry.search = [config.registry.search];
    }

    // Ensure that every registry endpoint does not end with /
    config.registry.search = config.registry.search.map(function (url) {
        return url.replace(/\/+$/, '');
    });
    config.registry.register = config.registry.register.replace(/\/+$/, '');
    config.registry.publish = config.registry.publish.replace(/\/+$/, '');

    // CA
    if (!config.ca || typeof config.ca === 'string') {
        config.ca = {
            search: [config.ca],
            register: config.ca,
            publish: config.ca
        };
    } else if (!Array.isArray(config.ca.search)) {
        config.ca.search = [config.ca.search];
    }

    // Cache
    if (!config.cache) {
        config.cache = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), 'bower-registry');
    }

    // Timeout
    if (typeof config.timeout !== 'number') {
        config.timeout = 5000;
    }

    // Strict ssl
    config.strictSsl = config.strictSsl == null ? true : !!config.strictSsl;

    // Init the cache
    this._initCache();
}

// Add every method to the prototype
RegistryClient.prototype.lookup = methods.lookup;
RegistryClient.prototype.search = methods.search;
RegistryClient.prototype.list = methods.list;
RegistryClient.prototype.register = methods.register;

RegistryClient.prototype.clearCache = function (name, callback) {
    async.parallel([
        this.lookup.clearCache.bind(this, name),
        this.search.clearCache.bind(this, name),
        this.list.clearCache.bind(this)
    ], callback);
};

RegistryClient.prototype.resetCache = function (name) {
    this.lookup.resetCache.call(this, name);
    this.search.resetCache.call(this, name);
    this.list.resetCache.call(this);

    return this;
};

RegistryClient.clearRuntimeCache = function () {
    Cache.clearRuntimeCache();
};

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

RegistryClient.prototype._initCache = function () {
    var cache;
    var dir = this._config.cache;

    // Cache is stored/retrieved statically to ensure singularity
    // among instances
    cache = this.constructor._cache = this.constructor._cache || {};
    this._cache = cache[dir] = cache[dir] || {};

    this.lookup.initCache.call(this);
    this.search.initCache.call(this);
    this.list.initCache.call(this);
};

module.exports = RegistryClient;
<commit_msg>Minor tweaks.<commit_after>var os = require('os');
var path = require('path');
var async = require('async');
var deepExtend = require('deep-extend');
var methods = require('./lib');
var Cache = require('./lib/util/Cache');

function RegistryClient(config, logger) {
    var bowerRegistry = 'https://bower.herokuapp.com';

    config = config || {};
    this._config = config;
    this._logger = logger;

    // Parse config
    // Registry
    config.registry = config.registry || bowerRegistry;
    if (typeof config.registry === 'string') {
        config.registry = {
            search: [config.registry],
            register: config.registry,
            publish: config.registry
        };
    } else {
        config.registry = deepExtend({
            search: bowerRegistry,
            register: bowerRegistry,
            publish: bowerRegistry
        }, config.registry);

        if (!Array.isArray(config.registry.search)) {
            config.registry.search = [config.registry.search];
        }
    }

    // Ensure that every registry endpoint does not end with /
    config.registry.search = config.registry.search.map(function (url) {
        return url.replace(/\/+$/, '');
    });
    config.registry.register = config.registry.register.replace(/\/+$/, '');
    config.registry.publish = config.registry.publish.replace(/\/+$/, '');

    // CA
    if (typeof config.ca === 'string') {
        config.ca = {
            search: [config.ca],
            register: config.ca,
            publish: config.ca
        };
    } else {
        config.ca = config.ca || {};

        if (config.ca.search && !Array.isArray(config.ca.search)) {
            config.ca.search = [config.ca.search];
        }
    }

    // Cache
    if (!config.cache) {
        config.cache = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), 'bower-registry');
    }

    // Timeout
    if (typeof config.timeout !== 'number') {
        config.timeout = 5000;
    }

    // Strict ssl
    config.strictSsl = config.strictSsl == null ? true : !!config.strictSsl;

    // Init the cache
    this._initCache();
}

// Add every method to the prototype
RegistryClient.prototype.lookup = methods.lookup;
RegistryClient.prototype.search = methods.search;
RegistryClient.prototype.list = methods.list;
RegistryClient.prototype.register = methods.register;

RegistryClient.prototype.clearCache = function (name, callback) {
    if (typeof name === 'function') {
        callback = name;
        name = null;
    }

    async.parallel([
        this.lookup.clearCache.bind(this, name),
        this.search.clearCache.bind(this, name),
        this.list.clearCache.bind(this)
    ], callback);
};

RegistryClient.prototype.resetCache = function (name) {
    this.lookup.resetCache.call(this, name);
    this.search.resetCache.call(this, name);
    this.list.resetCache.call(this);

    return this;
};

RegistryClient.clearRuntimeCache = function () {
    Cache.clearRuntimeCache();
};

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

RegistryClient.prototype._initCache = function () {
    var cache;
    var dir = this._config.cache;

    // Cache is stored/retrieved statically to ensure singularity
    // among instances
    cache = this.constructor._cache = this.constructor._cache || {};
    this._cache = cache[dir] = cache[dir] || {};

    this.lookup.initCache.call(this);
    this.search.initCache.call(this);
    this.list.initCache.call(this);
};

module.exports = RegistryClient;
<|endoftext|>


<commit_before>/*
 * Height fix for pages with .profile-info box
 */
function profileInfoHeightFix() {
  var pro_h = $('.profile-info').height()+210; //add 210 for the profile pic
  var main_h = $('#page .page-wrapper').height();

  if(pro_h > main_h){
    $('#page .page-wrapper').css({'min-height':pro_h});
  }
}

// Show a tab and hide the others. Load content from remote source if needed.
function activateSimpleTab( $heading_element ) {

  // Check that we have something to work with
  if ( ! $heading_element.size() ) return false;

  var tab_content_id = $heading_element.attr('rel');
  var $tab_content = $(tab_content_id);

  // If this tab is already open don't open it again
  if ( $tab_content.hasClass('open') )
    return true;

  // hide any currently active tabs
  $('#tab-nav ul li.active').removeClass('active');
  $('.tab.open').removeClass('open');
  $('.tab').not('.open').hide();

  // Show and activate the new tab
  $heading_element.addClass('active');
  $tab_content.addClass('open').show();

  // load content using ajax if the div has an data-tab-content-source-url
  // TODO: use a cleaner way to specify this - probably best to have a global object and tell it that certain tabs have special opening behaviour
  var content_url = $tab_content.attr('data-tab-content-source-url');
  if ( content_url ) {
      $tab_content.load(content_url);
  }
}

//generic re-usable hide or show with class states
//todo: add states to trigger elem if provided
function hideShow(elem, trig) {
  elem.toggleClass(function() {
    if ($(this).is('.open')) {
      $(this).hide().removeClass('open');
      trig.removeClass('active');
      return 'closed';
    } else {
      $(this).show().removeClass('closed');
      trig.addClass('active');
      return 'open';
    }
  });
}

$(function(){
  /*
   * auto complete
   */
    $('input.search-autocomplete-name')
    .autocomplete({
        source: "/search/autocomplete/",
        minLength: 2,
        select: function(event, ui) {
            if (ui.item) return window.location = ui.item.url;
        }
    });

    // hide/show home intro
    $('.home-read-more').on('click', function(e){
      e.preventDefault();
      hideShow($('#home-intro span.details'), $(this));
      if($('.home-read-more.active').length !== 0){
        $(this).text('less');
      }else{
        $(this).text('more');
      }
    });

    // auto-advance cycles through featured MPs; it also immediately replaces the
    // featured MP in the page (since we assume that has been frozen by caching)
    var auto_advance_enabled = false;
    var auto_advance_delay = 12000; // milliseconds
    var auto_advance_timeout = false;

    function transitionDiv(height) {
      return '

loading...

';
    }

    // important to delegate this (with on()) because the contents change each auto-advance
    $('#home-featured-person').on("click", '.feature-nav > a',
        function(e, is_auto_advancing){
          e.preventDefault();
          if (! is_auto_advancing) { // user clicked
            auto_advance_enabled = false;
            if (auto_advance_timeout) {
              clearTimeout(auto_advance_timeout);
            }
          }
          var m = $(this).attr('href').match(/(before|after)=([-\w]+)$/);
          if (m.length==3) { // wee sanity check: found direction [1] and slug [2]
            // fix the container height to stop content below jumping up when contents fadeOut
            $('#home-featured-person').css("height", $('#home-featured-person').height() + "px");
            $('.featured-person', '#home-featured-person').fadeOut('fast',
              function(){
                 $('#home-featured-person').html(transitionDiv())
                    .load(
                      "person/featured/" + m[1] + '/' + m[2],
                      function() {
                        $('#home-featured-person').css("height","auto");
                      }
                    );
              })
          }
        }
    );

    if (auto_advance_enabled) {
      $('#home-featured-person').html(transitionDiv()).load(
          'person/featured/' + Math.floor(Math.random()*100) // some random index of featured person
      );
      function auto_advance(){
        if (auto_advance_enabled){
          $('a.feature-next', '#home-featured-person').trigger("click", true);
          auto_advance_timeout = window.setTimeout(auto_advance, auto_advance_delay);
        }
      }
      auto_advance_timeout = window.setTimeout(auto_advance, auto_advance_delay);
    }

    /*
     * enable dialog based feedback links
     */
      $('a.feedback_link')
        .on(
            'click',
            function(event) {
                // Note - we could bail out here if the window is too small, as
                // we'd be on a mobile and it might be better just to send them to
                // the feedback page. Not done as this js should only be loaded on
                // a desktop.

                // don't follow the link to the feedback page.
                event.preventDefault();

                // create a div to use in the dialog
                var dialog_div = $('
Loading...
');

                // Load the initial content for the dialog
                dialog_div.load( event.target.href + ' #ajax_dialog_subcontent' );

                // Form subission should be done using ajax, and only the ajax_dialog_subcontent should be shown.
                var handle_form_submission = function( form_submit_event ) {
                    form_submit_event.preventDefault();
                    var form = $(form_submit_event.target);
                    form.ajaxSubmit({
                        success: function( responseText ) {
                            dialog_div.html( $(responseText).find('#ajax_dialog_subcontent') );
                        }
                    });
                };

                // catch all form submissions and do them using ajax
                dialog_div.on( 'submit', 'form', handle_form_submission );

                // Show the dialog
                dialog_div.dialog({
                  modal: true,
                  minHeight: 320,
                  width: 500,
                  title: 'Leave Feedback'
                });

            }
        );

  /*
   * simple tabs
   */

  // build the nav from the relavant links dotted around
  var $tabnavs = $('h2.tab-nav');
  $tabnavs.hide();
  $('.tab-wrapper').before('

');
  $tabnavs.each(function(){
    var rel = $(this).attr('rel');
    var txt = $(this).text();
    var href = $('a', this).attr('href');
    var aClass  = $(this).hasClass('tab-static-link')? 'class="tab-static-link"':'';
    var liClass = $(this).hasClass('tab-active')? 'tab-active':'';
    var newElem = '
'+txt+'
';
    $('#tab-nav ul').append(newElem);
  }).remove();

  // store the matched element from the hash here:
  // first look to see if it's already been marked server-side (tab-active)
  var matched_element = $('li.tab-active').first();

  // If not already active, if there is a hash try to load from that
  if(! matched_element.length && window.location.hash !== '') {
    var hash = window.location.hash;
    matched_element = $('li[rel='+hash+']');
  }

  // If there was no hash, or it didn't match, use the first one
  if ( ! matched_element.length ) {
    matched_element = $('li.tab-nav-heading').first();
  }

  // activate the tab
  activateSimpleTab(matched_element);
  profileInfoHeightFix();

  //for clicks
  $("#tab-nav ul li a").not(".tab-static-link").click(function(e){
    e.preventDefault();
    window.location.hash = $(this).parent('li').attr('rel');
    activateSimpleTab($(this).parent('li'));
    profileInfoHeightFix();
  });

  // $(".tab-static-link").click(function(e){
  //   var hash = $(this).attr('rel');
  //   window.location.hash = hash;
  //   e.preventDefault();
  //   activateSimpleTab($(this));
  //   $("#tab-nav ul li[rel='"+hash+"']").addClass('active');
  // });

  /*
   * scorecard
   */
  // prep
  $('div.details').hide();

  // hide/show details
  $('ul.scorecard article.has-details').live('click', function(){
    hideShow($('div.details', $(this)), $(this));
  });

  /*
   * Comments: hover and show tools
   */
  $('.comments li').hover(function() {
    $(this).addClass('hovered');
  },
  function() {
    $(this).removeClass('hovered');
  });

  /*
   * Login box
   */
  var login_target = $('#site-user-tools .login a').attr('href');

  $('#site-user-tools').after('
Loading...
');

  $('li.login').on('click', function(event) {
    event.preventDefault();
    $('#login-box').load(login_target + ' #login');
    hideShow($('#login-box'), $(this));
  });

  /*
   * Main nav hover
   */
  $('#main-menu ul > li').on('mouseover', function(){
    if($(this).children('ul').length === 1){
      // alert('oh yeah');
      $(this).addClass('active');
    }
  }).on('mouseout', function(){
    $(this).removeClass('active');
  });

  /* carry search terms across when switching between search pages */
  $("#search-hansard-instead").click(function(e){
    e.preventDefault();
    location.href="/search/hansard?q=" + escape($('#core-search,#id_q,#loc').first().val());
  });
  $("#search-core-instead").click(function(e){
    e.preventDefault();
    location.href="/search?q=" + escape($('#id_q,#loc').first().val());
  });
});<commit_msg>Handle menu taps on tablets (iPads only?) - fixes #366<commit_after>/*
 * Height fix for pages with .profile-info box
 */
function profileInfoHeightFix() {
  var pro_h = $('.profile-info').height()+210; //add 210 for the profile pic
  var main_h = $('#page .page-wrapper').height();

  if(pro_h > main_h){
    $('#page .page-wrapper').css({'min-height':pro_h});
  }
}

// Show a tab and hide the others. Load content from remote source if needed.
function activateSimpleTab( $heading_element ) {

  // Check that we have something to work with
  if ( ! $heading_element.size() ) return false;

  var tab_content_id = $heading_element.attr('rel');
  var $tab_content = $(tab_content_id);

  // If this tab is already open don't open it again
  if ( $tab_content.hasClass('open') )
    return true;

  // hide any currently active tabs
  $('#tab-nav ul li.active').removeClass('active');
  $('.tab.open').removeClass('open');
  $('.tab').not('.open').hide();

  // Show and activate the new tab
  $heading_element.addClass('active');
  $tab_content.addClass('open').show();

  // load content using ajax if the div has an data-tab-content-source-url
  // TODO: use a cleaner way to specify this - probably best to have a global object and tell it that certain tabs have special opening behaviour
  var content_url = $tab_content.attr('data-tab-content-source-url');
  if ( content_url ) {
      $tab_content.load(content_url);
  }
}

//generic re-usable hide or show with class states
//todo: add states to trigger elem if provided
function hideShow(elem, trig) {
  elem.toggleClass(function() {
    if ($(this).is('.open')) {
      $(this).hide().removeClass('open');
      trig.removeClass('active');
      return 'closed';
    } else {
      $(this).show().removeClass('closed');
      trig.addClass('active');
      return 'open';
    }
  });
}

$(function(){
  /*
   * auto complete
   */
    $('input.search-autocomplete-name')
    .autocomplete({
        source: "/search/autocomplete/",
        minLength: 2,
        select: function(event, ui) {
            if (ui.item) return window.location = ui.item.url;
        }
    });

    // hide/show home intro
    $('.home-read-more').on('click', function(e){
      e.preventDefault();
      hideShow($('#home-intro span.details'), $(this));
      if($('.home-read-more.active').length !== 0){
        $(this).text('less');
      }else{
        $(this).text('more');
      }
    });

    // auto-advance cycles through featured MPs; it also immediately replaces the
    // featured MP in the page (since we assume that has been frozen by caching)
    var auto_advance_enabled = false;
    var auto_advance_delay = 12000; // milliseconds
    var auto_advance_timeout = false;

    function transitionDiv(height) {
      return '

loading...

';
    }

    // important to delegate this (with on()) because the contents change each auto-advance
    $('#home-featured-person').on("click", '.feature-nav > a',
        function(e, is_auto_advancing){
          e.preventDefault();
          if (! is_auto_advancing) { // user clicked
            auto_advance_enabled = false;
            if (auto_advance_timeout) {
              clearTimeout(auto_advance_timeout);
            }
          }
          var m = $(this).attr('href').match(/(before|after)=([-\w]+)$/);
          if (m.length==3) { // wee sanity check: found direction [1] and slug [2]
            // fix the container height to stop content below jumping up when contents fadeOut
            $('#home-featured-person').css("height", $('#home-featured-person').height() + "px");
            $('.featured-person', '#home-featured-person').fadeOut('fast',
              function(){
                 $('#home-featured-person').html(transitionDiv())
                    .load(
                      "person/featured/" + m[1] + '/' + m[2],
                      function() {
                        $('#home-featured-person').css("height","auto");
                      }
                    );
              })
          }
        }
    );

    if (auto_advance_enabled) {
      $('#home-featured-person').html(transitionDiv()).load(
          'person/featured/' + Math.floor(Math.random()*100) // some random index of featured person
      );
      function auto_advance(){
        if (auto_advance_enabled){
          $('a.feature-next', '#home-featured-person').trigger("click", true);
          auto_advance_timeout = window.setTimeout(auto_advance, auto_advance_delay);
        }
      }
      auto_advance_timeout = window.setTimeout(auto_advance, auto_advance_delay);
    }

    /*
     * enable dialog based feedback links
     */
      $('a.feedback_link')
        .on(
            'click',
            function(event) {
                // Note - we could bail out here if the window is too small, as
                // we'd be on a mobile and it might be better just to send them to
                // the feedback page. Not done as this js should only be loaded on
                // a desktop.

                // don't follow the link to the feedback page.
                event.preventDefault();

                // create a div to use in the dialog
                var dialog_div = $('
Loading...
');

                // Load the initial content for the dialog
                dialog_div.load( event.target.href + ' #ajax_dialog_subcontent' );

                // Form subission should be done using ajax, and only the ajax_dialog_subcontent should be shown.
                var handle_form_submission = function( form_submit_event ) {
                    form_submit_event.preventDefault();
                    var form = $(form_submit_event.target);
                    form.ajaxSubmit({
                        success: function( responseText ) {
                            dialog_div.html( $(responseText).find('#ajax_dialog_subcontent') );
                        }
                    });
                };

                // catch all form submissions and do them using ajax
                dialog_div.on( 'submit', 'form', handle_form_submission );

                // Show the dialog
                dialog_div.dialog({
                  modal: true,
                  minHeight: 320,
                  width: 500,
                  title: 'Leave Feedback'
                });

            }
        );

  /*
   * simple tabs
   */

  // build the nav from the relavant links dotted around
  var $tabnavs = $('h2.tab-nav');
  $tabnavs.hide();
  $('.tab-wrapper').before('

');
  $tabnavs.each(function(){
    var rel = $(this).attr('rel');
    var txt = $(this).text();
    var href = $('a', this).attr('href');
    var aClass  = $(this).hasClass('tab-static-link')? 'class="tab-static-link"':'';
    var liClass = $(this).hasClass('tab-active')? 'tab-active':'';
    var newElem = '
'+txt+'
';
    $('#tab-nav ul').append(newElem);
  }).remove();

  // store the matched element from the hash here:
  // first look to see if it's already been marked server-side (tab-active)
  var matched_element = $('li.tab-active').first();

  // If not already active, if there is a hash try to load from that
  if(! matched_element.length && window.location.hash !== '') {
    var hash = window.location.hash;
    matched_element = $('li[rel='+hash+']');
  }

  // If there was no hash, or it didn't match, use the first one
  if ( ! matched_element.length ) {
    matched_element = $('li.tab-nav-heading').first();
  }

  // activate the tab
  activateSimpleTab(matched_element);
  profileInfoHeightFix();

  //for clicks
  $("#tab-nav ul li a").not(".tab-static-link").click(function(e){
    e.preventDefault();
    window.location.hash = $(this).parent('li').attr('rel');
    activateSimpleTab($(this).parent('li'));
    profileInfoHeightFix();
  });

  // $(".tab-static-link").click(function(e){
  //   var hash = $(this).attr('rel');
  //   window.location.hash = hash;
  //   e.preventDefault();
  //   activateSimpleTab($(this));
  //   $("#tab-nav ul li[rel='"+hash+"']").addClass('active');
  // });

  /*
   * scorecard
   */
  // prep
  $('div.details').hide();

  // hide/show details
  $('ul.scorecard article.has-details').live('click', function(){
    hideShow($('div.details', $(this)), $(this));
  });

  /*
   * Comments: hover and show tools
   */
  $('.comments li').hover(function() {
    $(this).addClass('hovered');
  },
  function() {
    $(this).removeClass('hovered');
  });

  /*
   * Login box
   */
  var login_target = $('#site-user-tools .login a').attr('href');

  $('#site-user-tools').after('
Loading...
');

  $('li.login').on('click', function(event) {
    event.preventDefault();
    $('#login-box').load(login_target + ' #login');
    hideShow($('#login-box'), $(this));
  });

  /*
   * Main nav hover
   */
  $('#main-menu ul > li')
    .on( 'mouseover', function () {
        var $menu_heading = $(this);

        if($menu_heading.children('ul').length === 1){

            // close other menus that might be open
            $menu_heading.siblings('.active').trigger('mouseout');

            $menu_heading.addClass('active');
        }
    })
    .on( 'mouseout', function () {
        $(this).removeClass('active');
    })
    .on ('touchstart', function ( e ) {
        // If we are not active then this is probably a tap on a tablet. Trigger a
        // mouseover event instead.
        var $menu_heading = $(this);

        if ( $menu_heading.children('ul').length === 1 && ! $menu_heading.hasClass('active') ) {
            e.preventDefault();                 // don't follow through with a click
            $menu_heading.trigger('mouseover'); // show the menu
            // TODO - some how cancel the menu - atm it'll stay up now until you navigate away from page or click on another menu heading
        }
    });

  /* carry search terms across when switching between search pages */
  $("#search-hansard-instead").click(function(e){
    e.preventDefault();
    location.href="/search/hansard?q=" + escape($('#core-search,#id_q,#loc').first().val());
  });
  $("#search-core-instead").click(function(e){
    e.preventDefault();
    location.href="/search?q=" + escape($('#id_q,#loc').first().val());
  });
});<|endoftext|>


<commit_before>require.config({
    baseUrl: "js",
});

define(
// Requirements
["map", "mini-map", "player", "first-person", "lib/undo-manager", "lib/jquery"],

// Module definition
function (Map, MiniMap, Player, FirstPerson, UndoManager) {

    // START MAP + MINI-MAP DEBUG CODE
    var map = new Map();
    var miniMap = new MiniMap({
        container: $( $(".mini-map")[0] ),
        viewRadius: 5,
        cellSize: 10
    });

    var player = new Player();

    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);

    var firstPerson = new FirstPerson({
        container: $( $(".first-person")[0] )
    });
    firstPerson.setPerspective(map.getTilesInArea(player.x, player.y, 1));

    // END MAP + MINI-MAP DEBUG CODE

    /**
     * Return a 2D Array of tiles that represents the player's current
     * perspective.
     */
    function _getPlayerPerspective () {

        var tiles = map.getTilesInArea(player.x + player.bearing[0],
                                       player.y + player.bearing[1]);

        // Rotate array to the right.
        function rotate (arr){
            var temp = new Array(arr.length);
            var i, j;
            for(i = 0; i < temp.length; ++i){
            temp[i] = new Array(temp.length);
            for (j = 0; j < temp.length; ++j){
            temp[i][j] = arr[temp.length - j - 1][i];
            }
            }
            arr = temp;
        }

        var playerTile = tiles[1][2], safety = 0;
        while (playerTile.x != player.x || playerTile.y != player.y) {
            rotate(tiles);

            // A safety clause to make sure that my hastily written code doesn't
            // break FireFox.  I know, I know... I'm worse than Hitler.
            safety++;
            if (safety > 3) break;
        }

        return tiles;
    }

    /**
     * Check to see if the player can move in the given direction and return
     * a Boolean representing success.
     */
    function _checkMove (bearing) {
        var tiles  = map.getTilesInArea(player.x, player.y, 1);

        var tx = 1 + bearing[0],
            ty = 1 + bearing[1],
            target = tiles[tx][ty];

        if (target.passable) {
            return true;
        } else {
            return false;
        }
    }

    // Handle keyboard input for movement.
    $(window).keydown(function(e) {
        switch (e.which) {
            // Left arrow
            case 37:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move left.");

                if (_checkMove([-1,0])) {
                    player.bearLeft();
                    player.move();
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                }

                break;
            // Up arrow
            case 38:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move forward.");

                if (_checkMove([0,-1])) {
                    player.move();
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                }

                break;
            // Right arrow
            case 39:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move right.");

                if (_checkMove([1,0])) {
                    player.bearRight();
                    player.move();
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                }

                break;
            // Down arrow
            case 40:
                e.preventDefault(); // Prevent scrolling the window.
                // Not used
                break;
            default:
                break;
        }
    });

    // Handle mousewheel input for traversing movement history.
    $(window).on('mousewheel DOMMouseScroll',function(e){
        // To catch up or down scroll:
        e = e.originalEvent;
        // delta == 1 = down, delta == -1 = up;
        var delta = e.wheelDelta > 0 || e.detail < 0 ? 1 : -1;

        switch (delta) {
            // Scroll up
            case 1:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle redo.");
                break;
            // Scroll down
            case -1:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle undo.");
                break;
            default:
                break;
        }
    });
});<commit_msg>Add bullshit key handlers.<commit_after>require.config({
    baseUrl: "js",
});

define(
// Requirements
["map", "mini-map", "player", "first-person", "lib/undo-manager", "lib/jquery"],

// Module definition
function (Map, MiniMap, Player, FirstPerson, UndoManager) {

    // START MAP + MINI-MAP DEBUG CODE
    var map = new Map();
    var miniMap = new MiniMap({
        container: $( $(".mini-map")[0] ),
        viewRadius: 5,
        cellSize: 10
    });

    var player = new Player();

    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);

    var firstPerson = new FirstPerson({
        container: $( $(".first-person")[0] )
    });
    firstPerson.setPerspective(map.getTilesInArea(player.x, player.y, 1));

    // END MAP + MINI-MAP DEBUG CODE

    /**
     * Return a 2D Array of tiles that represents the player's current
     * perspective.
     */
    function _getPlayerPerspective () {
        var tiles = map.getTilesInArea(player.x + player.bearing[0],
                                       player.y + player.bearing[1],
                                       1);

        // Rotate array to the right.
        function rotate (arr){
            var temp = new Array(arr.length);
            var i, j;
            for(i = 0; i < temp.length; ++i){
            temp[i] = new Array(temp.length);
            for (j = 0; j < temp.length; ++j){
            temp[i][j] = arr[temp.length - j - 1][i];
            }
            }
            arr = temp;
        }

        var playerTile = tiles[1][2], safety = 0;
        while (playerTile.x != player.x || playerTile.y != player.y) {
            rotate(tiles);

            // A safety clause to make sure that my hastily written code doesn't
            // break FireFox.  I know, I know... I'm worse than Hitler.
            safety++;
            if (safety > 3) break;
        }

        return tiles;
    }

    /**
     * Check to see if the player can move in the given direction and return
     * a Boolean representing success.
     */
    function _checkMove (bearing) {
        var tiles  = map.getTilesInArea(player.x, player.y, 1);

        var tx = 1 + bearing[0],
            ty = 1 + bearing[1],
            target = tiles[tx][ty];

        if (target.passable) {
            return true;
        } else {
            return false;
        }
    }

    // Handle keyboard input for movement.
    $(window).keydown(function(e) {
        switch (e.which) {
            // Left arrow
            case 37:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move left.");

                player.bearLeft();
                if (_checkMove(player.bearing)) {
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                    firstPerson.setPerspective(_getPlayerPerspective());
                }

                break;
            // Up arrow
            case 38:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move forward.");

                if (_checkMove(player.bearing)) {
                    player.move();
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                    firstPerson.setPerspective(_getPlayerPerspective());
                }

                break;
            // Right arrow
            case 39:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle move right.");

                player.bearRight();
                if (_checkMove(player.bearing)) {
                    miniMap.draw(map.getTilesInArea(player.x, player.y, 5), player.bearing);
                    firstPerson.setPerspective(_getPlayerPerspective());
                }

                break;
            // Down arrow
            case 40:
                e.preventDefault(); // Prevent scrolling the window.
                // Not used
                break;
            default:
                break;
        }
    });

    // Handle mousewheel input for traversing movement history.
    $(window).on('mousewheel DOMMouseScroll',function(e){
        // To catch up or down scroll:
        e = e.originalEvent;
        // delta == 1 = down, delta == -1 = up;
        var delta = e.wheelDelta > 0 || e.detail < 0 ? 1 : -1;

        switch (delta) {
            // Scroll up
            case 1:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle redo.");
                break;
            // Scroll down
            case -1:
                e.preventDefault(); // Prevent scrolling the window.
                console.log("TODO: Handle undo.");
                break;
            default:
                break;
        }
    });
});<|endoftext|>


<commit_before>import {Observable as O} from 'rx';
import I from 'immutable';

const ownerElement = (rootElement) =>
  rootElement.observable
  .map((e) => e[0])
  .distinctUntilChanged(
    (e) => e,
    (a, b) => a === b
  ).shareReplay(1)
;

// get the id of the given touch
const touchId = (touch) => touch.identifier;

// get a stream of current touch ids
const toucheIds = (globalEvents, rootElement) => O.merge([
  rootElement.events('touchstart').map(
    (e) => (ids) => ids.union(
      Array.prototype.map.call(e.changedTouches, touchId)
    )
  ),
  globalEvents.events('touchend').map(
    (e) => (ids) => ids.subtract(
      Array.prototype.map.call(e.changedTouches, touchId)
    )
  ),
]).scan(
  (ids, modFn) => modFn(ids),
  I.Set()
).distinctUntilChanged(
  (e) => e,
  (a, b) => a === b
).share();

// get the center point of the given touches
const touchCenter = (touches) => {
  const length = touches.length;
  const center = Array.prototype.reduce.call(touches, (sum, touch) => ({
    x: sum.x + touch.pageX / length,
    y: sum.y + touch.pageY / length,
  }), {x: 0, y: 0});

  return center;
};

// get the avarage radius of the touches around their center
const touchRadius = (touches) => {
  const length = touches.length;
  const {x,y} = touchCenter(touches);

  return touches.reduce((sum, touch) =>
    sum +
    Math.sqrt(
      Math.pow(touch.pageX - x, 2) +
      Math.pow(touch.pageY - y, 2)
    )
  , 0) / length;
};

// Return only the touches with the given ids.
const filterTouches = (ids, touches) =>
  Array.prototype.filter.call(
    touches,
    (touch) => ids.contains(touch.identifier)
  )
;

// get the zoom factor for the given wheel event
const wheelFactor = (wheelEvent) => {
  const wheel = wheelEvent.deltaY / -40;
  return Math.pow(
    1 + Math.abs(wheel) / 2,
    wheel > 0 ? 1 : -1
  );
};

const touchZoom = ({
  pinchStart$, pinchMove$, pinchEnd$, owner$, positionFn,
}) =>
  pinchStart$
  .map((ids) =>
    pinchMove$
    .withLatestFrom(owner$, (moveEvt, owner) => {
      const touches = filterTouches(ids, moveEvt.touches);
      const {x, y} = positionFn(touchCenter(touches), owner);
      return {
        distance: touchRadius(touches),
        pivot: {x, y},
        factor: 1,
      };
    })
    .scan((prev, current) => ({
      pivot: current.pivot,
      distance: current.distance,
      factor: current.distance / prev.distance,
    }))
    .takeUntil(pinchEnd$)
    .share()
  ).switch()
;

const wheelZoom = ({wheel$, owner$, positionFn}) =>
  wheel$.withLatestFrom(owner$, (evt, owner) => {
    const pivot = positionFn({
      x: evt.pageX,
      y: evt.pageY,
    }, owner);

    const factor = wheelFactor(evt);

    return {
      factor,
      pivot,
    };
  }).share()
;

// interpret touch and scroll events as pinch
export const zoom = (globalEvents, rootElement, positionFn) => {
  const owner$ = ownerElement(rootElement);

  const touches$ = toucheIds(globalEvents, rootElement);
  const pinchStart$ = touches$.filter((ids) => ids.size === 2);
  const pinchEnd$ = touches$.filter((ids) => ids.size !== 2);

  const wheel$ = rootElement.events('wheel')
    .filter((evt) => !evt.altKey)
    .share();

  const pinchMove$ = globalEvents
    .events('touchmove');

  const touchAction$ = touchZoom({
    pinchStart$, pinchMove$, pinchEnd$,
    owner$, positionFn,
  });

  const wheelAction$ = wheelZoom({wheel$, owner$, positionFn});

  return {
    action$: O.merge([
      touchAction$,
      wheelAction$,
    ]).share(),
    stopPropagation: O.empty(),
    preventDefault: wheel$,
  };
};

const touchPan = ({
  touchChange$, panStart$, panMove$, panEnd$, owner$, positionFn,
}) =>
  panStart$
  .withLatestFrom(touchChange$, owner$, (ids, initial, owner) => {
    const {x: startX, y: startY} = positionFn(
      touchCenter(filterTouches(ids, initial.touches)), owner
    );
    return panMove$.map((move) => positionFn(
      touchCenter(filterTouches(ids, move.touches)),
      owner
    ))
    .map((target) => ({
      x: target.x - startX,
      y: target.y - startY,
    }))
    .takeUntil(panEnd$)
    .share();
  }).switch()
;

const mousePan = ({
  mouseDown$, mouseMove$, mouseUp$, owner$, positionFn,
}) =>
  mouseDown$
  .withLatestFrom(owner$, (downEvt, owner) => {
    const {x: startX, y: startY} = positionFn(
      {x: downEvt.pageX, y: downEvt.pageY}, owner
    );
    return mouseMove$.map((move) => positionFn(
      {x: move.pageX, y: move.pageY},
      owner
    ))
    .map((target) => ({
      x: target.x - startX,
      y: target.y - startY,
    }))
    .takeUntil(mouseUp$)
    .share();
  }).switch()
;

// interpret drag and touch events as pan
export const pan = (globalEvents, rootElement, positionFn) => {
  const owner$ = ownerElement(rootElement);

  const touchChange$ = O.merge([
    rootElement.events('touchstart'),
    globalEvents.events('touchend'),
  ]).share();

  const touches$ = toucheIds(globalEvents, rootElement);
  const panStart$ = touches$.filter((ids) => ids.size > 0);
  const panEnd$ = touches$.filter((ids) => ids.size <= 0);
  const panMove$ = globalEvents.events('touchmove');

  const mouseDown$ = rootElement.events('mousedown');
  const mouseMove$ = globalEvents.events('mousemove');
  const mouseUp$ = globalEvents.events('mouseup');

  const touchAction$ = touchPan({
    touchChange$, panStart$, panMove$, panEnd$, owner$, positionFn,
  });

  const mouseAction$ = mousePan({
    mouseDown$, mouseMove$, mouseUp$, owner$, positionFn,
  });

  const preventDefault = O.merge([
    panStart$.map(() =>
      panMove$.takeUntil(panEnd$)
    ).switch(),
    mouseDown$.map(() =>
      mouseMove$.takeUntil(mouseUp$)
    ).switch(),
  ]).share();

  return {
    action$: O.merge([
      touchAction$,
      mouseAction$,
    ]).share(),
    stopPropagation: O.empty(),
    preventDefault,
  };
};
<commit_msg>allow panning only with primary mouse button<commit_after>import {Observable as O} from 'rx';
import I from 'immutable';

const ownerElement = (rootElement) =>
  rootElement.observable
  .map((e) => e[0])
  .distinctUntilChanged(
    (e) => e,
    (a, b) => a === b
  ).shareReplay(1)
;

// get the id of the given touch
const touchId = (touch) => touch.identifier;

// http://stackoverflow.com/a/12737882/1533291
const isPrimaryMouseButton = (event) => {
  if ('buttons' in event) {
    return event.buttons === 1;
  } else if ('which' in event) {
    return event.which === 1;
  } else {
    return event.button === 1;
  }
};

// get a stream of current touch ids
const toucheIds = (globalEvents, rootElement) => O.merge([
  rootElement.events('touchstart').map(
    (e) => (ids) => ids.union(
      Array.prototype.map.call(e.changedTouches, touchId)
    )
  ),
  globalEvents.events('touchend').map(
    (e) => (ids) => ids.subtract(
      Array.prototype.map.call(e.changedTouches, touchId)
    )
  ),
]).scan(
  (ids, modFn) => modFn(ids),
  I.Set()
).distinctUntilChanged(
  (e) => e,
  (a, b) => a === b
).share();

// get the center point of the given touches
const touchCenter = (touches) => {
  const length = touches.length;
  const center = Array.prototype.reduce.call(touches, (sum, touch) => ({
    x: sum.x + touch.pageX / length,
    y: sum.y + touch.pageY / length,
  }), {x: 0, y: 0});

  return center;
};

// get the avarage radius of the touches around their center
const touchRadius = (touches) => {
  const length = touches.length;
  const {x,y} = touchCenter(touches);

  return touches.reduce((sum, touch) =>
    sum +
    Math.sqrt(
      Math.pow(touch.pageX - x, 2) +
      Math.pow(touch.pageY - y, 2)
    )
  , 0) / length;
};

// Return only the touches with the given ids.
const filterTouches = (ids, touches) =>
  Array.prototype.filter.call(
    touches,
    (touch) => ids.contains(touch.identifier)
  )
;

// get the zoom factor for the given wheel event
const wheelFactor = (wheelEvent) => {
  const wheel = wheelEvent.deltaY / -40;
  return Math.pow(
    1 + Math.abs(wheel) / 2,
    wheel > 0 ? 1 : -1
  );
};

const touchZoom = ({
  pinchStart$, pinchMove$, pinchEnd$, owner$, positionFn,
}) =>
  pinchStart$
  .map((ids) =>
    pinchMove$
    .withLatestFrom(owner$, (moveEvt, owner) => {
      const touches = filterTouches(ids, moveEvt.touches);
      const {x, y} = positionFn(touchCenter(touches), owner);
      return {
        distance: touchRadius(touches),
        pivot: {x, y},
        factor: 1,
      };
    })
    .scan((prev, current) => ({
      pivot: current.pivot,
      distance: current.distance,
      factor: current.distance / prev.distance,
    }))
    .takeUntil(pinchEnd$)
    .share()
  ).switch()
;

const wheelZoom = ({wheel$, owner$, positionFn}) =>
  wheel$.withLatestFrom(owner$, (evt, owner) => {
    const pivot = positionFn({
      x: evt.pageX,
      y: evt.pageY,
    }, owner);

    const factor = wheelFactor(evt);

    return {
      factor,
      pivot,
    };
  }).share()
;

// interpret touch and scroll events as pinch
export const zoom = (globalEvents, rootElement, positionFn) => {
  const owner$ = ownerElement(rootElement);

  const touches$ = toucheIds(globalEvents, rootElement);
  const pinchStart$ = touches$.filter((ids) => ids.size === 2);
  const pinchEnd$ = touches$.filter((ids) => ids.size !== 2);

  const wheel$ = rootElement.events('wheel')
    .filter((evt) => !evt.altKey)
    .share();

  const pinchMove$ = globalEvents
    .events('touchmove');

  const touchAction$ = touchZoom({
    pinchStart$, pinchMove$, pinchEnd$,
    owner$, positionFn,
  });

  const wheelAction$ = wheelZoom({wheel$, owner$, positionFn});

  return {
    action$: O.merge([
      touchAction$,
      wheelAction$,
    ]).share(),
    stopPropagation: O.empty(),
    preventDefault: wheel$,
  };
};

const touchPan = ({
  touchChange$, panStart$, panMove$, panEnd$, owner$, positionFn,
}) =>
  panStart$
  .withLatestFrom(touchChange$, owner$, (ids, initial, owner) => {
    const {x: startX, y: startY} = positionFn(
      touchCenter(filterTouches(ids, initial.touches)), owner
    );
    return panMove$.map((move) => positionFn(
      touchCenter(filterTouches(ids, move.touches)),
      owner
    ))
    .map((target) => ({
      x: target.x - startX,
      y: target.y - startY,
    }))
    .takeUntil(panEnd$)
    .share();
  }).switch()
;

const mousePan = ({
  mouseDown$, mouseMove$, mouseUp$, owner$, positionFn,
}) =>
  mouseDown$
  .withLatestFrom(owner$, (downEvt, owner) => {
    const {x: startX, y: startY} = positionFn(
      {x: downEvt.pageX, y: downEvt.pageY}, owner
    );
    return mouseMove$.map((move) => positionFn(
      {x: move.pageX, y: move.pageY},
      owner
    ))
    .map((target) => ({
      x: target.x - startX,
      y: target.y - startY,
    }))
    .takeUntil(mouseUp$)
    .share();
  }).switch()
;

// interpret drag and touch events as pan
export const pan = (globalEvents, rootElement, positionFn) => {
  const owner$ = ownerElement(rootElement);

  const touchChange$ = O.merge([
    rootElement.events('touchstart'),
    globalEvents.events('touchend'),
  ]).share();

  const touches$ = toucheIds(globalEvents, rootElement);
  const panStart$ = touches$.filter((ids) => ids.size > 0);
  const panEnd$ = touches$.filter((ids) => ids.size <= 0);
  const panMove$ = globalEvents.events('touchmove');

  const mouseDown$ = rootElement.events('mousedown')
    .filter(isPrimaryMouseButton);
  const mouseMove$ = globalEvents.events('mousemove');
  const mouseUp$ = globalEvents.events('mouseup');

  const touchAction$ = touchPan({
    touchChange$, panStart$, panMove$, panEnd$, owner$, positionFn,
  });

  const mouseAction$ = mousePan({
    mouseDown$, mouseMove$, mouseUp$, owner$, positionFn,
  });

  const preventDefault = O.merge([
    panStart$.map(() =>
      panMove$.takeUntil(panEnd$)
    ).switch(),
    mouseDown$.map(() =>
      mouseMove$.takeUntil(mouseUp$)
    ).switch(),
  ]).share();

  return {
    action$: O.merge([
      touchAction$,
      mouseAction$,
    ]).share(),
    stopPropagation: O.empty(),
    preventDefault,
  };
};
<|endoftext|>