<commit_before>var querry, editor, map;
var style_file = "styles/default.yaml";
var style_content = "";

// TOOLS
//----------------------------------------------
function fetchHTTP(url, methood){
    var request = new XMLHttpRequest(), response;

    request.onreadystatechange = function () {
        if (request.readyState === 4 && request.status === 200) {
            response = request.responseText;
        }
    }
    request.open(methood ? methood : 'GET', url, false);
    request.send();
    return response;
}

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

function parseQuery(qstr){
  var query = {};
  var a = qstr.split('&');
  for (var i in a){
    var b = a[i].split('=');
    query[decodeURIComponent(b[0])] = decodeURIComponent(b[1]);
  }

  return query;
}

function isNumber(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); }

function jumpToLine(i) {
    var t = editor.charCoords({line: i-1, ch: 0}, "local").top;
    // var middleHeight = editor.getScrollerElement().offsetHeight / 2;
    editor.scrollTo(null, t);// - middleHeight - 5);
}

function selectLine( nLine ){
    if (editor) {
        editor.setSelection({ line: nLine, ch:0},
                            { line: nLine, ch:editor.lineInfo(nLine).text.length } );
    }
}

function selectLines( _string ){
    if (editor) {
        if ( isNumber(_string) ){
            selectLine(parseInt(_string)-1);
            jumpToLine(parseInt(_string)-1);
        } else {
            var lines = _string.split('-');
            var from = parseInt(lines[0])-1;
            var to = parseInt(lines[1])-1;

            editor.setSelection({ line: from, ch:0},
                                { line: to, ch:editor.lineInfo(to).text.length } );
            jumpToLine(from);
        }
    }
}

// function foldByLevel(cm, level) {
//     foldByNodeOrder(cm, 0);
//     // initialize vars
//     var cursor = cm.getCursor();
//     cursor.ch = 0;
//     cursor.line = 0;
//     var range = cm.getViewport();
//     foldByLevelRec(cm, cursor, range, level);
// };

// function foldByLevelRec(cm, cursor, range, level) {
//     if (level > 0) {
//         var searcher = cm.getSearchCursor("<", cursor, false);
//         while (searcher.findNext() && searcher.pos.from.line < range.to) {
//             // unfold the tag
//             cm.foldCode(searcher.pos.from, null, "unfold");
//             // move the cursor into the tag
//             cursor = searcher.pos.from;
//             cursor.ch = searcher.pos.from.ch + 1;
//             // find the closing tag
//             var match = CodeMirror.findMatchingTag(cm, cursor, range);
//             if (match) {
//                 if (match.close) {
//                     // create the inner-range and jump the searcher after the ending tag
//                     var innerrange = { from: range.from, to: range.to };
//                     innerrange.from = cursor.line + 1;
//                     innerrange.to = match.close.to.line;
//                     // the recursive call
//                     foldByLevelRec(cm, cursor, innerrange, level - 1);
//                 }
//                 // move to the next element in the same tag of this function scope
//                 var nextcursor = { line: cursor.line, to: cursor.ch };
//                 if (match.close) {
//                     nextcursor.line = match.close.to.line;
//                 }
//                 nextcursor.ch = 0;
//                 nextcursor.line = nextcursor.line + 1;
//                 searcher = cm.getSearchCursor("\t", nextcursor, false);
//             }
//         }
//     }
// }

// function foldByNodeOrder(cm, node) {
//     // 0 - fold all
//     unfoldAll(cm);
//     node++;
//     for (var l = cm.firstLine() ; l <= cm.lastLine() ; ++l)
//         if (node == 0)
//             cm.foldCode({ line: l, ch: 0 }, null, "fold");
//         else node--;
// }

// function unfoldAll() {
//     for (var i = 0; i < editor.lineCount() ; i++) {
//         editor.foldCode({ line: i }, null, "unfold");
//     }
// }

// CODE EDITOR
//----------------------------------------------
function newContent(){
    window.location.href = ".";
}

function openContent(input){
    var reader = new FileReader();
    reader.onload = function(e) {
        editor.setValue(e.target.result);
    }
    reader.readAsText(input.files[0]);
}

var updateContet = debounce(function(){
    if (editor) {
        var createObjectURL = (window.URL && window.URL.createObjectURL) || (window.webkitURL && window.webkitURL.createObjectURL); // for Safari compatibliity
        var url = createObjectURL(new Blob([ editor.getValue() ]));
        scene.reload(url);
    }
}, 500);

function saveContent(){
    if (editor) {
        var blob = new Blob([editor.getValue()], {type: "text/plain;charset=utf-8"});
        saveAs(blob, "style.yaml");
    }
}

function initEditor(){
    querry = parseQuery(window.location.search.slice(1));

    if (querry['location']){
        style_file = querry['location'];
    }

    // Load style_file into style content
    style_content = fetchHTTP(style_file);

    var demoEditor = document.getElementById("editor");
    if(demoEditor){
        editor = CodeMirror(demoEditor,{
            value: style_content,
            lineNumbers: true,
            matchBrackets: true,
            mode: "text/x-yaml",
            keyMap: "sublime",
            autoCloseBrackets: true,
            extraKeys: { },
            foldGutter: {
                rangeFinder: CodeMirror.fold.indent
            },
            gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
            showCursorWhenSelecting: true,
            theme: "tangram",
            lineWrapping: true,
            autofocus: true,
            indentUnit: 4
        });

        editor.on("change", function(cm) {
            updateContet();
        });
    }
}

function setupEditor(){
    if (querry['lines']){
        selectLines(querry['lines']);
    }
}

// TANGRAM
//----------------------------------------------
function initMap() {
    'use strict';
    var map_start_location = [40.70531887544228, -74.00976419448853, 16];

    /*** URL parsing ***/

    // leaflet-style URL hash pattern:
    // ?style.yaml#[zoom],[lat],[lng]
    var url_hash = window.location.hash.slice(1).split('/');
    if (url_hash.length == 3) {
        map_start_location = [url_hash[1],url_hash[2], url_hash[0]];
        // convert from strings
        map_start_location = map_start_location.map(Number);
    }

    /*** Map ***/
    map = L.map('map',
        { zoomControl: false },
        {'keyboardZoomOffset': .05}
    );

    L.control.zoom({ position: 'topright' }).addTo(map);

    map.attributionControl.setPrefix('Leaflet')
    var layer = Tangram.leafletLayer({
        scene: style_file,
        attribution: 'Tangram | © OSM contributors | Mapzen'
    });

    window.layer = layer;
    var scene = layer.scene;
    window.scene = scene;

    map.setView(map_start_location.slice(0, 2), map_start_location[2]);
    var hash = new L.Hash(map);

    window.addEventListener('load', function () {
        // Scene initialized
        layer.addTo(map);
    });

    //disable mousewheel zoom if iframed
    if (window.self !== window.top) {
      map.scrollWheelZoom.disable();
    }
}

// Events
//----------------------------------------------
function resizeMap() {
    editor.setSize('100%',(window.innerHeight-31) + 'px');
}

window.addEventListener('resize', resizeMap);

// MAIN
//----------------------------------------------
initEditor();

initMap();
resizeMap();

setupEditor();
<commit_msg>style instead of locator<commit_after>var querry, editor, map;
var style_file = "styles/default.yaml";
var style_content = "";

// TOOLS
//----------------------------------------------
function fetchHTTP(url, methood){
    var request = new XMLHttpRequest(), response;

    request.onreadystatechange = function () {
        if (request.readyState === 4 && request.status === 200) {
            response = request.responseText;
        }
    }
    request.open(methood ? methood : 'GET', url, false);
    request.send();
    return response;
}

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

function parseQuery(qstr){
  var query = {};
  var a = qstr.split('&');
  for (var i in a){
    var b = a[i].split('=');
    query[decodeURIComponent(b[0])] = decodeURIComponent(b[1]);
  }

  return query;
}

function isNumber(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); }

function jumpToLine(i) {
    var t = editor.charCoords({line: i-1, ch: 0}, "local").top;
    // var middleHeight = editor.getScrollerElement().offsetHeight / 2;
    editor.scrollTo(null, t);// - middleHeight - 5);
}

function selectLine( nLine ){
    if (editor) {
        editor.setSelection({ line: nLine, ch:0},
                            { line: nLine, ch:editor.lineInfo(nLine).text.length } );
    }
}

function selectLines( _string ){
    if (editor) {
        if ( isNumber(_string) ){
            selectLine(parseInt(_string)-1);
            jumpToLine(parseInt(_string)-1);
        } else {
            var lines = _string.split('-');
            var from = parseInt(lines[0])-1;
            var to = parseInt(lines[1])-1;

            editor.setSelection({ line: from, ch:0},
                                { line: to, ch:editor.lineInfo(to).text.length } );
            jumpToLine(from);
        }
    }
}

// function foldByLevel(cm, level) {
//     foldByNodeOrder(cm, 0);
//     // initialize vars
//     var cursor = cm.getCursor();
//     cursor.ch = 0;
//     cursor.line = 0;
//     var range = cm.getViewport();
//     foldByLevelRec(cm, cursor, range, level);
// };

// function foldByLevelRec(cm, cursor, range, level) {
//     if (level > 0) {
//         var searcher = cm.getSearchCursor("<", cursor, false);
//         while (searcher.findNext() && searcher.pos.from.line < range.to) {
//             // unfold the tag
//             cm.foldCode(searcher.pos.from, null, "unfold");
//             // move the cursor into the tag
//             cursor = searcher.pos.from;
//             cursor.ch = searcher.pos.from.ch + 1;
//             // find the closing tag
//             var match = CodeMirror.findMatchingTag(cm, cursor, range);
//             if (match) {
//                 if (match.close) {
//                     // create the inner-range and jump the searcher after the ending tag
//                     var innerrange = { from: range.from, to: range.to };
//                     innerrange.from = cursor.line + 1;
//                     innerrange.to = match.close.to.line;
//                     // the recursive call
//                     foldByLevelRec(cm, cursor, innerrange, level - 1);
//                 }
//                 // move to the next element in the same tag of this function scope
//                 var nextcursor = { line: cursor.line, to: cursor.ch };
//                 if (match.close) {
//                     nextcursor.line = match.close.to.line;
//                 }
//                 nextcursor.ch = 0;
//                 nextcursor.line = nextcursor.line + 1;
//                 searcher = cm.getSearchCursor("\t", nextcursor, false);
//             }
//         }
//     }
// }

// function foldByNodeOrder(cm, node) {
//     // 0 - fold all
//     unfoldAll(cm);
//     node++;
//     for (var l = cm.firstLine() ; l <= cm.lastLine() ; ++l)
//         if (node == 0)
//             cm.foldCode({ line: l, ch: 0 }, null, "fold");
//         else node--;
// }

// function unfoldAll() {
//     for (var i = 0; i < editor.lineCount() ; i++) {
//         editor.foldCode({ line: i }, null, "unfold");
//     }
// }

// CODE EDITOR
//----------------------------------------------
function newContent(){
    window.location.href = ".";
}

function openContent(input){
    var reader = new FileReader();
    reader.onload = function(e) {
        editor.setValue(e.target.result);
    }
    reader.readAsText(input.files[0]);
}

var updateContet = debounce(function(){
    if (editor) {
        var createObjectURL = (window.URL && window.URL.createObjectURL) || (window.webkitURL && window.webkitURL.createObjectURL); // for Safari compatibliity
        var url = createObjectURL(new Blob([ editor.getValue() ]));
        scene.reload(url);
    }
}, 500);

function saveContent(){
    if (editor) {
        var blob = new Blob([editor.getValue()], {type: "text/plain;charset=utf-8"});
        saveAs(blob, "style.yaml");
    }
}

function initEditor(){
    querry = parseQuery(window.location.search.slice(1));

    if (querry['style']){
        style_file = querry['style'];
    }

    // Load style_file into style content
    style_content = fetchHTTP(style_file);

    var demoEditor = document.getElementById("editor");
    if(demoEditor){
        editor = CodeMirror(demoEditor,{
            value: style_content,
            lineNumbers: true,
            matchBrackets: true,
            mode: "text/x-yaml",
            keyMap: "sublime",
            autoCloseBrackets: true,
            extraKeys: { },
            foldGutter: {
                rangeFinder: CodeMirror.fold.indent
            },
            gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
            showCursorWhenSelecting: true,
            theme: "tangram",
            lineWrapping: true,
            autofocus: true,
            indentUnit: 4
        });

        editor.on("change", function(cm) {
            updateContet();
        });
    }
}

function setupEditor(){
    if (querry['lines']){
        selectLines(querry['lines']);
    }
}

// TANGRAM
//----------------------------------------------
function initMap() {
    'use strict';
    var map_start_location = [40.70531887544228, -74.00976419448853, 16];

    /*** URL parsing ***/

    // leaflet-style URL hash pattern:
    // ?style.yaml#[zoom],[lat],[lng]
    var url_hash = window.location.hash.slice(1).split('/');
    if (url_hash.length == 3) {
        map_start_location = [url_hash[1],url_hash[2], url_hash[0]];
        // convert from strings
        map_start_location = map_start_location.map(Number);
    }

    /*** Map ***/
    map = L.map('map',
        { zoomControl: false },
        {'keyboardZoomOffset': .05}
    );

    L.control.zoom({ position: 'topright' }).addTo(map);

    map.attributionControl.setPrefix('Leaflet')
    var layer = Tangram.leafletLayer({
        scene: style_file,
        attribution: 'Tangram | © OSM contributors | Mapzen'
    });

    window.layer = layer;
    var scene = layer.scene;
    window.scene = scene;

    map.setView(map_start_location.slice(0, 2), map_start_location[2]);
    var hash = new L.Hash(map);

    window.addEventListener('load', function () {
        // Scene initialized
        layer.addTo(map);
    });

    //disable mousewheel zoom if iframed
    if (window.self !== window.top) {
      map.scrollWheelZoom.disable();
    }
}

// Events
//----------------------------------------------
function resizeMap() {
    editor.setSize('100%',(window.innerHeight-31) + 'px');
}

window.addEventListener('resize', resizeMap);

// MAIN
//----------------------------------------------
initEditor();

initMap();
resizeMap();

setupEditor();
<|endoftext|>


<commit_before>'use babel'

const extractionRegex = /at (.*?) \((.*?):(\d+):(\d+)\)/

export function capture() {
  const toReturn = []
  const stack = new Error().stack.split('\n')

  stack.shift()
  stack.forEach(function(entry) {
    const matches = extractionRegex.exec(entry)
    if (matches !== null) {
      toReturn.push({
        function: matches[1],
        file: matches[2],
        line: parseInt(matches[3]) || 1,
        col: parseInt(matches[4]) || 1
      })
    }
  })
  // Remove self from the stack
  toReturn.shift()

  return toReturn
}
<commit_msg>:new: Add fromStack method<commit_after>'use babel'

const extractionRegex = /at (.*?) \((.*?):(\d+):(\d+)\)/

export function capture() {
  const traces = fromStack(new Error().stack)
  // Remove self from the stack
  traces.shift()
  return traces
}

export function fromStack(stack) {
  const stackChunks = stack.split('\n')
  const traces = []

  stackChunks.forEach(function(entry) {
    const matches = extractionRegex.exec(entry)
    if (matches !== null) {
      traces.push({
        function: matches[1],
        file: matches[2],
        line: parseInt(matches[3]) || 1,
        col: parseInt(matches[4]) || 1
      })
    }
  })

  return traces
}
<|endoftext|>


<commit_before>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		return (

				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> is closed. Final Round to begin soon

		);

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<commit_msg>final round text<commit_after>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		return (

				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Final Round!

		);

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<|endoftext|>


<commit_before>/* jslint node: true */
'use strict';

//	ENiGMA½
const PluginModule			= require('./plugin_module.js').PluginModule;
const Config				= require('./config.js').config;
const Log					= require('./logger.js').log;

const _						= require('lodash');
const later					= require('later');
const path					= require('path');
const pty					= require('ptyw.js');
const gaze					= require('gaze');

exports.getModule				= EventSchedulerModule;
exports.EventSchedulerModule	= EventSchedulerModule;	//	allow for loadAndStart

exports.moduleInfo = {
	name	: 'Event Scheduler',
	desc	: 'Support for scheduling arbritary events',
	author	: 'NuSkooler',
};

const SCHEDULE_REGEXP	= /(?:^|or )?(@watch\:)([^\0]+)?$/;
const ACTION_REGEXP		= /\@(method|execute)\:([^\0]+)?$/;

class ScheduledEvent {
	constructor(events, name) {
		this.name		= name;
		this.schedule 	= this.parseScheduleString(events[name].schedule);
		this.action		= this.parseActionSpec(events[name].action);
		if(this.action) {
			this.action.args = events[name].args;
		}
	}

	get isValid() {
		if((!this.schedule || (!this.schedule.sched && !this.schedule.watchFile)) || !this.action) {
			return false;
		}

		if('method' === this.action.type && !this.action.location) {
			return false;
		}

		return true;
	}

	parseScheduleString(schedStr) {
		if(!schedStr) {
			return false;
		}

		let schedule = {};

		const m = SCHEDULE_REGEXP.exec(schedStr);
		if(m) {
			schedStr = schedStr.substr(0, m.index).trim();

			if('@watch:' === m[1]) {
				schedule.watchFile = m[2];
			}
		}

		if(schedStr.length > 0) {
			const sched = later.parse.text(schedStr);
			if(-1 === sched.error) {
				schedule.sched = sched;
			}
		}

		//	return undefined if we couldn't parse out anything useful
		if(!_.isEmpty(schedule)) {
			return schedule;
		}
	}

	parseActionSpec(actionSpec) {
		if(actionSpec) {
			if('@' === actionSpec[0]) {
				const m = ACTION_REGEXP.exec(actionSpec);
				if(m) {
					if(m[2].indexOf(':') > -1) {
						const parts = m[2].split(':');
						return {
							type		: m[1],
							location	: parts[0],
							what		: parts[1],
						};
					} else {
						return {
							type	: m[1],
							what	: m[2],
						};
					}
				}
			} else {
				return {
					type	: 'execute',
					what	: actionSpec,
				};
			}
		}
	}

	executeAction(reason, cb) {
		Log.info( { eventName : this.name, action : this.action, reason : reason }, 'Executing scheduled event action...');

		if('method' === this.action.type) {
			const modulePath = path.join(__dirname, '../', this.action.location);	//	enigma-bbs base + supplied location (path/file.js')
			try {
				const methodModule = require(modulePath);
				methodModule[this.action.what](this.action.args, err => {
					if(err) {
						Log.debug(
							{ error : err.toString(), eventName : this.name, action : this.action },
							'Error performing scheduled event action');
					}

					return cb(err);
				});
			} catch(e) {
				Log.warn(
					{ error : e.toString(), eventName : this.name, action : this.action },
					'Failed to perform scheduled event action');

				return cb(e);
			}
		} else if('execute' === this.action.type) {
			const opts = {
				//	:TODO: cwd
				name	: this.name,
				cols	: 80,
				rows	: 24,
				env		: process.env,
			};

			const proc = pty.spawn(this.action.what, this.action.args, opts);

			proc.once('exit', exitCode => {
				if(exitCode) {
					Log.warn(
						{ eventName : this.name, action : this.action, exitCode : exitCode },
						'Bad exit code while performing scheduled event action');
				}
				return cb(exitCode ? new Error(`Bad exit code while performing scheduled event action: ${exitCode}`) : null);
			});
		}
	}
}

function EventSchedulerModule(options) {
	PluginModule.call(this, options);

	if(_.has(Config, 'eventScheduler')) {
		this.moduleConfig = Config.eventScheduler;
	}

	const self = this;
	this.runningActions = new Set();

	this.performAction = function(schedEvent, reason) {
		if(self.runningActions.has(schedEvent.name)) {
			return;	//	already running
		}

		self.runningActions.add(schedEvent.name);

		schedEvent.executeAction(reason, () => {
			self.runningActions.delete(schedEvent.name);
		});
	};
}

//	convienence static method for direct load + start
EventSchedulerModule.loadAndStart = function(cb) {
	const loadModuleEx = require('./module_util.js').loadModuleEx;

	const loadOpts = {
		name		: path.basename(__filename, '.js'),
		path		: __dirname,
	};

	loadModuleEx(loadOpts, (err, mod) => {
		if(err) {
			return cb(err);
		}

		const modInst = new mod.getModule();
		modInst.startup( err => {
			return cb(err, modInst);
		});
	});
};

EventSchedulerModule.prototype.startup = function(cb) {

	this.eventTimers	= [];
	const self = this;

	if(this.moduleConfig && _.has(this.moduleConfig, 'events')) {
		const events = Object.keys(this.moduleConfig.events).map( name => {
			return new ScheduledEvent(this.moduleConfig.events, name);
		});

		events.forEach( schedEvent => {
			if(!schedEvent.isValid) {
				Log.warn( { eventName : schedEvent.name }, 'Invalid scheduled event entry');
				return;
			}

			Log.debug(
				{
					eventName	: schedEvent.name,
					schedule	: this.moduleConfig.events[schedEvent.name].schedule,
					action		: schedEvent.action,
				},
				'Scheduled event loaded'
			);

			if(schedEvent.schedule.sched) {
				this.eventTimers.push(later.setInterval( () => {
					self.performAction(schedEvent, 'Schedule');
				}, schedEvent.schedule.sched));
			}

			if(schedEvent.schedule.watchFile) {
				gaze(schedEvent.schedule.watchFile, (err, watcher) => {
					//	:TODO: should track watched files & stop watching @ shutdown
					watcher.on('all', (watchEvent, watchedPath) => {
						if(schedEvent.schedule.watchFile === watchedPath) {
							self.performAction(schedEvent, `Watch file: ${watchedPath}`);
						}
					});
				});
			}
		});
	}

	cb(null);
};

EventSchedulerModule.prototype.shutdown = function(cb) {
	if(this.eventTimers) {
		this.eventTimers.forEach( et => et.clear() );
	}

	cb(null);
};
<commit_msg>Default to empty[] args<commit_after>/* jslint node: true */
'use strict';

//	ENiGMA½
const PluginModule			= require('./plugin_module.js').PluginModule;
const Config				= require('./config.js').config;
const Log					= require('./logger.js').log;

const _						= require('lodash');
const later					= require('later');
const path					= require('path');
const pty					= require('ptyw.js');
const gaze					= require('gaze');

exports.getModule				= EventSchedulerModule;
exports.EventSchedulerModule	= EventSchedulerModule;	//	allow for loadAndStart

exports.moduleInfo = {
	name	: 'Event Scheduler',
	desc	: 'Support for scheduling arbritary events',
	author	: 'NuSkooler',
};

const SCHEDULE_REGEXP	= /(?:^|or )?(@watch\:)([^\0]+)?$/;
const ACTION_REGEXP		= /\@(method|execute)\:([^\0]+)?$/;

class ScheduledEvent {
	constructor(events, name) {
		this.name		= name;
		this.schedule 	= this.parseScheduleString(events[name].schedule);
		this.action		= this.parseActionSpec(events[name].action);
		if(this.action) {
			this.action.args = events[name].args || [];
		}
	}

	get isValid() {
		if((!this.schedule || (!this.schedule.sched && !this.schedule.watchFile)) || !this.action) {
			return false;
		}

		if('method' === this.action.type && !this.action.location) {
			return false;
		}

		return true;
	}

	parseScheduleString(schedStr) {
		if(!schedStr) {
			return false;
		}

		let schedule = {};

		const m = SCHEDULE_REGEXP.exec(schedStr);
		if(m) {
			schedStr = schedStr.substr(0, m.index).trim();

			if('@watch:' === m[1]) {
				schedule.watchFile = m[2];
			}
		}

		if(schedStr.length > 0) {
			const sched = later.parse.text(schedStr);
			if(-1 === sched.error) {
				schedule.sched = sched;
			}
		}

		//	return undefined if we couldn't parse out anything useful
		if(!_.isEmpty(schedule)) {
			return schedule;
		}
	}

	parseActionSpec(actionSpec) {
		if(actionSpec) {
			if('@' === actionSpec[0]) {
				const m = ACTION_REGEXP.exec(actionSpec);
				if(m) {
					if(m[2].indexOf(':') > -1) {
						const parts = m[2].split(':');
						return {
							type		: m[1],
							location	: parts[0],
							what		: parts[1],
						};
					} else {
						return {
							type	: m[1],
							what	: m[2],
						};
					}
				}
			} else {
				return {
					type	: 'execute',
					what	: actionSpec,
				};
			}
		}
	}

	executeAction(reason, cb) {
		Log.info( { eventName : this.name, action : this.action, reason : reason }, 'Executing scheduled event action...');

		if('method' === this.action.type) {
			const modulePath = path.join(__dirname, '../', this.action.location);	//	enigma-bbs base + supplied location (path/file.js')
			try {
				const methodModule = require(modulePath);
				methodModule[this.action.what](this.action.args, err => {
					if(err) {
						Log.debug(
							{ error : err.toString(), eventName : this.name, action : this.action },
							'Error performing scheduled event action');
					}

					return cb(err);
				});
			} catch(e) {
				Log.warn(
					{ error : e.toString(), eventName : this.name, action : this.action },
					'Failed to perform scheduled event action');

				return cb(e);
			}
		} else if('execute' === this.action.type) {
			const opts = {
				//	:TODO: cwd
				name	: this.name,
				cols	: 80,
				rows	: 24,
				env		: process.env,
			};

			const proc = pty.spawn(this.action.what, this.action.args, opts);

			proc.once('exit', exitCode => {
				if(exitCode) {
					Log.warn(
						{ eventName : this.name, action : this.action, exitCode : exitCode },
						'Bad exit code while performing scheduled event action');
				}
				return cb(exitCode ? new Error(`Bad exit code while performing scheduled event action: ${exitCode}`) : null);
			});
		}
	}
}

function EventSchedulerModule(options) {
	PluginModule.call(this, options);

	if(_.has(Config, 'eventScheduler')) {
		this.moduleConfig = Config.eventScheduler;
	}

	const self = this;
	this.runningActions = new Set();

	this.performAction = function(schedEvent, reason) {
		if(self.runningActions.has(schedEvent.name)) {
			return;	//	already running
		}

		self.runningActions.add(schedEvent.name);

		schedEvent.executeAction(reason, () => {
			self.runningActions.delete(schedEvent.name);
		});
	};
}

//	convienence static method for direct load + start
EventSchedulerModule.loadAndStart = function(cb) {
	const loadModuleEx = require('./module_util.js').loadModuleEx;

	const loadOpts = {
		name		: path.basename(__filename, '.js'),
		path		: __dirname,
	};

	loadModuleEx(loadOpts, (err, mod) => {
		if(err) {
			return cb(err);
		}

		const modInst = new mod.getModule();
		modInst.startup( err => {
			return cb(err, modInst);
		});
	});
};

EventSchedulerModule.prototype.startup = function(cb) {

	this.eventTimers	= [];
	const self = this;

	if(this.moduleConfig && _.has(this.moduleConfig, 'events')) {
		const events = Object.keys(this.moduleConfig.events).map( name => {
			return new ScheduledEvent(this.moduleConfig.events, name);
		});

		events.forEach( schedEvent => {
			if(!schedEvent.isValid) {
				Log.warn( { eventName : schedEvent.name }, 'Invalid scheduled event entry');
				return;
			}

			Log.debug(
				{
					eventName	: schedEvent.name,
					schedule	: this.moduleConfig.events[schedEvent.name].schedule,
					action		: schedEvent.action,
				},
				'Scheduled event loaded'
			);

			if(schedEvent.schedule.sched) {
				this.eventTimers.push(later.setInterval( () => {
					self.performAction(schedEvent, 'Schedule');
				}, schedEvent.schedule.sched));
			}

			if(schedEvent.schedule.watchFile) {
				gaze(schedEvent.schedule.watchFile, (err, watcher) => {
					//	:TODO: should track watched files & stop watching @ shutdown
					watcher.on('all', (watchEvent, watchedPath) => {
						if(schedEvent.schedule.watchFile === watchedPath) {
							self.performAction(schedEvent, `Watch file: ${watchedPath}`);
						}
					});
				});
			}
		});
	}

	cb(null);
};

EventSchedulerModule.prototype.shutdown = function(cb) {
	if(this.eventTimers) {
		this.eventTimers.forEach( et => et.clear() );
	}

	cb(null);
};
<|endoftext|>


<commit_before>import {app, screen, BrowserWindow, ipcMain} from 'electron';
import path from 'path';

import {mkdirp, ipcDecorator} from './main/helpers';
import {APP_DATA_PATH} from './main/constants';
import {sequelize} from './main/models';

import * as folder from './main/controllers/folder';

const ipc = ipcDecorator.decorate(ipcMain);

sequelize.sync()
  .then(init);

function init() {

  let mainWindow = null;

  app.on('window-all-closed', () => {
    app.quit();
  });

  app.on('ready', () => {

    const {width, height} = screen.getPrimaryDisplay().workAreaSize;
    mainWindow = new BrowserWindow({width, height});

    mainWindow.loadURL('file://' + __dirname + '/index.html');

    mainWindow.on('close', (event) => {
    });

    mainWindow.on('closed', () => {
      mainWindow = null;
    });
  });

  ipc.on('GET /folders', folder.listFolders);
}
<commit_msg>refactor main.js<commit_after>import {app, screen, BrowserWindow, ipcMain} from 'electron';
import path from 'path';

import {mkdirp, ipcDecorator} from './main/helpers';
import {APP_DATA_PATH} from './main/constants';
import {sequelize} from './main/models';

import * as folder from './main/controllers/folder';

let mainWindow = null;

app.on('window-all-closed', () => {
  app.quit();
});

app.on('ready', handleAppReady);

function handleAppReady() {

  const {width, height} = screen.getPrimaryDisplay().workAreaSize;
  mainWindow = new BrowserWindow({width, height});

  mainWindow.on('close', (event) => {
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  sequelize.sync()
    .then(() => {
      mainWindow.loadURL('file://' + __dirname + '/index.html');
    })
    .catch((err) => {
      log.error('err:', err);
    });

  const ipc = ipcDecorator.decorate(ipcMain);

  ipc.on('GET /folders', folder.listFolders);
}
<|endoftext|>


<commit_before>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		return (

				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Final Round!

		);

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<commit_msg>Hack for the theme announcement<commit_after>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		return (

				<NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> has ended. The theme will be announced soon.

		);

// Yes, if you're watching the source code, we're going to have to do this manually.
//				{"Theme:"}{" ???"}

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Final Round!

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<|endoftext|>


<commit_before>import Frame from './Frame';
import SigninPage from './pages/SigninPage';
import SignupPage from './pages/SignupPage';
import AccountHomePage from './pages/AccountHomePage';
import MagicLinkPage from './pages/MagicLinkPage';
import LoadingPage from './pages/LoadingPage';
import {ReactComponent as CloseIcon} from '../images/icons/close.svg';
import {ParentContext} from './ParentContext';
import FrameStyle from './Frame.styles';

const React = require('react');

const Styles = {
    modalContainer: {
        zIndex: '1000',
        paddingTop: '100px',
        position: 'fixed',
        left: '0',
        top: '0',
        width: '100%',
        height: '100%',
        overflow: 'auto',
        backgroundColor: 'rgba(128,128,128,0.5)'
    },
    frame: {
        common: {
            margin: 'auto',
            position: 'relative',
            padding: '0',
            outline: '0',
            width: '500px',
            borderRadius: '8px',
            boxShadow: 'rgba(0, 0, 0, 0.16) 0px 5px 40px',
            opacity: '1',
            overflow: 'hidden',
            height: '60%',
            backgroundColor: 'white'
        },
        signin: {
            minHeight: '200px',
            maxHeight: '330px'
        },
        signup: {
            minHeight: '580px',
            maxHeight: '620px'
        },
        accountHome: {
            width: '500px',
            minHeight: '300px',
            maxHeight: '350px'
        },
        magiclink: {
            minHeight: '230px',
            maxHeight: '230px'
        },
        loading: {
            minHeight: '130px'
        }
    },
    popup: {
        container: {
            width: '100%',
            height: '100%',
            position: 'absolute',
            letterSpacing: '0',
            textRendering: 'optimizeLegibility',
            fontSize: '1.5rem',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'flex-start',
            top: '0px',
            bottom: '0px',
            left: '0px',
            right: '0px',
            overflow: 'hidden',
            paddingTop: '18px',
            paddingBottom: '18px',
            textAlign: 'left',
            boxSizing: 'border-box'
        },
        closeIcon: {
            width: '16px',
            height: '16px',
            color: 'grey',
            cursor: 'pointer'
        }
    }
};

const Pages = {
    signin: SigninPage,
    signup: SignupPage,
    accountHome: AccountHomePage,
    magiclink: MagicLinkPage,
    loading: LoadingPage
};

export default class PopupModal extends React.Component {
    static contextType = ParentContext;

    renderCurrentPage(page) {
        const PageComponent = Pages[page];

        return (
            <PageComponent />
        );
    }

    renderPopupClose() {
        return (

                <CloseIcon style={Styles.popup.closeIcon} onClick = {() => this.context.onAction('closePopup')} />

        );
    }

    renderPopupContent() {
        return (

                {this.renderPopupClose()}
                {this.renderCurrentPage(this.context.page)}

        );
    }

    handlePopupClose(e) {
        e.preventDefault();
        if (e.target === e.currentTarget) {
            this.context.onAction('closePopup');
        }
    }

    renderFrameStyles() {
        return (


<commit_before><commit_msg>fixed server invite link<commit_after><|endoftext|>


<commit_before>/*
 * saucenao.js - grab a image's source using saucenao. Grabs the last message attachment/URL, provided along with the command
 * or providing an attachment using the command as the caption.
 *
 *  Contributed by Capuccino
 */

/* eslint-env node */

exports.commands = [
    'saucenao'
];

const imgRegex = str => /(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi.test(str);

exports.saucenao = {
    desc: 'Grab an image\'s source using saucenao API.',
    longDesc: 'Gets the image source from sacuenao.',
    usage: '[image url]',
    main(bot, ctx) {
        return new Promise((resolve, reject) => {
            if (!ctx.suffix) {
                //grab the last message with an attachment
                if (!ctx.channel.attachments) {
                    ctx.createMessage('Aw, no Image here.');
                } else {
                    let imgUrl = ctx.channel.attachments[0].url;
                    got(`https://saucenao.com`, {
                        method: 'POST',
                        'Content-Type': 'application/json',
                        body: JSON.stringify({url: imgUrl})
                    }).then(res => {
                        //add entries
                        const fields = [];
                        for (res.url of res) {
                            /**
                             * @todo Limit results to 5. Assuming saucenao emits more than we need.
                             */
                            fields.push({name: '', value: res[0].url, inline: true});
                        }
                        //finally send the message in embed
                        ctx.createMessage({
                            embed: {
                                title: 'saucenao Query',
                                desccription: 'This is the closest I can find.',
                                fields
                            }
                        });
                    }).then(resolve).catch(reject);
                }
            } else {
                if (!imgRegex) {
                    ctx.createMessage('Your URL is invalid!, Please make sure it\'s a valid image URL.');
                } else {
                    //query saucenao but this time, we use the suffix value
                    got('https://saucenao.com', {
                        method: 'POST',
                        'Content-Type': 'application/json',
                        body: JSON.stringify({url: ctx.suffix})
                    }).then(res => {
                        const fields = [];
                        for (res of res.url) {
                            /**
                             * @todo Limit results to 5. Assuming saucenao emits more than we need.
                             */
                            fields.push({name: '', value: res.url});
                        }
                        ctx.createMessage({
                            embed: {
                                title: 'saucenao query',
                                desccription: 'this is the closest I can find',
                                fields
                            }
                        });
                    }).then(resolve).catch(reject);
                }
            }
        });
    }
};<commit_msg>empty saucenao for now<commit_after>/*
 * saucenao.js - grab a image's source using saucenao. Grabs the last message attachment/URL, provided along with the command
 * or providing an attachment using the command as the caption.
 *
 *  Contributed by Capuccino
 */

/* eslint-env node */

exports.commands = [
    'saucenao'
];

exports.saucenao = {

};<|endoftext|>


<commit_before>import React from 'react'
import { Container, Heading, Flex, Box, Text, Subhead, Link as A } from 'rebass'
import theme, { colors, mx } from '../theme'
import { Field, Submit } from './Forms'
import { withFormik } from 'formik'
import yup from 'yup'
import fetch from 'unfetch'

const Base = Container.extend.attrs({
  is: 'form',
  py: 4,
  px: 3,
  maxWidth: 40 * 16
})`
  display: grid;
  grid-gap: 1rem;
  ${mx[1]} {
    grid-template-columns: repeat(2, 1fr);
    h2, .textarea, #referer { grid-column: 1 / -1; }
    #start_date { display: none !important; }
  }
`

const Subheading = Heading.extend.attrs({
  f: 4,
  mt: 2,
  mb: 0,
  color: 'primary'
})``

const systems_hacked =
  'https://www.quora.com/When-have-you-most-successfully-hacked-a-non-computer-system-to-your-advantage'
const next12Months = () => {
  const monthNames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ]
  const genMonth = date => {
    const month = monthNames[date.getMonth()]
    const label = `${month} ${date.getFullYear()}`
    const iso = date.toISOString()
    return { label, iso }
  }
  const months = []
  const today = new Date()
  const asap = genMonth(today)
  asap.label = 'ASAP'
  months.push(asap)
  for (let i = 0; i < 12; i++) {
    today.setMonth(today.getMonth() + 1)
    months.push(genMonth(today))
  }
  return months
}

const statusMessage = status =>
  status
    ? {
        success: 'Submitted! 🎉',
        error: 'Something went wrong 😰'
      }[status]
    : 'Submit application 📝'
const InnerForm = ({
  values,
  errors,
  touched,
  handleChange,
  handleBlur,
  handleSubmit,
  isSubmitting,
  status,
  months = next12Months()
}) => (
  <Base onSubmit={handleSubmit}>
    <Subheading>About you</Subheading>
    <Field
      label="First name"
      name="first_name"
      p="Cat"
      onChange={handleChange}
      onBlur={handleBlur}
      value={values.first_name}
      error={touched.first_name && errors.first_name}
    />
    <Field
      label="Last name"
      name="last_name"
      value={values.last_name}
      p="Hackworth"
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.last_name && errors.last_name}
    />
    <Field
      label="Email"
      name="email"
      type="email"
      p="cat@hackclub.com"
      value={values.email}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.email && errors.email}
    />
    <Field
      label="Phone number"
      name="phone"
      type="phone"
      p="(555) 555-5555"
      value={values.phone}
      onChange={handleChange}
    />
    <Field
      label="GitHub"
      name="github"
      p="cathackworth"
      value={values.github}
      onChange={handleChange}
      onBlur={handleBlur}
    />
    <Field
      label="Twitter"
      name="twitter"
      p="cathackworth"
      value={values.twitter}
      onChange={handleChange}
      onBlur={handleBlur}
    />
    <Field
      label="High school name"
      name="high_school"
      p="Hacking High School"
      value={values.high_school}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.high_school && errors.high_school}
    />
    <Field
      label="When do you graduate?"
      name="year"
      type="select"
      value={values.year || "Select One"}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.year && errors.year}
    >
      <option value="Select One" disabled>Select one</option>
      <option value="9001">2022</option>
      <option value="9002">2021</option>
      <option value="9003">2020</option>
      <option value="9004">2019</option>
      <option value="9005">2018</option>
      <option value="9008">Graduated</option>
      <option value="9009">Teacher</option>
      <option value="9010">Other</option>
    </Field>
    <Field
      label="When do you want to start? *"
      name="start_date"
      type="select"
      value={values.start_date || 0}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.start_date && errors.start_date}
    >
      <option value={0} disabled>Select one</option>
      {months.map(({ iso, label }, i) => (
        <option value={iso} key={`start-${i}`} children={label} />
      ))}
    </Field>
    <Field
      label="How did you hear about us?"
      name="referer"
      type="text"
      value={values.referer}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.referer && errors.referer}
    />
    <Subheading>Your club</Subheading>
    <Field
      label="Please tell us about an interesting project, preferably outside of class, that you created or worked on."
      name="interesting_project"
      type="textarea"
      value={values.interesting_project}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.interesting_project && errors.interesting_project}
    />
    <Field
      label={

          Please tell us about the time you most successfully hacked some
          (non-computer) system to your advantage. (
            See some kinds of responses we’re looking for.
          )

      }
      name="systems_hacked"
      type="textarea"
      value={values.systems_hacked}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.systems_hacked && errors.systems_hacked}
    />
    <Field
      label="What steps have you taken so far in starting your club?"
      name="steps_taken"
      type="textarea"
      value={values.steps_taken}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.steps_taken && errors.steps_taken}
    />
    <Submit
      disabled={isSubmitting}
      onClick={handleSubmit}
      value={statusMessage(status)}
    />
    <Text color="slate" f={2} style={{ lineHeight: '1.25' }}>
      * The soonest we’re accepting applications for is the second semester of
      the 2017–2018 school year.
    </Text>
  </Base>
)
const r = 'required'
const ApplicationForm = withFormik({
  mapPropsToValues: ({ params }) => ({ ...params }),
  validationSchema: yup.object().shape({
    first_name: yup.string().required(r),
    last_name: yup.string().required(r),
    email: yup
      .string()
      .required(r)
      .email('invalid email address')
      .matches(/^((?!hackclub\.com).)*$/, 'cannot be @hackclub.com'),
    high_school: yup.string().required(r),
    year: yup.string().required(r),
    start_date: yup.string().required(r),
    referer: yup.string().required(r),
    interesting_project: yup.string().required(r),
    systems_hacked: yup.string().required(r),
    steps_taken: yup.string().required(r)
  }),
  enableReinitialize: true,
  handleSubmit: (data, { setSubmitting, setStatus, resetForm }) => {
    console.log(data)
    fetch('https://api.hackclub.com/v1/club_applications', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
      .then(res => {
        console.log(res)
        resetForm()
        setStatus('success')
      })
      .catch(e => {
        console.error(e)
        setSubmitting(false)
        setStatus('error')
      })
  },
  displayName: 'ApplicationForm'
})(InnerForm)
export default ApplicationForm
<commit_msg>Fix select menus, closes #32<commit_after>import React from 'react'
import { Container, Heading, Flex, Box, Text, Subhead, Link as A } from 'rebass'
import theme, { colors, mx } from '../theme'
import { Field, Submit } from './Forms'
import { withFormik } from 'formik'
import yup from 'yup'
import fetch from 'unfetch'

const Base = Container.extend.attrs({
  is: 'form',
  py: 4,
  px: 3,
  maxWidth: 40 * 16
})`
  display: grid;
  grid-gap: 1rem;
  ${mx[1]} {
    grid-template-columns: repeat(2, 1fr);
    h2, .textarea { grid-column: 1 / -1; }
  }
`

const Subheading = Heading.extend.attrs({
  f: 4,
  mt: 2,
  mb: 0,
  color: 'primary'
})``

const systems_hacked =
  'https://www.quora.com/When-have-you-most-successfully-hacked-a-non-computer-system-to-your-advantage'
const next12Months = () => {
  const monthNames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ]
  const genMonth = date => {
    const month = monthNames[date.getMonth()]
    const label = `${month} ${date.getFullYear()}`
    const iso = date.toISOString()
    return { label, iso }
  }
  const months = []
  const today = new Date()
  const asap = genMonth(today)
  asap.label = 'ASAP'
  months.push(asap)
  for (let i = 0; i < 12; i++) {
    today.setMonth(today.getMonth() + 1)
    months.push(genMonth(today))
  }
  return months
}

const statusMessage = status =>
  status
    ? {
        success: 'Submitted! 🎉',
        error: 'Something went wrong 😰'
      }[status]
    : 'Submit application 📝'
const InnerForm = ({
  values,
  errors,
  touched,
  handleChange,
  handleBlur,
  handleSubmit,
  isSubmitting,
  status,
  months = next12Months()
}) => (
  <Base onSubmit={handleSubmit}>
    <Subheading>About you</Subheading>
    <Field
      label="First name"
      name="first_name"
      p="Cat"
      onChange={handleChange}
      onBlur={handleBlur}
      value={values.first_name}
      error={touched.first_name && errors.first_name}
    />
    <Field
      label="Last name"
      name="last_name"
      value={values.last_name}
      p="Hackworth"
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.last_name && errors.last_name}
    />
    <Field
      label="Email"
      name="email"
      type="email"
      p="cat@hackclub.com"
      value={values.email}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.email && errors.email}
    />
    <Field
      label="Phone number"
      name="phone"
      type="phone"
      p="(555) 555-5555"
      value={values.phone}
      onChange={handleChange}
    />
    <Field
      label="GitHub"
      name="github"
      p="cathackworth"
      value={values.github}
      onChange={handleChange}
      onBlur={handleBlur}
    />
    <Field
      label="Twitter"
      name="twitter"
      p="cathackworth"
      value={values.twitter}
      onChange={handleChange}
      onBlur={handleBlur}
    />
    <Field
      label="High school name"
      name="high_school"
      p="Hacking High School"
      value={values.high_school}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.high_school && errors.high_school}
    />
    <Field
      label="When do you graduate?"
      name="year"
      type="select"
      defaultValue={values.year || 'select'}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.year && errors.year}
    >
      <option value="select" disabled>
        Select one
      </option>
      <option value="9001">2022</option>
      <option value="9002">2021</option>
      <option value="9003">2020</option>
      <option value="9004">2019</option>
      <option value="9005">2018</option>
      <option value="9008">Graduated</option>
      <option value="9009">Teacher</option>
      <option value="9010">Other</option>
    </Field>
    <Field
      label="When do you want to start? *"
      name="start_date"
      type="select"
      defaultValue={values.start_date || 'select'}
      onBlur={handleBlur}
      error={touched.start_date && errors.start_date}
    >
      <option value="select" disabled>
        Select one
      </option>
      {months.map(({ iso, label }, i) => (
        <option value={iso} key={`start-${i}`} children={label} />
      ))}
    </Field>
    <Field
      label="How did you hear about us?"
      name="referer"
      type="text"
      value={values.referer}
      onBlur={handleBlur}
      error={touched.referer && errors.referer}
    />
    <Subheading>Your club</Subheading>
    <Field
      label="Please tell us about an interesting project, preferably outside of class, that you created or worked on."
      name="interesting_project"
      type="textarea"
      value={values.interesting_project}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.interesting_project && errors.interesting_project}
    />
    <Field
      label={

          Please tell us about the time you most successfully hacked some
          (non-computer) system to your advantage. (
            See some kinds of responses we’re looking for.
          )

      }
      name="systems_hacked"
      type="textarea"
      value={values.systems_hacked}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.systems_hacked && errors.systems_hacked}
    />
    <Field
      label="What steps have you taken so far in starting your club?"
      name="steps_taken"
      type="textarea"
      value={values.steps_taken}
      onChange={handleChange}
      onBlur={handleBlur}
      error={touched.steps_taken && errors.steps_taken}
    />
    <Submit
      disabled={isSubmitting}
      onClick={handleSubmit}
      value={statusMessage(status)}
    />
    <Text color="slate" f={2} style={{ lineHeight: '1.25' }}>
      * The soonest we’re accepting applications for is the second semester of
      the 2017–2018 school year.
    </Text>
  </Base>
)
const r = 'required'
const ApplicationForm = withFormik({
  mapPropsToValues: ({ params }) => ({ ...params }),
  validationSchema: yup.object().shape({
    first_name: yup.string().required(r),
    last_name: yup.string().required(r),
    email: yup
      .string()
      .required(r)
      .email('invalid email address')
      .matches(/^((?!hackclub\.com).)*$/, 'cannot be @hackclub.com'),
    high_school: yup.string().required(r),
    year: yup.string().required(r),
    start_date: yup.string().required(r),
    referer: yup.string().required(r),
    interesting_project: yup.string().required(r),
    systems_hacked: yup.string().required(r),
    steps_taken: yup.string().required(r)
  }),
  enableReinitialize: true,
  handleSubmit: (data, { setSubmitting, setStatus, resetForm }) => {
    console.log(data)
    fetch('https://api.hackclub.com/v1/club_applications', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
      .then(res => {
        console.log(res)
        resetForm()
        setStatus('success')
      })
      .catch(e => {
        console.error(e)
        setSubmitting(false)
        setStatus('error')
      })
  },
  displayName: 'ApplicationForm'
})(InnerForm)
export default ApplicationForm
<|endoftext|>


<commit_before>import React from 'react'
import { get } from 'lodash/fp'
import Icon from './Icon'
import DatetimePicker from 'react-datetime'
import { sanitizeTagInput } from '../util/textInput'
import { fetchTag } from '../actions'
const { func, object } = React.PropTypes

export const validateTag = (tag, dispatch) => {
  return dispatch(fetchTag(tag))
  .then(({ payload, error, meta: { tagName } }) => {
    if (error && get('response.status', payload) === 404) return true

    window.alert(`The tag "${tagName}" is already in use.`)
    return false
  })
}

export default class EventPostEditor extends React.Component {
  static propTypes = {
    postEdit: object,
    post: object,
    update: func.isRequired
  }

  static contextTypes = {dispatch: func}

  validate = () => {
    const { postEdit: { tag }, post } = this.props
    return tag === get('tag', post) ? true
      : validateTag(tag, this.context.dispatch)
  }

  render () {
    const { postEdit, update } = this.props
    const { start_time, end_time, tag, type } = postEdit
    if (type !== 'event') setTimeout(() => update({type: 'event'})) // smelly

    const startTime = start_time ? new Date(start_time) : null
    const endTime = end_time ? new Date(end_time) : null
    const updateTag = tag => update({tag, tagEdited: true})

    return

        <Icon name='Calendar'/>
        <DatetimePicker inputProps={{placeholder: 'start time'}}
          value={startTime}
          onChange={m => update({start_time: m.toISOString()})}/>

        <Icon name='Calendar'/>
        <DatetimePicker inputProps={{placeholder: 'end time'}}
          value={endTime}
          onChange={m => update({end_time: m.toISOString()})}/>

        <Icon name='Pin-1'/>
        <input type='text' placeholder='location'
          defaultValue={postEdit.location}
          onChange={event => update({location: event.target.value})}/>

          <Icon name='Tag' />
          <input type='text' placeholder='hashtag' value={tag}
          onKeyPress={sanitizeTagInput}
          onChange={event => updateTag(event.target.value)}/>

  }
}
<commit_msg>fixes for event editor<commit_after>import React from 'react'
import { get } from 'lodash/fp'
import Icon from './Icon'
import DatetimePicker from 'react-datetime'
import { sanitizeTagInput } from '../util/textInput'
import { fetchTag } from '../actions'
const { func, object } = React.PropTypes

export const validateTag = (tag, dispatch) => {
  return dispatch(fetchTag(tag))
  .then(({ payload, error, meta: { tagName } }) => {
    if (error && get('response.status', payload) === 404) return true

    window.alert(`The tag "${tagName}" is already in use.`)
    return false
  })
}

export default class EventPostEditor extends React.Component {
  static propTypes = {
    postEdit: object,
    post: object,
    update: func.isRequired
  }

  static contextTypes = {dispatch: func}

  validate = () => {
    const { postEdit: { tag }, post } = this.props
    return tag === get('tag', post) ? true
      : validateTag(tag, this.context.dispatch)
  }

  render () {
    const { postEdit, update } = this.props
    const { start_time, end_time, tag, type } = postEdit
    if (type !== 'event') setTimeout(() => update({type: 'event'})) // smelly

    const startTime = start_time ? new Date(start_time) : null
    const endTime = end_time ? new Date(end_time) : null
    const updateTag = tag => update({tag, tagEdited: true})
    const updateTime = name => time => {
      if (time._isAMomentObject) update({[name]: time.toISOString()})
    }

    return

        <Icon name='Calendar'/>
        <DatetimePicker inputProps={{placeholder: 'start time'}}
          value={startTime}
          onBlur={updateTime('start_time')}/>

        <Icon name='Calendar'/>
        <DatetimePicker inputProps={{placeholder: 'end time'}}
          value={endTime}
          onBlur={updateTime('end_time')}/>

        <Icon name='Pin-1'/>
        <input type='text' placeholder='location'
          defaultValue={postEdit.location}
          onChange={event => update({location: event.target.value})}/>

        <Icon name='Tag' />
        <input type='text' placeholder='hashtag' value={tag || ''}
          onKeyPress={sanitizeTagInput}
          onChange={event => updateTag(event.target.value)}/>

  }
}
<|endoftext|>


<commit_before>import styled from 'styled-components';

export const Wrapper = styled.div`margin-top: 10px;`;

export const Title = styled.h1`
  font-size: 1.5rem;
  text-align: center;
  color: ${props => props.theme.primary};
`;

export const SubTitle = styled.p`
  font-size: 1rem;
  text-align: center;
  color: ${props => props.theme.secondary};
`;
<commit_msg>update components<commit_after>import styled from 'styled-components';

export const Wrapper = styled.div`
  background-color: blue;
  width: 100%;
  height: 100vh;
  margin-top: 10px;
`;

export const Title = styled.h1`
  font-size: 1.5rem;
  text-align: center;
  color: ${props => props.theme.primary};
`;

export const SubTitle = styled.p`
  font-size: 1rem;
  text-align: center;
  color: ${props => props.theme.secondary};
`;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'; // eslint-disable-line

import { connect as reduxConnect } from 'react-redux'; // eslint-disable-line

import { reset } from 'redux-form';

import { setCurrentUser, setCurrentPerms, clearCurrentUser, setOkapiToken, clearOkapiToken, authFailure, clearAuthFailure } from '../../okapiActions';
import Login from './Login';

class LoginCtrl extends Component {
  static contextTypes = {
    store: PropTypes.object,
    router: PropTypes.object,
    authFail: PropTypes.bool,
  }

  static propTypes = {
    authFail: PropTypes.bool,
  }

  constructor(props, context) {
    super();
    this.store = context.store;
    this.router = context.router;
    this.requestLogin = this.requestLogin.bind(this);
    this.sys = require('stripes-loader'); // eslint-disable-line
    this.okapiUrl = this.sys.okapi.url;
    this.tenant = this.sys.okapi.tenant;
    this.store.dispatch(clearAuthFailure());
    this.initialValues = { username: '', password: '' };
  }

  getUser(username) {
    fetch(`${this.okapiUrl}/users?query=(username="${username}")`, { headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'X-Okapi-Token': this.store.getState().okapi.token }) })
      .then((response) => {
        if (response.status >= 400) {
          this.store.dispatch(clearCurrentUser());
        } else {
          response.json().then((json) => {
            this.store.dispatch(setCurrentUser(json.users[0].personal));
          });
        }
      });
  }

  getPerms(username) {
    fetch(`${this.okapiUrl}/perms/users/${username}/permissions?expanded=true&full=true`,
          { headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'X-Okapi-Token': this.store.getState().okapi.token }) })
      .then((response) => {
        if (response.status >= 400) {
          this.store.dispatch(clearCurrentUser());
        } else {
          response.json().then((json) => {
            this.store.dispatch(setCurrentPerms(json));
          });
        }
      });
  }

  requestLogin(data) {
    fetch(`${this.okapiUrl}/authn/login`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'Content-Type': 'application/json' }),
      body: JSON.stringify(data),
    }).then((response) => {
      if (response.status >= 400) {
        this.store.dispatch(clearOkapiToken());
        this.store.dispatch(reset('login'));
        this.initialValues.username = data.username;
        this.store.dispatch(authFailure());
      } else {
        const token = response.headers.get('X-Okapi-Token');
        this.store.dispatch(setOkapiToken(token));
        this.store.dispatch(clearAuthFailure());
        this.getUser(data.username);
        this.getPerms(data.username);
      }
    });
  }

  render() {
    const authFail = this.props.authFail;
    return (
      <Login
        onSubmit={this.requestLogin}
        authFail={authFail}
        initialValues={this.initialValues}
      />
    );
  }
}

function mapStateToProps(state) {
  return { authFail: state.okapi.authFailure };
}

export default reduxConnect(mapStateToProps)(LoginCtrl);
<commit_msg>Transform big, ugly JSON response into a nice simple map of permission-names to display-names.<commit_after>import React, { Component, PropTypes } from 'react'; // eslint-disable-line

import { connect as reduxConnect } from 'react-redux'; // eslint-disable-line

import { reset } from 'redux-form';

import { setCurrentUser, setCurrentPerms, clearCurrentUser, setOkapiToken, clearOkapiToken, authFailure, clearAuthFailure } from '../../okapiActions';
import Login from './Login';

class LoginCtrl extends Component {
  static contextTypes = {
    store: PropTypes.object,
    router: PropTypes.object,
    authFail: PropTypes.bool,
  }

  static propTypes = {
    authFail: PropTypes.bool,
  }

  constructor(props, context) {
    super();
    this.store = context.store;
    this.router = context.router;
    this.requestLogin = this.requestLogin.bind(this);
    this.sys = require('stripes-loader'); // eslint-disable-line
    this.okapiUrl = this.sys.okapi.url;
    this.tenant = this.sys.okapi.tenant;
    this.store.dispatch(clearAuthFailure());
    this.initialValues = { username: '', password: '' };
  }

  getUser(username) {
    fetch(`${this.okapiUrl}/users?query=(username="${username}")`, { headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'X-Okapi-Token': this.store.getState().okapi.token }) })
      .then((response) => {
        if (response.status >= 400) {
          this.store.dispatch(clearCurrentUser());
        } else {
          response.json().then((json) => {
            this.store.dispatch(setCurrentUser(json.users[0].personal));
          });
        }
      });
  }

  getPerms(username) {
    fetch(`${this.okapiUrl}/perms/users/${username}/permissions?expanded=true&full=true`,
          { headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'X-Okapi-Token': this.store.getState().okapi.token }) })
      .then((response) => {
        if (response.status >= 400) {
          this.store.dispatch(clearCurrentUser());
        } else {
          response.json().then((json) => {
            // You are not expected to understand this
            const map = Object.assign({}, ...json.permissionNames.map(p =>({[p.permissionName]: p.displayName})));
            this.store.dispatch(setCurrentPerms(map));
          });
        }
      });
  }

  requestLogin(data) {
    fetch(`${this.okapiUrl}/authn/login`, {
      method: 'POST',
      headers: Object.assign({}, { 'X-Okapi-Tenant': this.tenant, 'Content-Type': 'application/json' }),
      body: JSON.stringify(data),
    }).then((response) => {
      if (response.status >= 400) {
        this.store.dispatch(clearOkapiToken());
        this.store.dispatch(reset('login'));
        this.initialValues.username = data.username;
        this.store.dispatch(authFailure());
      } else {
        const token = response.headers.get('X-Okapi-Token');
        this.store.dispatch(setOkapiToken(token));
        this.store.dispatch(clearAuthFailure());
        this.getUser(data.username);
        this.getPerms(data.username);
      }
    });
  }

  render() {
    const authFail = this.props.authFail;
    return (
      <Login
        onSubmit={this.requestLogin}
        authFail={authFail}
        initialValues={this.initialValues}
      />
    );
  }
}

function mapStateToProps(state) {
  return { authFail: state.okapi.authFailure };
}

export default reduxConnect(mapStateToProps)(LoginCtrl);
<|endoftext|>


<commit_before>import * as React from 'react';

export default class MapLayersPicker extends React.Component {

	constructor (props) {
		super(props);
	}

	componentWillMount () {
	}

	componentDidMount () {
	}

	componentWillUnmount () {
	}

	componentDidUpdate () {
	}

	render () {
		return (

						Map Layers

					{this.props.layers.map(layer => this.renderCheckbox(layer.key, layer.name, 'layers', layer.checked))}

						Transportation

					{this.props.transportation.map(layer => this.renderCheckbox(layer.key, layer.name, 'transportation'))}

		);
	}

	handleChange (key, group, checked) {
		if (group === 'layer') {
			this.props.onLayerChange(key, checked);
		}
		if (group === 'transportation') {
			this.props.onTransportationChange(key, checked);
		}
	}

	renderCheckbox (key, name, group, checked) {
		return (

				<input type="checkbox" id={key} onChange={e => this.handleChange(key, group, e.target.checked)} checked={checked} />
				{name}

		);
	}
}
<commit_msg>fix typo in map layers picker so that map layers action fires<commit_after>import * as React from 'react';

export default class MapLayersPicker extends React.Component {

	constructor (props) {
		super(props);
	}

	componentWillMount () {
	}

	componentDidMount () {
	}

	componentWillUnmount () {
	}

	componentDidUpdate () {
	}

	render () {
		return (

						Map Layers

					{this.props.layers.map(layer => this.renderCheckbox(layer.key, layer.name, 'layers', layer.checked))}

						Transportation

					{this.props.transportation.map(layer => this.renderCheckbox(layer.key, layer.name, 'transportation'))}

		);
	}

	handleChange (key, group, checked) {
		if (group === 'layers') {
			this.props.onLayerChange(key, checked);
		}
		if (group === 'transportation') {
			this.props.onTransportationChange(key, checked);
		}
	}

	renderCheckbox (key, name, group, checked) {
		return (

				<input type="checkbox" id={key} onChange={e => this.handleChange(key, group, e.target.checked)} checked={checked} />
				{name}

		);
	}
}
<|endoftext|>


<commit_before>function makeDocs(start, end, templateDoc) {
  var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}";
  if (end === undefined) {
    end = start;
    start = 0;
  }
  var docs = [];
  for (var i = start; i < end; i++) {
    /* jshint: evil */
    var newDoc = eval("(" + templateDocSrc + ")");
    newDoc._id = (i).toString();
    newDoc.integer = i;
    newDoc.string = (i).toString();
    docs.push(newDoc);
  }
  return docs;
}

function initTestDB(name, callback) {
  // ignore errors, the database might not exist
  Pouch.destroy(name, function(err) {
      if (err && err.status !== 404) {
        console.error(err);
        ok(false, 'failed to open database');
        return start();
      }
    new Pouch(name, function(err, db) {
      if (err) {
        console.error(err);
        ok(false, 'failed to open database');
        return start();
      }
      callback.apply(this, arguments);
    });
  });
}

function initDBPair(local, remote, callback) {
  initTestDB(local, function(err, localDb) {
    initTestDB(remote, function(err, remoteDb) {
      callback(localDb, remoteDb);
    });
  });
}

function generateAdapterUrl(id) {
  var opt = id.split('-');
  if (opt[0] === 'idb') {
    return 'idb://test_suite_db' + opt[1];
  }
  if (opt[0] === 'http') {
    return 'http://localhost:2020/test_suite_db' + opt[1];
  }
}

/**** Test Result Support ***************/
    var doc = {};
    QUnit.jUnitReport = function(report) {
        clearTimeout(scroller);
        doc.report = report;
        doc.completed = new Date().getTime();
        doc.system = System;
        if (window.location.hash && window.location.hash.length > 0) {
            doc.git_commit = window.location.hash.substring(1);
        }
        $('button').on('click', function(){
            submitResults();
        });
    }

    function submitResults() {

        $('button').text('uploading...').attr('disabled', 'disabled');
        initTestDB(generateAdapterUrl('http-1'), function(err, db) {
            if (err) return console.log('Cant open db to store results');
            db.post(doc, function (err, info) {
              if (err) return console.log('Could not post results');
              $.ajax({
                  type: 'POST',
                  url: 'http://localhost:2020/_replicate',
                  data: JSON.stringify({"source":"test_suite_db1","target":"http://reupholster.iriscouch.com/pouch_tests"}),
                  success: function() {
                      $('button').hide();
                      $('body').append('
Submission complete.
')
                  },
                  headers: {
                    Accept: 'application/json'
                  },
                  dataType: 'json',
                  contentType: 'application/json'
              });
            });
        });

    }
$(function(){
    var scroller = setInterval(function(){
        window.scrollTo(0,$(document).height());
    }, 300);
})

<commit_msg>Dont do the scrolling anymore, status on top in new qunit<commit_after>function makeDocs(start, end, templateDoc) {
  var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}";
  if (end === undefined) {
    end = start;
    start = 0;
  }
  var docs = [];
  for (var i = start; i < end; i++) {
    /* jshint: evil */
    var newDoc = eval("(" + templateDocSrc + ")");
    newDoc._id = (i).toString();
    newDoc.integer = i;
    newDoc.string = (i).toString();
    docs.push(newDoc);
  }
  return docs;
}

function initTestDB(name, callback) {
  // ignore errors, the database might not exist
  Pouch.destroy(name, function(err) {
      if (err && err.status !== 404) {
        console.error(err);
        ok(false, 'failed to open database');
        return start();
      }
    new Pouch(name, function(err, db) {
      if (err) {
        console.error(err);
        ok(false, 'failed to open database');
        return start();
      }
      callback.apply(this, arguments);
    });
  });
}

function initDBPair(local, remote, callback) {
  initTestDB(local, function(err, localDb) {
    initTestDB(remote, function(err, remoteDb) {
      callback(localDb, remoteDb);
    });
  });
}

function generateAdapterUrl(id) {
  var opt = id.split('-');
  if (opt[0] === 'idb') {
    return 'idb://test_suite_db' + opt[1];
  }
  if (opt[0] === 'http') {
    return 'http://localhost:2020/test_suite_db' + opt[1];
  }
}

/**** Test Result Support ***************/
    var doc = {};
    QUnit.jUnitReport = function(report) {
        doc.report = report;
        doc.completed = new Date().getTime();
        doc.system = System;
        if (window.location.hash && window.location.hash.length > 0) {
            doc.git_commit = window.location.hash.substring(1);
        }
        $('button').on('click', function(){
            submitResults();
        });
    }

    function submitResults() {

        $('button').text('uploading...').attr('disabled', 'disabled');
        initTestDB(generateAdapterUrl('http-1'), function(err, db) {
            if (err) return console.log('Cant open db to store results');
            db.post(doc, function (err, info) {
              if (err) return console.log('Could not post results');
              $.ajax({
                  type: 'POST',
                  url: 'http://localhost:2020/_replicate',
                  data: JSON.stringify({"source":"test_suite_db1","target":"http://reupholster.iriscouch.com/pouch_tests"}),
                  success: function() {
                      $('button').hide();
                      $('body').append('
Submission complete.
')
                  },
                  headers: {
                    Accept: 'application/json'
                  },
                  dataType: 'json',
                  contentType: 'application/json'
              });
            });
        });

    }

<|endoftext|>