<commit_before>var Note = React.createClass({

    getInitialState: function() {
        return {editing: false}
    },

    edit: function() {
        this.setState({editing: true});
    },

    save: function() {
        this.props.onChange(this.refs.newText.getDOMNode().value, this.props.index);
        this.setState({editing: false});
    },

    remove: function() {
        this.props.onRemove(this.props.index);
    },
    renderDisplay: function() {
        return (

{this.props.children}

                    <button onClick={this.edit}
                            className="btn btn-primary glyphicon glyphicon-pencil"/>
                    <button onClick={this.remove}
                            className="btn btn-danger glyphicon glyphicon-trash"/>

            );
    },
    renderForm: function() {
        return (

            <textarea ref="newText" defaultValue={this.props.children}
            className="form-control"></textarea>
            <button onClick={this.save} className="btn btn-success btn-sm glyphicon glyphicon-floppy-disk" />

            )
    },
    render: function() {
        if (this.state.editing) {
            return this.renderForm();
        }
        else {
            return this.renderDisplay();
        }
    }
});

var Board = React.createClass({
    propTypes: {
        count: function(props, propName) {
            if (typeof props[propName] !== "number"){
                return new Error('The count property must be a number');
            }
            if (props[propName] > 100) {
                return new Error("Creating " + props[propName] + " notes is ridiculous");
            }
        }
    },
    getInitialState: function() {
        return {
            notes: []
        };
    },

    add: function(text) {

        var arr = this.state.notes;

        arr.push(text);

        this.setState({notes: arr});

    },

    update: function(newText, i) {
        var arr = this.state.notes;
        arr[i] = newText;
        this.setState({notes:arr});
    },
    remove: function(i) {
        var arr = this.state.notes;
        arr.splice(i, 1);
        this.setState({notes: arr});
    },
    eachNote: function(note, i) {
        return (
                <Note key={i}
                    index={i}
                    onChange={this.update}
                    onRemove={this.remove}
                >{note}</Note>
            );
    },
    render: function() {
        return (

                    {this.state.notes.map(this.eachNote)}

                    <button className="btn btn-sm glyphicon glyphicon-plus" onClick={this.add} />

        );
    }
});

React.render(<Board count={10}/>,
    document.getElementById('react-container'));

<commit_msg>[FINISH] Adding Notes<commit_after>var Note = React.createClass({

    getInitialState: function() {
        return {editing: false}
    },

    edit: function() {
        this.setState({editing: true});
    },

    save: function() {
        this.props.onChange(this.refs.newText.getDOMNode().value, this.props.index);
        this.setState({editing: false});
    },

    remove: function() {
        this.props.onRemove(this.props.index);
    },
    renderDisplay: function() {
        return (

{this.props.children}

                    <button onClick={this.edit}
                            className="btn btn-primary glyphicon glyphicon-pencil"/>
                    <button onClick={this.remove}
                            className="btn btn-danger glyphicon glyphicon-trash"/>

            );
    },
    renderForm: function() {
        return (

            <textarea ref="newText" defaultValue={this.props.children}
            className="form-control"></textarea>
            <button onClick={this.save} className="btn btn-success btn-sm glyphicon glyphicon-floppy-disk" />

            )
    },
    render: function() {
        if (this.state.editing) {
            return this.renderForm();
        }
        else {
            return this.renderDisplay();
        }
    }
});

var Board = React.createClass({
    propTypes: {
        count: function(props, propName) {
            if (typeof props[propName] !== "number"){
                return new Error('The count property must be a number');
            }
            if (props[propName] > 100) {
                return new Error("Creating " + props[propName] + " notes is ridiculous");
            }
        }
    },
    getInitialState: function() {
        return {
            notes: []
        };
    },

    add: function(text) {

        var arr = this.state.notes;

        arr.push(text);

        this.setState({notes: arr});

    },

    update: function(newText, i) {
        var arr = this.state.notes;
        arr[i] = newText;
        this.setState({notes:arr});
    },
    remove: function(i) {
        var arr = this.state.notes;
        arr.splice(i, 1);
        this.setState({notes: arr});
    },
    eachNote: function(note, i) {
        return (
                <Note key={i}
                    index={i}
                    onChange={this.update}
                    onRemove={this.remove}
                >{note}</Note>
            );
    },
    render: function() {
        return (

                    {this.state.notes.map(this.eachNote)}

                    <button className="btn btn-sm btn-success glyphicon glyphicon-plus" onClick={this.add.bind(null, "New Note")} />

        );
    }
});

React.render(<Board count={10}/>,
    document.getElementById('react-container'));

<|endoftext|>


<commit_before>"use strict";

describe("luga", function() {

	it("Requires jQuery in order to work", function() {
		expect(jQuery).toBeDefined();
	});

	it("Lives inside its own namespace", function() {
		expect(luga).toBeDefined();
	});

	it("Uses jQuery.extend() as simple/crude inheritance solution", function() {

		/* Class Person. */
		function Person(gender) {
			this.gender = gender;
			this.getGender = function() {
				return this.gender;
			};
		}
		/* Class Superstar */
		function Superstar(gender, name) {
			jQuery.extend(this, new Person());
			this.gender = gender;
			this.name = name;
			this.getName = function() {
				return this.name;
			};
		}
		var nicole = new Superstar("female", "Nicole Kidman");

		expect(nicole.getGender()).toEqual("female");
		expect(nicole.getName()).toEqual("Nicole Kidman");
	});

	describe(".namespace()", function() {

		it("Does not override existing namespaces", function() {
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function() {
			luga.namespace("ciccio");
			expect(ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function() {
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe(".extend()", function() {

		it("Offers a simple solution for inheritance among classes", function() {

			/* Class Person. */
			function Person(gender) {
				this.gender = gender;
				this.getGender = function() {
					return this.gender;
				};
			}
			/* Class Superstar */
			function Superstar(gender, name) {
				luga.extend(Person, this, arguments);
				this.gender = gender;
				this.name = name;
				this.getName = function() {
					return this.name;
				};
			}
			var nicole = new Superstar("female", "Nicole Kidman");

			expect(nicole.getGender()).toEqual("female");
			expect(nicole.getName()).toEqual("Nicole Kidman")
		});

	});

	describe(".merge()", function() {

		it("Merges the contents of two objects together into the first object", function() {
			var config = {letter: "a", number: 1};
			var params = {number: 2, symbol: "@"};
			luga.merge(config, params);
			expect(config).toEqual({letter: "a", number: 2, symbol: "@"});
		});

	});

});

describe("luga.utils stores generic, utilities and static methods", function() {

	it("Lives inside its own namespace", function() {
		expect(luga.utils).toBeDefined();
	});

	describe(".formatString()", function() {
		it("Given a string containing placeholders, assembles a new string", function() {
			expect(luga.utils.formatString("My name is {0} {1}", ["Ciccio", "Pasticcio"])).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {0} is just a {0}", ["test"])).toEqual("This test is just a test");
			expect(luga.utils.formatString("My name is {firstName} {lastName}", {firstName: "Ciccio", lastName: "Pasticcio"})).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {str} is just a {str}", { str:"test"} )).toEqual("This test is just a test");
		});
	});

	describe(".stringToFunction(). Given the name of a function as a string", function() {

		it("Returns the relevant function if it finds it inside the window/global scope", function() {
			window.myFunc = function(){};
			var result = luga.utils.stringToFunction("myFunc");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Returns null if the function does not exist", function() {
			expect(luga.utils.stringToFunction("missing")).toBeNull();
		});

		it("Or if the variable exists, but it's not a function", function() {
			window.str = "ciao";
			expect(luga.utils.stringToFunction("str")).toBeNull();
		});

	});

	describe(".displayMessage()", function() {

		it("Displays a message box above a given node", function() {
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayMessage(formNode, "Ciao");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Ciao");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayErrorMessage()", function() {

		it("Displays an error box above a given node", function() {
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.ERROR_MESSAGE);
		});

	});

});<commit_msg>Removed redundant test spec<commit_after>"use strict";

describe("luga", function() {

	it("Requires jQuery in order to work", function() {
		expect(jQuery).toBeDefined();
	});

	it("Lives inside its own namespace", function() {
		expect(luga).toBeDefined();
	});

	describe(".namespace()", function() {

		it("Does not override existing namespaces", function() {
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function() {
			luga.namespace("ciccio");
			expect(ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function() {
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe(".extend()", function() {

		it("Offers a simple solution for inheritance among classes", function() {

			/* Class Person. */
			function Person(gender) {
				this.gender = gender;
				this.getGender = function() {
					return this.gender;
				};
			}
			/* Class Superstar */
			function Superstar(gender, name) {
				luga.extend(Person, this, arguments);
				this.gender = gender;
				this.name = name;
				this.getName = function() {
					return this.name;
				};
			}
			var nicole = new Superstar("female", "Nicole Kidman");

			expect(nicole.getGender()).toEqual("female");
			expect(nicole.getName()).toEqual("Nicole Kidman")
		});

	});

	describe(".merge()", function() {

		it("Merges the contents of two objects together into the first object", function() {
			var config = {letter: "a", number: 1};
			var params = {number: 2, symbol: "@"};
			luga.merge(config, params);
			expect(config).toEqual({letter: "a", number: 2, symbol: "@"});
		});

	});

});

describe("luga.utils stores generic, utilities and static methods", function() {

	it("Lives inside its own namespace", function() {
		expect(luga.utils).toBeDefined();
	});

	describe(".formatString()", function() {
		it("Given a string containing placeholders, assembles a new string", function() {
			expect(luga.utils.formatString("My name is {0} {1}", ["Ciccio", "Pasticcio"])).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {0} is just a {0}", ["test"])).toEqual("This test is just a test");
			expect(luga.utils.formatString("My name is {firstName} {lastName}", {firstName: "Ciccio", lastName: "Pasticcio"})).toEqual("My name is Ciccio Pasticcio");
			expect(luga.utils.formatString("This {str} is just a {str}", { str:"test"} )).toEqual("This test is just a test");
		});
	});

	describe(".stringToFunction(). Given the name of a function as a string", function() {

		it("Returns the relevant function if it finds it inside the window/global scope", function() {
			window.myFunc = function(){};
			var result = luga.utils.stringToFunction("myFunc");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Returns null if the function does not exist", function() {
			expect(luga.utils.stringToFunction("missing")).toBeNull();
		});

		it("Or if the variable exists, but it's not a function", function() {
			window.str = "ciao";
			expect(luga.utils.stringToFunction("str")).toBeNull();
		});

	});

	describe(".displayMessage()", function() {

		it("Displays a message box above a given node", function() {
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayMessage(formNode, "Ciao");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Ciao");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayErrorMessage()", function() {

		it("Displays an error box above a given node", function() {
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.ERROR_MESSAGE);
		});

	});

});<|endoftext|>


<commit_before>var through2 = require('through2');
var hyperquest = require('hyperquest');
var bl = require('bl');
var exercise = require('workshopper-exercise')();
var filecheck = require('workshopper-exercise/filecheck');
var execute = require('workshopper-exercise/execute');
var comparestdout = require('workshopper-exercise/comparestdout');
var fs = require('fs');

// checks that the submission file actually exists
exercise = filecheck(exercise);

// execute the solution and submission in parallel with spawn()
exercise = execute(exercise);

// compare stdout of solution and submission
exercise = comparestdout(exercise);

// checks if a file exists, named a certain way, containing the mustache we want
fs.readFile('/atoms/01-tutorial-button.mustache', 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }
  if((data.indexOf('<button>') !== -1) & (data.indexOf('</button>') !== -1)) {
    console.log(data);
  }
}); //data is the contents of the file, for this lesson we want it to have: <button></button>

module.exports = exercise;
<commit_msg>update file path for readFile<commit_after>var through2 = require('through2');
var hyperquest = require('hyperquest');
var bl = require('bl');
var exercise = require('workshopper-exercise')();
var filecheck = require('workshopper-exercise/filecheck');
var execute = require('workshopper-exercise/execute');
var comparestdout = require('workshopper-exercise/comparestdout');
var fs = require('fs');

// checks that the submission file actually exists
exercise = filecheck(exercise);

// execute the solution and submission in parallel with spawn()
exercise = execute(exercise);

// compare stdout of solution and submission
// exercise = comparestdout(exercise);

// checks if a file exists, named a certain way, containing the mustache we want
fs.readFile('solution/atoms/01-tutorial-button.mustache', 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }
  if((data.indexOf('<button>') !== -1) & (data.indexOf('</button>') !== -1)) {
    console.log(data);
  }
}); //data is the contents of the file, for this lesson we want it to have: <button></button>

module.exports = exercise;
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var path = require('path');
var fs = require('fs');
var $$ = require('../../lib/js-yaml/common');

var _common = module.exports = {};
$$.extend(_common, $$);

function toArray(sequence) {
  if (Array.isArray(sequence)) {
    return sequence;
  } else if ($$.isNothing(sequence)) {
    return [];
  } else {
    return [ sequence ];
  }
}

function DataFile(filepath) {
  this.path = path.normalize(filepath);
  this.encoding = 'utf8';
  this.content = DataFile.read(this);
}

DataFile.read = function read(dataFile) {
  if ('.js' === path.extname(dataFile.path)) {
    return require(dataFile.path);
  } else {
    return fs.readFileSync(dataFile.path, dataFile.encoding);
  }
};

function makeClassConstructor(Class, params) {
  var mapKeys      = params.map      || {},
      requiredKeys = params.required || [],
      optionalKeys = params.optional || [];

  return function fromYAMLNode(object, explicit) {
    assert.equal(typeof object, 'object');

    $$.each(mapKeys, function (newKey, oldKey) {
      if (object.hasOwnProperty(oldKey)) {
        object[newKey] = object[oldKey];
        delete object[oldKey];
      }
    });

    requiredKeys.forEach(function (key) {
      assert(object.hasOwnProperty(key),
        'Mapping must contain ' + JSON.stringify(key) + ' key');
    });

    $$.each(object, function (value, key) {
      var hasAsRequired = (0 <= requiredKeys.indexOf(key)),
          hasAsOptional = (0 <= optionalKeys.indexOf(key));

      assert((hasAsRequired || hasAsOptional),
        'Mapping should not contain ' + JSON.stringify(key) + ' key');
    });

    return new Class(object);
  };
}

_common.toArray = toArray;
_common.DataFile = DataFile;
_common.makeClassConstructor = makeClassConstructor;
<commit_msg>Fix test helpers.<commit_after>'use strict';

var assert = require('assert');
var path = require('path');
var fs = require('fs');
var $$ = require('../../lib/js-yaml/common');
var NIL = $$.NIL;

var _hasOwnProperty = Object.prototype.hasOwnProperty;

var _common = module.exports = {};
$$.extend(_common, $$);

function toArray(sequence) {
  if (Array.isArray(sequence)) {
    return sequence;
  } else if ($$.isNothing(sequence)) {
    return [];
  } else {
    return [ sequence ];
  }
}

function DataFile(filepath) {
  this.path = path.normalize(filepath);
  this.encoding = 'utf8';
  this.content = DataFile.read(this);
}

DataFile.read = function read(dataFile) {
  if ('.js' === path.extname(dataFile.path)) {
    return require(dataFile.path);
  } else {
    return fs.readFileSync(dataFile.path, dataFile.encoding);
  }
};

function makeClassConstructor(Class, params) {
  var mapKeys      = params.map      || {},
      requiredKeys = params.required || [],
      optionalKeys = params.optional || [];

  return function fromYAMLNode(object, explicit) {
    if (!$$.isObject(object)) {
      return NIL;
    }

    $$.each(mapKeys, function (newKey, oldKey) {
      if (_hasOwnProperty.call(object, oldKey)) {
        object[newKey] = object[oldKey];
        delete object[oldKey];
      }
    });

    requiredKeys.forEach(function (key) {
      if (!_hasOwnProperty.call(object, key)) {
        return NIL;
      }
    });

    $$.each(object, function (value, key) {
      var hasAsRequired = (0 <= requiredKeys.indexOf(key)),
          hasAsOptional = (0 <= optionalKeys.indexOf(key));

      if (!hasAsRequired && !hasAsOptional) {
        return NIL;
      }
    });

    return new Class(object);
  };
}

_common.toArray = toArray;
_common.DataFile = DataFile;
_common.makeClassConstructor = makeClassConstructor;
<|endoftext|>


<commit_before>//
//  frontier.js
//
//  main lobby server
//

require('scalra')('dev');

// show debug / warning / error messages
LOG.setLevel(2);
LOG.show('all');

var l_name = 'Monitor';

//-----------------------------------------
// define local variables
//
//-----------------------------------------

// a list of names for all collections to be created
var collections =  ['log'];

var config = {
    path:               __dirname,
    handlers: [
		{file: 'log.js', owner: 'SR'}
    ],
    components: [
        //SR.Component.REST('HTTP', ['REST_handle.js']),      // start a HTTP server
		//SR.Component.REST('HTTPS', ['REST_handle.js']),     // start a HTTPs server
    ],
	modules: {
		'reporting': {}
	}
};

// create frontier
var l_frontier = new SR.Frontier(config);

// set custom REST handlers
SR.REST.addHandler('REST_handle.js');
SR.REST.addHandler('REST_execute.js');

//
// define API
//

// API to get system paths
SR.API.add('_SYS_PATH', {
	type:	'string'
}, function (args, onDone) {
	switch (args.type) {
		case 'demo': {
			var path = SR.path.resolve(SR.Settings.SR_PATH, 'demo');
			return onDone(null, path);
		}
		default: {
			// check if the requested path exists in settings
			if (SR.Settings.hasOwnProperty(args.type)) {
				return onDone(null, SR.Settings[args.type]);
			}
			return onDone('unknown type [' + args.type + ']');
		}
	}
});

// API to set system paths
SR.API.add('_SET_SYS_PATH', {
	type:	'string',
	path:	'string'
}, function (args, onDone) {

	if (SR.Settings.hasOwnProperty(args.type) === true) {
		return onDone('[' + args.type + '] already set');
	}
	SR.Settings[args.type] = args.path;
	l_buildPaths();
	onDone(null);
});

// server mapping from id -> info
var l_servers = {};

// API to subscribe for monitor alerts
SR.API.add('_MONITOR_ALERT', {
	id:	'string'
}, function (args, onDone, extra) {
	LOG.warn('server [' + args.id + '] subscribing alert:', l_name);
	LOG.warn(extra.conn, l_name);
	l_servers[args.id] = extra.conn;
	onDone(null);
});

// remove disconnected servers from list
SR.Callback.onDisconnect(function (conn) {
	for (var id in l_servers) {
		if (l_servers[id] === conn) {
			LOG.warn('server [' + id + '] disconnected', l_name);
			delete l_servers[id];
			return;
		}
	}
});

// API to start servers
SR.API.add('_START_SERVER', {
	owner:		'string',
	project:	'string',
	name:		'string',
	size:		'+number'
}, function (args, onDone) {

});

// API to stop servers (based on serverID)
SR.API.add('_STOP_SERVER', {
	id:		'string'
}, function (args, onDone) {
	if (l_servers.hasOwnProperty(args.id) === false) {
		return onDone('server [' + args.id + '] not registered');
	}

	LOG.warn('sending MONITOR_ALERT to [' + args.id + ']...', l_name);

	// notify server to shutdown via previous connection
	SR.EventManager.send('_MONITOR_ALERT', {type: 'SHUTDOWN'}, [l_servers[args.id]]);
	onDone(null);
});

// list of subscribers to screen (server id -> conn object list)
var l_subscribers = {};

const spawn = require('child_process').spawn;

// API to subscribe for the continous output wrirrrt
SR.API.add('_SUBSCRIBE_LOG', {
	owner:		'string',
	project:	'string',
	name:		'string'
}, function (args, onDone, extra) {

	LOG.warn('extra: ');
	LOG.warn(extra);

	var owner = args.owner;
	var project = args.project;
	var name = args.name;

	var serverID = owner + '-' + project + '-' + name;

	// check if already subscribed
	if (l_subscribers.hasOwnProperty(serverID) === false) {
		l_subscribers[serverID] = {
			proc: null,
			conns: []
		}
	}

	var subscriber = l_subscribers[serverID];

	// add a new subscriber connection (only if new)
	if (subscriber.conns.indexOf(extra.conn) === (-1)) {
		subscriber.conns.push(extra.conn);
	}

	// subscribe for output
	//SR.Comm.subscribe(extra.conn.connID, serverID, extra.conn);

	// return serverID to indicate subscription success
	onDone(null, {serverID: serverID, data: 'showing screen for [' + serverID + ']'});

	if (subscriber.proc !== null) {
		LOG.warn('screen output for [' + serverID + '] already exists, show existing tailing process', l_name);
		return;
	}

	// start tailing the project's output
	try {

		var log_file = SR.path.resolve(SR.Settings.PATH_USERBASE,
									   owner,
									   project,
									   'log',
									   'output.log');

		LOG.warn('tailing [' + log_file + ']...', l_name);
		subscriber.proc = spawn('tail', ['-n', '1000', '-f', log_file]);

		var onData = function (d) {
			// NOTE: strings like '[monitor]' will get replaced as well (incorrectly)
			var logData = d.toString().replace(/\[m/g, '[');
			//var logData = d.toString();

			//SR.Comm.publish(serverID, {
			//	serverID: serverID,
			//	data: logData
			//});
			//LOG.warn('sending to ' + subscriber.conns.length + ' subscribers...', l_name);
			SR.EventManager.send('_SUBSCRIBE_LOG',
								 {err: null, result: {serverID: serverID, data: logData}},
								 subscriber.conns);
		}

		subscriber.proc.stdout.on('data', onData);
		subscriber.proc.stderr.on('data', onData);

		subscriber.proc.on('exit', function (code) {
			LOG.warn('screen spawn exit: ' + code, l_name);
			subscriber.proc = undefined;
			delete l_subscribers[serverID];
		});
	}
	catch (e) {
		LOG.error(e, l_name);
	}
});

// remove subscription or stop tailing if nobody's interested in viewing
// TODO: should have system-mechanism for auto-unsubscribe
SR.Callback.onDisconnect(function (conn) {
	for (var serverID in l_subscribers) {
		for (var i=0; i < l_subscribers[serverID].conns.length; i++) {

			var c = l_subscribers[serverID].conns[i];
			if (c.connID !== conn.connID)
				continue;

			// remove the connection
			l_subscribers[serverID].conns.splice(i, 1);

			// check if nobody's subscribing, then should remove tailing process
			if (l_subscribers[serverID].conns.length === 0) {
				LOG.warn('no subscriber for [' + serverID + '] screen, stop tailing...');
				l_subscribers[serverID].proc.kill('SIGHUP');
				delete l_subscribers[serverID];
			}
			return;
		}
	}
});

var l_buildPaths = function () {

	// (re-)build path settings
	if (SR.Settings.PATH_USERBASE) {
		SR.Settings.PATH_LIB = SR.path.join(SR.Settings.PATH_USERBASE, 'lib');
	}
}

// execute all the steps for running a server
l_frontier.init(function () {

    // callback when lobby is started
    LOG.warn('monitor started successfully', l_name);
});
<commit_msg>fix: _SUBSCRIBE_LOG / _UNSUBSCRIBE_LOG now shows log correctly to multiple project screens (or multiple screen for same project) at once<commit_after>//
//  frontier.js
//
//  main lobby server
//

require('scalra')('dev');

// show debug / warning / error messages
LOG.setLevel(2);
LOG.show('all');

var l_name = 'Monitor';

//-----------------------------------------
// define local variables
//
//-----------------------------------------

// a list of names for all collections to be created
var collections =  ['log'];

var config = {
    path:               __dirname,
    handlers: [
		{file: 'log.js', owner: 'SR'}
    ],
    components: [
        //SR.Component.REST('HTTP', ['REST_handle.js']),      // start a HTTP server
		//SR.Component.REST('HTTPS', ['REST_handle.js']),     // start a HTTPs server
    ],
	modules: {
		'reporting': {}
	}
};

// create frontier
var l_frontier = new SR.Frontier(config);

// set custom REST handlers
SR.REST.addHandler('REST_handle.js');
SR.REST.addHandler('REST_execute.js');

//
// define API
//

// API to get system paths
SR.API.add('_SYS_PATH', {
	type:	'string'
}, function (args, onDone) {
	switch (args.type) {
		case 'demo': {
			var path = SR.path.resolve(SR.Settings.SR_PATH, 'demo');
			return onDone(null, path);
		}
		default: {
			// check if the requested path exists in settings
			if (SR.Settings.hasOwnProperty(args.type)) {
				return onDone(null, SR.Settings[args.type]);
			}
			return onDone('unknown type [' + args.type + ']');
		}
	}
});

// API to set system paths
SR.API.add('_SET_SYS_PATH', {
	type:	'string',
	path:	'string'
}, function (args, onDone) {

	if (SR.Settings.hasOwnProperty(args.type) === true) {
		return onDone('[' + args.type + '] already set');
	}
	SR.Settings[args.type] = args.path;
	l_buildPaths();
	onDone(null);
});

// server mapping from id -> info
var l_servers = {};

// API to subscribe for monitor alerts
SR.API.add('_MONITOR_ALERT', {
	id:	'string'
}, function (args, onDone, extra) {
	LOG.warn('server [' + args.id + '] subscribing alert:', l_name);
	LOG.warn(extra.conn, l_name);
	l_servers[args.id] = extra.conn;
	onDone(null);
});

// remove disconnected servers from list
SR.Callback.onDisconnect(function (conn) {
	for (var id in l_servers) {
		if (l_servers[id] === conn) {
			LOG.warn('server [' + id + '] disconnected', l_name);
			delete l_servers[id];
			return;
		}
	}
});

// API to start servers
SR.API.add('_START_SERVER', {
	owner:		'string',
	project:	'string',
	name:		'string',
	size:		'+number'
}, function (args, onDone) {

});

// API to stop servers (based on serverID)
SR.API.add('_STOP_SERVER', {
	id:		'string'
}, function (args, onDone) {
	if (l_servers.hasOwnProperty(args.id) === false) {
		return onDone('server [' + args.id + '] not registered');
	}

	LOG.warn('sending MONITOR_ALERT to [' + args.id + ']...', l_name);

	// notify server to shutdown via previous connection
	SR.EventManager.send('_MONITOR_ALERT', {type: 'SHUTDOWN'}, [l_servers[args.id]]);
	onDone(null);
});

// list of subscribers to screen (server id -> conn object list)
var l_subscribers = {};

const spawn = require('child_process').spawn;

var l_tailOutput = function (owner, project, name, subscriber) {

	var serverID = owner + '-' + project + '-' + name;

	// start tailing the project's output
	try {

		var log_file = SR.path.resolve(SR.Settings.PATH_USERBASE,
									   owner,
									   project,
									   'log',
									   'output.log');

		LOG.warn('tailing [' + log_file + ']...', l_name);
		subscriber.proc = spawn('tail', ['-n', '1000', '-f', log_file]);

		var onData = function (d) {
			// NOTE: strings like '[monitor]' will get replaced as well (incorrectly)
			var logData = d.toString().replace(/\[m/g, '[');

			SR.EventManager.send('_SUBSCRIBE_LOG',
								 {err: null, result: {subID: subscriber.subID, data: logData}},
								 subscriber.conns);
		}

		subscriber.proc.stdout.on('data', onData);
		subscriber.proc.stderr.on('data', onData);

		subscriber.proc.on('exit', function (code) {
			LOG.warn('screen spawn exit: ' + code, l_name);
			subscriber.proc = undefined;
			delete l_subscribers[serverID];
		});
	}
	catch (e) {
		LOG.error(e, l_name);
	}
}

// API to subscribe for the continous output of a log file
SR.API.add('_SUBSCRIBE_LOG', {
	owner:		'string',
	project:	'string',
	name:		'string',
	subID:		'string'
}, function (args, onDone, extra) {

	var owner = args.owner;
	var project = args.project;
	var name = args.name;

	var serverID = owner + '-' + project + '-' + name;

	// check if already subscribed
	if (l_subscribers.hasOwnProperty(serverID) === false) {
		l_subscribers[serverID] = {
			proc: null,
			subID: args.subID,
			conns: []
		}
	}

	var subscriber = l_subscribers[serverID];

	// add a new subscriber connection (only if new)
	if (subscriber.conns.indexOf(extra.conn) === (-1)) {
		subscriber.conns.push(extra.conn);
	}

	// return subID to indicate subscription success
	onDone(null, {subID: args.subID, data: 'showing screen for [' + serverID + ']...\n'});

	if (subscriber.proc !== null) {
		LOG.warn('screen output for [' + serverID + '] already exists, show existing tailing process', l_name);
		return;
	}

	// use another function to make sure 'subscriber' is still valid in callback
	l_tailOutput(owner, project, name, subscriber);
});

// API to subscribe for the continous output of a log file
SR.API.add('_UNSUBSCRIBE_LOG', {
	subID:			'string',
	connID:			'+string'
}, function (args, onDone, extra) {

	var connID = args.connID || extra.conn.connID;
	var subID = args.subID;
	var subscriber = undefined;

	for (var serverID in l_subscribers) {

		if (l_subscribers[serverID].subID === subID) {
			subscriber = l_subscribers[serverID];
			break;
		}
	}

	if (!subscriber) {
		return onDone('subID [' + args.subID + '] not found');
	}

	// find & remove the connection object
	for (var i=0; i < subscriber.conns.length; i++) {
		if (subscriber.conns[i].connID === connID) {

			subscriber.conns.splice(i, 1);

			// check if nobody's subscribing, then should remove tailing process
			if (subscriber.conns.length === 0) {
				LOG.warn('no subscriber for [' + serverID + '] screen, stop tailing...', l_name);
				subscriber.proc.kill('SIGHUP');
				delete l_subscribers[serverID];
			}
			return onDone(null);
		}
	}
	return onDone('cannot find connection to remove');
});

// remove subscription or stop tailing if nobody's interested in viewing
// TODO: should have system-mechanism for auto-unsubscribe
SR.Callback.onDisconnect(function (conn) {
	for (var serverID in l_subscribers) {
		for (var i=0; i < l_subscribers[serverID].conns.length; i++) {

			var c = l_subscribers[serverID].conns[i];
			if (c.connID !== conn.connID)
				continue;

			// remove the connection
			SR.API._UNSUBSCRIBE_LOG({
				serverID: serverID,
				connID: c.connID
			});

			// NOTE: we just break because it's possible the same connection (from a single icpm instance)
			// may subscribe for multiple serverID's output
			break;
		}
	}
});

var l_buildPaths = function () {

	// (re-)build path settings
	if (SR.Settings.PATH_USERBASE) {
		SR.Settings.PATH_LIB = SR.path.join(SR.Settings.PATH_USERBASE, 'lib');
	}
}

// execute all the steps for running a server
l_frontier.init(function () {

    // callback when lobby is started
    LOG.warn('monitor started successfully', l_name);
});
<|endoftext|>


<commit_before>const ist = require("ist")
const {eq, doc, p, blockquote, ul, ol, li, hr, br} = require("prosemirror-test-builder")
const {NodeSelection, TextSelection} = require("prosemirror-state")
const {Slice, Fragment} = require("prosemirror-model")
const {tempEditor} = require("./view")

const {__serializeForClipboard: serializeForClipboard, __parseFromClipboard: parseFromClipboard} = require("..")

describe("Clipboard interface", () => {
  it("copies only the node for a node selection", () => {
    let d = doc(blockquote(p("a"), "", hr), p("b"))
    let view = tempEditor({doc: d})
    let {dom} = serializeForClipboard(view, NodeSelection.create(d, d.tag.a).content())
    ist(dom.innerHTML, '
')
    ist(parseFromClipboard(view, "", dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.a + 1), eq)
  })

  it("includes context for text selections", () => {
    let d = doc(blockquote(ul(li(p("foo"), p("bar")))))
    let view = tempEditor({doc: d})
    let slice = TextSelection.create(d, d.tag.a, d.tag.b).content(), {dom, text} = serializeForClipboard(view, slice)
    ist(dom.innerHTML, '

o

b

')
    ist(parseFromClipboard(view, text, dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.b, true), eq)
    ist(parseFromClipboard(view, text, dom.innerHTML, true, d.resolve(1)), new Slice(doc(p("o"), p("b")).content, 1, 1), eq)
  })

  it("preserves open nodes", () => {
    let d = doc(blockquote(blockquote(p("foo"))))
    let view = tempEditor({doc: d})
    let slice = new Slice(Fragment.from(d.firstChild), 1, 1)
    let html = serializeForClipboard(view, slice).dom.innerHTML
    let parsed = parseFromClipboard(view, "-", html, false, d.resolve(1))
    ist(parsed, slice, eq)
  })

  it("uses clipboardTextSerializer when given", () => {
    let view = tempEditor({doc: doc(p("hello")),
                           clipboardTextSerializer(_) { return "OK" }})
    let {text} = serializeForClipboard(view, view.state.doc.slice(1, 6))
    ist(text, "OK")
  })

  it("can read external HTML", () => {
    let view = tempEditor(), $p = view.state.doc.resolve(1)
    ist(parseFromClipboard(view, "", "
hello

", false, $p), new Slice(doc(p("hello"), hr).content, 1, 0), eq)
    ist(parseFromClipboard(view, "", "
hello
bar", false, $p), new Slice(doc(p("hello"), p("bar")).content, 1, 1), eq)
  })

  it("will sanely clean up top-level nodes in HTML", () => {
    let view = tempEditor(), $p = view.state.doc.resolve(1)
    ist(parseFromClipboard(view, "", "

foo

bar
", false, $p),
        new Slice(doc(ul(li(p("foo"))), p("bar", br)).content, 3, 1), eq)
    ist(parseFromClipboard(view, "", "

foo

bar

x
", false, $p),
        new Slice(doc(ul(li(p("foo"))), p("bar", br), p("x")).content, 3, 1), eq)
    ist(parseFromClipboard(view, "", "
foo

bar

x
", false, $p),
        new Slice(doc(ol(li(p("foo")), li(p("bar"))), p("x")).content, 3, 1), eq)
  })

  it("will call transformPastedHTML", () => {
    let view = tempEditor({transformPastedHTML(_) { return "abc" }})
    ist(parseFromClipboard(view, "", "def", false, view.state.doc.resolve(1)),
        new Slice(p("abc").content, 0, 0), eq)
  })

  it("will call transformPastedText", () => {
    let view = tempEditor({transformPastedText(_) { return "abc" }})
    ist(parseFromClipboard(view, "def", null, false, view.state.doc.resolve(1)),
        new Slice(doc(p("abc")).content, 1, 1), eq)
  })

  it("allows text parsing to be overridden with clipboardTextParser", () => {
    let view = tempEditor({clipboardTextParser(text) { return doc(p(text.toUpperCase())).slice(1, text.length + 1) }})
    ist(parseFromClipboard(view, "abc", null, false, view.state.doc.resolve(1)),
        new Slice(p("ABC").content, 0, 0), eq)
  })

  it("preserves attributes", () => {
    let d = doc(ol({order: 3}, li(p("foo"))))
    let view = tempEditor({doc: d})
    let {dom, text} = serializeForClipboard(view, TextSelection.create(d, d.tag.a, d.tag.b).content())
    ist(parseFromClipboard(view, text, dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.b, true), eq)
  })
})
<commit_msg>Fix test for clipboard parsing change<commit_after>const ist = require("ist")
const {eq, doc, p, blockquote, ul, ol, li, hr, br} = require("prosemirror-test-builder")
const {NodeSelection, TextSelection} = require("prosemirror-state")
const {Slice, Fragment} = require("prosemirror-model")
const {tempEditor} = require("./view")

const {__serializeForClipboard: serializeForClipboard, __parseFromClipboard: parseFromClipboard} = require("..")

describe("Clipboard interface", () => {
  it("copies only the node for a node selection", () => {
    let d = doc(blockquote(p("a"), "", hr), p("b"))
    let view = tempEditor({doc: d})
    let {dom} = serializeForClipboard(view, NodeSelection.create(d, d.tag.a).content())
    ist(dom.innerHTML, '
')
    ist(parseFromClipboard(view, "", dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.a + 1), eq)
  })

  it("includes context for text selections", () => {
    let d = doc(blockquote(ul(li(p("foo"), p("bar")))))
    let view = tempEditor({doc: d})
    let slice = TextSelection.create(d, d.tag.a, d.tag.b).content(), {dom, text} = serializeForClipboard(view, slice)
    ist(dom.innerHTML, '

o

b

')
    ist(parseFromClipboard(view, text, dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.b, true), eq)
    ist(parseFromClipboard(view, text, dom.innerHTML, true, d.resolve(1)), new Slice(doc(p("o"), p("b")).content, 1, 1), eq)
  })

  it("preserves open nodes", () => {
    let d = doc(blockquote(blockquote(p("foo"))))
    let view = tempEditor({doc: d})
    let slice = new Slice(Fragment.from(d.firstChild), 1, 1)
    let html = serializeForClipboard(view, slice).dom.innerHTML
    let parsed = parseFromClipboard(view, "-", html, false, d.resolve(1))
    ist(parsed, slice, eq)
  })

  it("uses clipboardTextSerializer when given", () => {
    let view = tempEditor({doc: doc(p("hello")),
                           clipboardTextSerializer(_) { return "OK" }})
    let {text} = serializeForClipboard(view, view.state.doc.slice(1, 6))
    ist(text, "OK")
  })

  it("can read external HTML", () => {
    let view = tempEditor(), $p = view.state.doc.resolve(1)
    ist(parseFromClipboard(view, "", "
hello

", false, $p), new Slice(doc(p("hello"), hr).content, 1, 0), eq)
    ist(parseFromClipboard(view, "", "
hello
bar", false, $p), new Slice(doc(p("hello"), p("bar")).content, 1, 1), eq)
  })

  it("will sanely clean up top-level nodes in HTML", () => {
    let view = tempEditor(), $p = view.state.doc.resolve(1)
    ist(parseFromClipboard(view, "", "

foo

bar
baz", false, $p),
        new Slice(doc(ul(li(p("foo"))), p("bar", br, "baz")).content, 3, 1), eq)
    ist(parseFromClipboard(view, "", "

foo

bar

x
", false, $p),
        new Slice(doc(ul(li(p("foo"))), p("bar", br), p("x")).content, 3, 1), eq)
    ist(parseFromClipboard(view, "", "
foo

bar

x
", false, $p),
        new Slice(doc(ol(li(p("foo")), li(p("bar"))), p("x")).content, 3, 1), eq)
  })

  it("will call transformPastedHTML", () => {
    let view = tempEditor({transformPastedHTML(_) { return "abc" }})
    ist(parseFromClipboard(view, "", "def", false, view.state.doc.resolve(1)),
        new Slice(p("abc").content, 0, 0), eq)
  })

  it("will call transformPastedText", () => {
    let view = tempEditor({transformPastedText(_) { return "abc" }})
    ist(parseFromClipboard(view, "def", null, false, view.state.doc.resolve(1)),
        new Slice(doc(p("abc")).content, 1, 1), eq)
  })

  it("allows text parsing to be overridden with clipboardTextParser", () => {
    let view = tempEditor({clipboardTextParser(text) { return doc(p(text.toUpperCase())).slice(1, text.length + 1) }})
    ist(parseFromClipboard(view, "abc", null, false, view.state.doc.resolve(1)),
        new Slice(p("ABC").content, 0, 0), eq)
  })

  it("preserves attributes", () => {
    let d = doc(ol({order: 3}, li(p("foo"))))
    let view = tempEditor({doc: d})
    let {dom, text} = serializeForClipboard(view, TextSelection.create(d, d.tag.a, d.tag.b).content())
    ist(parseFromClipboard(view, text, dom.innerHTML, false, d.resolve(1)), d.slice(d.tag.a, d.tag.b, true), eq)
  })
})
<|endoftext|>


<commit_before>process.env.NODE_ENV = 'test';

const app = require('../../src/app');
const superTest = require('supertest-promised')(app);
const test = require('ava');

module.exports = { superTest, app, test };
<commit_msg>WIP: added dummy test to avoid uncaught exception error on auto-test<commit_after>process.env.NODE_ENV = 'test';

const app = require('../../src/app');
const superTest = require('supertest-promised')(app);
const test = require('ava');

test('dummy test', (t) => {
  t.is(true, true);
});

module.exports = { superTest, app, test };
<|endoftext|>


<commit_before>function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrf_token = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function sameOrigin(url) {
    // If url starts with / it's relative and same origin
    if (url.substr(0, 1) == '/') {
        return true;
    }
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/');
    // or any other URL that isn't scheme relative or absolute i.e relative. !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrf_token);
        }
    }
});

Em.TextField.reopen({
    // Add 'step' to attributeBinding
    attributeBindings: ['type', 'value', 'size', 'step']
});

// TODO Rename App to BlueBottle, BB or BBApp.
App = Em.Application.create({
    VERSION: '1.0.0',

    ready: function() {
        //..init code goes here...
    },

    _getTemplate: function(template, callback) {
        var hash = {};
        hash.url = '/en/templates/' + template + '.hbs';
        hash.type = 'GET';
        hash.contentType = 'application/json';
        hash.success = callback;
        hash.error = function(jqXHR, textStatus, errorThrown) {
            throw errorThrown + ' ' + hash.url;
        };
        jQuery.ajax(hash);
    },

    loadTemplate: function(templateFilename) {
        this._getTemplate(templateFilename, function(data) {
            // Iterate through handlebar tags
            $(data).filter('script[type="text/x-handlebars"]').each(function() {
                var templateName = $(this).attr('data-template-name');
                var raw = $(this).html();
                Em.TEMPLATES[templateName] = Em.Handlebars.compile(raw);
            });
        });
    },

    loadTemplates: function(templateFilenames) {
        var app = this;
        templateFilenames.forEach(function (templateFilename) {
            app.loadTemplate(templateFilename);
        });
    }
});

// Load the Handlebar templates.
// TODO: This is race condition that needs to be addressed but should work most of the time.
// TODO We want to actually figure out a way to load the templates on-demand and not do it like this.
App.loadTemplates(['projects', 'wallposts', 'reactions', 'orders', 'vouchers']);

// The Ember Data Adapter and Store configuration.

App.Adapter = DS.DRF2Adapter.extend({
    namespace: "i18n/api",

    plurals: {
        "projects/wallposts/media": "projects/wallposts/media",
        "projects/wallposts/text": "projects/wallposts/text",
        "fund/paymentinfo": "fund/paymentinfo",
        "fund/paymentmethodinfo": "fund/paymentmethodinfo"
    }

});

// Embedded Model Mapping
//
// http://stackoverflow.com/questions/14320925/how-to-make-embedded-hasmany-relationships-work-with-ember-data/14324532#14324532
// The two possible values of embedded are:
//   load: The child records are embedded when loading, but should be saved as standalone records. In order
//         for this to work, the child records must have an ID.
//   always: The child records are embedded when loading, and are saved embedded in the same record. This,
//           of course, affects the dirtiness of the records (if the child record changes, the adapter will
//           mark the parent record as dirty).
App.Adapter.map('App.Project', {
    owner: {embedded: 'load'},
    country: {embedded: 'load'}
});
App.Adapter.map('App.ProjectWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.ProjectTextWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.ProjectMediaWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.WallPostReaction', {
    author: {embedded: 'load'}
});
App.Adapter.map('App.Order', {
    donations: {embedded: 'load'},
    vouchers: {embedded: 'load'}
});
App.Adapter.map('App.CurrentOrder', {
    donations: {embedded: 'load'},
    vouchers: {embedded: 'load'}
});

App.Store = DS.Store.extend({
    revision: 11,
    adapter: 'App.Adapter'
});

/* User / Member authentication. */

// TODO The models are not worked properly yet.
App.Member = DS.Model.extend({
    url: 'members/users',

    username: DS.attr('string'),
    first_name: DS.attr('string'),
    last_name: DS.attr('string'),
    picture: DS.attr('string'),

    full_name: function() {
        return this.get('first_name') + ' ' + this.get('last_name');
    }.property('first_name', 'last_name')
});

App.User = App.Member.extend({
    url: 'members',
    email: DS.attr('string')
});

// Inspiration from:
// http://stackoverflow.com/questions/14388249/accessing-controllers-from-other-controllers
App.CurrentUserController = Em.ObjectController.extend({
    init: function() {
        this._super();
        this.set("model", App.User.find('current'));
    },

    isAuthenticated: function(){
        return (this.get('username')) ? true : false;
    }.property('username')
});

App.ApplicationController = Ember.Controller.extend({
    needs: ['currentUser']
});

/* Routing */

App.SlugRouter = Em.Mixin.create({
    serialize: function(model, params) {
        if (params.length !== 1) { return {}; }

        var name = params[0], object = {};
        object[name] = get(model, 'slug');

        return object;
    }
});

App.Router.map(function() {
    this.route("home", { path: "/" });

    this.resource('projectList', {path: '/projects'}, function() {
        this.route('new');
        this.route('search');
    });

    this.resource('project', {path: '/projects/:project_id'}, function() {
        this.route('edit');
        this.resource('projectWallPost', {path: '/wallposts/:projectwallpost_id'});
    });

    this.resource('currentOrder', {path: '/support'}, function() {
        this.route('donationList', {path: '/donations'});
        this.route('addDonation', {path: '/donations/add/:project_id'});
        this.route('voucherList', {path: '/vouchers'});
        this.route('paymentProfile', {path: '/details'});
        // TODO: Read the manual to see if this is the best way to do it.
        this.resource('currentOrderPayment', {path: '/payment'}, function(){
            this.resource('currentPaymentMethodInfo', {path: 'info'});
        });
    });

    this.resource('finalOrderItemList', {path: '/support/thanks'});

    this.resource('voucherStart', {path: '/vouchers'});
    this.resource('customVoucherRequest', {path: '/vouchers/custom'});
    this.resource('voucherRedeemDone', {path: '/vouchers/redeem/done'});

    this.resource('voucherRedeem', {path: '/vouchers/redeem'}, function() {
        this.route('add', {path: '/add/:slug'});
        this.route('code', {path: '/:code'});
    });
});

App.ProjectListRoute = Ember.Route.extend({
    model: function() {
        return App.Project.find({phase: 'fund'});
    }
});

/**
 * Project Routes
 */

App.ProjectRoute = Ember.Route.extend({
    setupController: function(controller, project) {
        this._super(controller, project);

        // Look if we've got an active voucher
        var voucher = this.controllerFor('voucherRedeem').get('voucher');
        controller.set('currentVoucher', voucher);

        // The RecordArray returned by findQuery can't be manipulated directly so we're temporarily setting it the
        // wallposts property. The controller will convert it to an Ember Array.
        var wallPostListController = this.controllerFor('projectWallPostList');
        wallPostListController.set('wallposts', App.ProjectWallPost.find({project: project.get('id')}));

        // Set the current project on the WallPost new controller.
        this.controllerFor('projectWallPostNew').set('currentProject', project);
    }
});

App.ProjectWallPostRoute = Ember.Route.extend({
    model: function(params) {
        return App.ProjectWallPost.find(params.projectwallpost_id);
    },

    setupController: function(controller, wallpost) {
        controller.set('content', wallpost);
    }
});

/**
 * Current Order Routes
 */

App.CurrentOrderRoute = Ember.Route.extend({
    model: function(params) {
        return App.CurrentOrder.find('current');
    },

    setupController: function(controller, order) {
        this._super(controller, order);
    }
});

// Redirect to the donations list if somebody tries load '/support'.
App.CurrentOrderIndexRoute = Ember.Route.extend({
    redirect: function() {
        this.transitionTo('currentOrder.donationList');
    }
});

App.CurrentOrderDonationListRoute = Ember.Route.extend({
    model: function(params) {
        var order = this.modelFor('currentOrder');
        return order.get('donations');
    },

    setupController: function(controller, donations) {
        this._super(controller, donations);
        this.controllerFor('currentOrder').set('isVoucherOrder', false);
    }
});

// This route is not really an application state we want but it doesn't seem possible to send a parameter (e.g. the
// project) to a route without having a parameter in the URL. This could be a missing feature in Ember or we could be
// missing something. More investigation is needed if we want to get rid of this route.
// Note: This route allows us to publish urls like; '/support/donations/add/<project slug>'
// which will add a donation the project in the current user's cart.
App.CurrentOrderAddDonationRoute = Ember.Route.extend({
    setupController: function (controller, project) {
        var order = this.modelFor('currentOrder');
        if (!order.get('isLoaded')) {
            var route = this;
            order.on("didLoad", function () {
                route.send('addDonation', order, project);
            });
        } else {
            this.send('addDonation', order, project);
        }
    },

    events: {
        addDonation: function (order, project) {
            if (!Em.isNone(project)) {
                var transaction = this.get('store').transaction();
                var donation = transaction.createRecord(App.CurrentOrderDonation);
                transaction.add(donation);
                donation.set('project', project);
                donation.set('order', order);
                transaction.commit();
            }

            // We're transitioning to the donation list route directly after adding the donation so that the url
            // doesn't show '/support/donations/add/slug' after the donation has been added.
            this.transitionTo('currentOrder.donationList');
        }
    }
});

App.CurrentOrderVoucherListRoute = Ember.Route.extend({
    model: function(params) {
        return App.CurrentOrder.find('current').get('vouchers');
    },

    setupController: function(controller, vouchers) {
        this._super(controller, vouchers);
        this.controllerFor('currentOrder').set('isVoucherOrder', true);
    }
});

App.PaymentProfileRoute = Ember.Route.extend({
    model: function(params) {
        return App.PaymentOrderProfile.find('current');
    },

    setupController: function(controller, orderprofile) {
        controller.set('content', orderprofile);
    }
});

/**
 * Vouchers Redeem Routes
 */

App.CustomVoucherRequestRoute = Ember.Route.extend({
    setupController: function(controller, context) {
        // TODO: Find out why init() doesn't run automatically.
        controller.init();
    }
});

App.VoucherRedeemCodeRoute = Ember.Route.extend({
    model: function(params) {
        var voucher = App.Voucher.find(params['code']);
        // We don't get the code from the server, but we want it to return it to the user here.
        voucher.set('code', params['code']);
        return voucher;
    },

    setupController: function(controller, voucher) {
        this.controllerFor('voucherRedeem').set('voucher', voucher);
    }
});

App.VoucherRedeemAddRoute = Ember.Route.extend({
    setupController: function(controller, project) {
        var voucher = this.controllerFor('voucherRedeem').get('voucher');
        if (project !== undefined) {
            var transaction = this.get('store').transaction();
            // TODO: Generify and move to DRF2 adapter.
            App.VoucherDonation.reopen({
                url: 'fund/vouchers/' + voucher.get('code') + '/donations'
            });
            var donation = transaction.createRecord(App.VoucherDonation);
            donation.set('project', project);
            // Ember object embedded isn't updated by server response. Manual update for embedded donation here.
            donation.on('didCreate', function(record){
                voucher.get('donations').clear();
                voucher.get('donations').pushObject(record);
            });
            transaction.commit();
        }
    }
});

App.CurrentPaymentMethodInfoRoute = Ember.Route.extend({
    model: function(params) {
        return App.PaymentMethodInfo.find('current');
    },

    setupController: function(controller, paymentmethodinfo) {
        controller.set('content', paymentmethodinfo);
    }
});

App.FinalOrderItemListRoute = Ember.Route.extend({
    model: function(params) {
        return App.LatestDonation.find();
    },

    setupController: function(controller, orderitems) {
        controller.set('content', orderitems);
    }
});

<commit_msg>Put section marker comment is the correct place.<commit_after>function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrf_token = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function sameOrigin(url) {
    // If url starts with / it's relative and same origin
    if (url.substr(0, 1) == '/') {
        return true;
    }
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/');
    // or any other URL that isn't scheme relative or absolute i.e relative. !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrf_token);
        }
    }
});

Em.TextField.reopen({
    // Add 'step' to attributeBinding
    attributeBindings: ['type', 'value', 'size', 'step']
});

// TODO Rename App to BlueBottle, BB or BBApp.
App = Em.Application.create({
    VERSION: '1.0.0',

    ready: function() {
        //..init code goes here...
    },

    _getTemplate: function(template, callback) {
        var hash = {};
        hash.url = '/en/templates/' + template + '.hbs';
        hash.type = 'GET';
        hash.contentType = 'application/json';
        hash.success = callback;
        hash.error = function(jqXHR, textStatus, errorThrown) {
            throw errorThrown + ' ' + hash.url;
        };
        jQuery.ajax(hash);
    },

    loadTemplate: function(templateFilename) {
        this._getTemplate(templateFilename, function(data) {
            // Iterate through handlebar tags
            $(data).filter('script[type="text/x-handlebars"]').each(function() {
                var templateName = $(this).attr('data-template-name');
                var raw = $(this).html();
                Em.TEMPLATES[templateName] = Em.Handlebars.compile(raw);
            });
        });
    },

    loadTemplates: function(templateFilenames) {
        var app = this;
        templateFilenames.forEach(function (templateFilename) {
            app.loadTemplate(templateFilename);
        });
    }
});

// Load the Handlebar templates.
// TODO: This is race condition that needs to be addressed but should work most of the time.
// TODO We want to actually figure out a way to load the templates on-demand and not do it like this.
App.loadTemplates(['projects', 'wallposts', 'reactions', 'orders', 'vouchers']);

// The Ember Data Adapter and Store configuration.

App.Adapter = DS.DRF2Adapter.extend({
    namespace: "i18n/api",

    plurals: {
        "projects/wallposts/media": "projects/wallposts/media",
        "projects/wallposts/text": "projects/wallposts/text",
        "fund/paymentinfo": "fund/paymentinfo",
        "fund/paymentmethodinfo": "fund/paymentmethodinfo"
    }

});

// Embedded Model Mapping
//
// http://stackoverflow.com/questions/14320925/how-to-make-embedded-hasmany-relationships-work-with-ember-data/14324532#14324532
// The two possible values of embedded are:
//   load: The child records are embedded when loading, but should be saved as standalone records. In order
//         for this to work, the child records must have an ID.
//   always: The child records are embedded when loading, and are saved embedded in the same record. This,
//           of course, affects the dirtiness of the records (if the child record changes, the adapter will
//           mark the parent record as dirty).
App.Adapter.map('App.Project', {
    owner: {embedded: 'load'},
    country: {embedded: 'load'}
});
App.Adapter.map('App.ProjectWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.ProjectTextWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.ProjectMediaWallPost', {
    author: {embedded: 'load'},
    photos: {embedded: 'load'},
    reactions: {embedded: 'load'}
});
App.Adapter.map('App.WallPostReaction', {
    author: {embedded: 'load'}
});
App.Adapter.map('App.Order', {
    donations: {embedded: 'load'},
    vouchers: {embedded: 'load'}
});
App.Adapter.map('App.CurrentOrder', {
    donations: {embedded: 'load'},
    vouchers: {embedded: 'load'}
});

App.Store = DS.Store.extend({
    revision: 11,
    adapter: 'App.Adapter'
});

/* User / Member authentication. */

// TODO The models are not worked properly yet.
App.Member = DS.Model.extend({
    url: 'members/users',

    username: DS.attr('string'),
    first_name: DS.attr('string'),
    last_name: DS.attr('string'),
    picture: DS.attr('string'),

    full_name: function() {
        return this.get('first_name') + ' ' + this.get('last_name');
    }.property('first_name', 'last_name')
});

App.User = App.Member.extend({
    url: 'members',
    email: DS.attr('string')
});

// Inspiration from:
// http://stackoverflow.com/questions/14388249/accessing-controllers-from-other-controllers
App.CurrentUserController = Em.ObjectController.extend({
    init: function() {
        this._super();
        this.set("model", App.User.find('current'));
    },

    isAuthenticated: function(){
        return (this.get('username')) ? true : false;
    }.property('username')
});

App.ApplicationController = Ember.Controller.extend({
    needs: ['currentUser']
});

/* Routing */

App.SlugRouter = Em.Mixin.create({
    serialize: function(model, params) {
        if (params.length !== 1) { return {}; }

        var name = params[0], object = {};
        object[name] = get(model, 'slug');

        return object;
    }
});

App.Router.map(function() {
    this.route("home", { path: "/" });

    this.resource('projectList', {path: '/projects'}, function() {
        this.route('new');
        this.route('search');
    });

    this.resource('project', {path: '/projects/:project_id'}, function() {
        this.route('edit');
        this.resource('projectWallPost', {path: '/wallposts/:projectwallpost_id'});
    });

    this.resource('currentOrder', {path: '/support'}, function() {
        this.route('donationList', {path: '/donations'});
        this.route('addDonation', {path: '/donations/add/:project_id'});
        this.route('voucherList', {path: '/vouchers'});
        this.route('paymentProfile', {path: '/details'});
        // TODO: Read the manual to see if this is the best way to do it.
        this.resource('currentOrderPayment', {path: '/payment'}, function(){
            this.resource('currentPaymentMethodInfo', {path: 'info'});
        });
    });

    this.resource('finalOrderItemList', {path: '/support/thanks'});

    this.resource('voucherStart', {path: '/vouchers'});
    this.resource('customVoucherRequest', {path: '/vouchers/custom'});
    this.resource('voucherRedeemDone', {path: '/vouchers/redeem/done'});

    this.resource('voucherRedeem', {path: '/vouchers/redeem'}, function() {
        this.route('add', {path: '/add/:slug'});
        this.route('code', {path: '/:code'});
    });
});

/**
 * Project Routes
 */

App.ProjectListRoute = Ember.Route.extend({
    model: function() {
        return App.Project.find({phase: 'fund'});
    }
});

App.ProjectRoute = Ember.Route.extend({
    setupController: function(controller, project) {
        this._super(controller, project);

        // Look if we've got an active voucher
        var voucher = this.controllerFor('voucherRedeem').get('voucher');
        controller.set('currentVoucher', voucher);

        // The RecordArray returned by findQuery can't be manipulated directly so we're temporarily setting it the
        // wallposts property. The controller will convert it to an Ember Array.
        var wallPostListController = this.controllerFor('projectWallPostList');
        wallPostListController.set('wallposts', App.ProjectWallPost.find({project: project.get('id')}));

        // Set the current project on the WallPost new controller.
        this.controllerFor('projectWallPostNew').set('currentProject', project);
    }
});

App.ProjectWallPostRoute = Ember.Route.extend({
    model: function(params) {
        return App.ProjectWallPost.find(params.projectwallpost_id);
    },

    setupController: function(controller, wallpost) {
        controller.set('content', wallpost);
    }
});

/**
 * Current Order Routes
 */

App.CurrentOrderRoute = Ember.Route.extend({
    model: function(params) {
        return App.CurrentOrder.find('current');
    },

    setupController: function(controller, order) {
        this._super(controller, order);
    }
});

// Redirect to the donations list if somebody tries load '/support'.
App.CurrentOrderIndexRoute = Ember.Route.extend({
    redirect: function() {
        this.transitionTo('currentOrder.donationList');
    }
});

App.CurrentOrderDonationListRoute = Ember.Route.extend({
    model: function(params) {
        var order = this.modelFor('currentOrder');
        return order.get('donations');
    },

    setupController: function(controller, donations) {
        this._super(controller, donations);
        this.controllerFor('currentOrder').set('isVoucherOrder', false);
    }
});

// This route is not really an application state we want but it doesn't seem possible to send a parameter (e.g. the
// project) to a route without having a parameter in the URL. This could be a missing feature in Ember or we could be
// missing something. More investigation is needed if we want to get rid of this route.
// Note: This route allows us to publish urls like; '/support/donations/add/<project slug>'
// which will add a donation the project in the current user's cart.
App.CurrentOrderAddDonationRoute = Ember.Route.extend({
    setupController: function (controller, project) {
        var order = this.modelFor('currentOrder');
        if (!order.get('isLoaded')) {
            var route = this;
            order.on("didLoad", function () {
                route.send('addDonation', order, project);
            });
        } else {
            this.send('addDonation', order, project);
        }
    },

    events: {
        addDonation: function (order, project) {
            if (!Em.isNone(project)) {
                var transaction = this.get('store').transaction();
                var donation = transaction.createRecord(App.CurrentOrderDonation);
                transaction.add(donation);
                donation.set('project', project);
                donation.set('order', order);
                transaction.commit();
            }

            // We're transitioning to the donation list route directly after adding the donation so that the url
            // doesn't show '/support/donations/add/slug' after the donation has been added.
            this.transitionTo('currentOrder.donationList');
        }
    }
});

App.CurrentOrderVoucherListRoute = Ember.Route.extend({
    model: function(params) {
        return App.CurrentOrder.find('current').get('vouchers');
    },

    setupController: function(controller, vouchers) {
        this._super(controller, vouchers);
        this.controllerFor('currentOrder').set('isVoucherOrder', true);
    }
});

App.PaymentProfileRoute = Ember.Route.extend({
    model: function(params) {
        return App.PaymentOrderProfile.find('current');
    },

    setupController: function(controller, orderprofile) {
        controller.set('content', orderprofile);
    }
});

/**
 * Vouchers Redeem Routes
 */

App.CustomVoucherRequestRoute = Ember.Route.extend({
    setupController: function(controller, context) {
        // TODO: Find out why init() doesn't run automatically.
        controller.init();
    }
});

App.VoucherRedeemCodeRoute = Ember.Route.extend({
    model: function(params) {
        var voucher = App.Voucher.find(params['code']);
        // We don't get the code from the server, but we want it to return it to the user here.
        voucher.set('code', params['code']);
        return voucher;
    },

    setupController: function(controller, voucher) {
        this.controllerFor('voucherRedeem').set('voucher', voucher);
    }
});

App.VoucherRedeemAddRoute = Ember.Route.extend({
    setupController: function(controller, project) {
        var voucher = this.controllerFor('voucherRedeem').get('voucher');
        if (project !== undefined) {
            var transaction = this.get('store').transaction();
            // TODO: Generify and move to DRF2 adapter.
            App.VoucherDonation.reopen({
                url: 'fund/vouchers/' + voucher.get('code') + '/donations'
            });
            var donation = transaction.createRecord(App.VoucherDonation);
            donation.set('project', project);
            // Ember object embedded isn't updated by server response. Manual update for embedded donation here.
            donation.on('didCreate', function(record){
                voucher.get('donations').clear();
                voucher.get('donations').pushObject(record);
            });
            transaction.commit();
        }
    }
});

App.CurrentPaymentMethodInfoRoute = Ember.Route.extend({
    model: function(params) {
        return App.PaymentMethodInfo.find('current');
    },

    setupController: function(controller, paymentmethodinfo) {
        controller.set('content', paymentmethodinfo);
    }
});

App.FinalOrderItemListRoute = Ember.Route.extend({
    model: function(params) {
        return App.LatestDonation.find();
    },

    setupController: function(controller, orderitems) {
        controller.set('content', orderitems);
    }
});

<|endoftext|>


<commit_before>var kanso = require('../lib/kanso'),
    dust = require('../deps/dustjs/lib/dust'),
    settings = require('../lib/settings'),
    fs = require('fs');

// integration test
exports['load'] = function (test) {
    var pdir = __dirname + '/fixtures/testproject';
    settings.load(pdir, function (err, settings) {
        test.ifError(err);
        kanso.load(pdir, settings, function (err, doc) {
            if (err) {
                throw err;
            }
            test.same(doc.settings, {
                'name': 'testproject',
                'load': 'lib/app',
                'modules': ['deps','lib'],
                'templates': 'templates',
                'attachments': 'static'
            });
            test.equals(doc.testproperty, 'test property');
            test.same(Object.keys(doc.lib).sort(), ['module1','app'].sort());
            test.equals(
                doc.lib.module1,
                fs.readFileSync(pdir + '/lib/module1.js').toString()
            );
            test.equals(
                doc.lib.app,
                fs.readFileSync(pdir + '/lib/app.js').toString()
            );
            test.same(doc.shows, {
                'testshow': 'function(){' +
                    'return require("lib/app")["shows"]["testshow"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.lists, {
                'testlist': 'function(){' +
                    'return require("lib/app")["lists"]["testlist"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.updates, {
                'testupdate': 'function(){' +
                    'return require("lib/app")["updates"]["testupdate"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.filters, {
                'testfilter': 'function(){' +
                    'return require("lib/app")["filters"]["testfilter"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.equals(
                doc.validate_doc_update,
                'function(){' +
                    'return require("lib/app")["validate_doc_update"]' +
                        '.apply(this, arguments);' +
                '}'
            );
            test.same(doc.deps, {
                'module2': 'exports.name = "module two";\n'
            });
            test.equal(
                doc.no_proxy_function,
                'function (){return "test";}'
            );
            test.same(doc.no_proxy_obj, {
                fn1: 'function (){return "one";}',
                fn2: 'function (){return "two";}'
            });
            test.same(doc._attachments['static/test.txt'], {
                    'content_type': 'text/plain',
                    'data': 'dGVzdCBkYXRhCg=='
            });
            // TODO: test value of _attachments['kanso.js']
            test.same(doc.rewrites, [
                {from: '/kanso.js', to: 'kanso.js'},
                {from: '/_db/*', to: '../../*'},
                {from: '/', to: '_show/testshow'}
            ]);
            test.done();
        });
    });
};
<commit_msg>update unit tests for latest default rewrite rules<commit_after>var kanso = require('../lib/kanso'),
    dust = require('../deps/dustjs/lib/dust'),
    settings = require('../lib/settings'),
    fs = require('fs');

// integration test
exports['load'] = function (test) {
    var pdir = __dirname + '/fixtures/testproject';
    settings.load(pdir, function (err, settings) {
        test.ifError(err);
        kanso.load(pdir, settings, function (err, doc) {
            if (err) {
                throw err;
            }
            test.same(doc.settings, {
                'name': 'testproject',
                'load': 'lib/app',
                'modules': ['deps','lib'],
                'templates': 'templates',
                'attachments': 'static'
            });
            test.equals(doc.testproperty, 'test property');
            test.same(Object.keys(doc.lib).sort(), ['module1','app'].sort());
            test.equals(
                doc.lib.module1,
                fs.readFileSync(pdir + '/lib/module1.js').toString()
            );
            test.equals(
                doc.lib.app,
                fs.readFileSync(pdir + '/lib/app.js').toString()
            );
            test.same(doc.shows, {
                'testshow': 'function(){' +
                    'return require("lib/app")["shows"]["testshow"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.lists, {
                'testlist': 'function(){' +
                    'return require("lib/app")["lists"]["testlist"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.updates, {
                'testupdate': 'function(){' +
                    'return require("lib/app")["updates"]["testupdate"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.same(doc.filters, {
                'testfilter': 'function(){' +
                    'return require("lib/app")["filters"]["testfilter"]' +
                        '.apply(this, arguments);' +
                '}'
            });
            test.equals(
                doc.validate_doc_update,
                'function(){' +
                    'return require("lib/app")["validate_doc_update"]' +
                        '.apply(this, arguments);' +
                '}'
            );
            test.same(doc.deps, {
                'module2': 'exports.name = "module two";\n'
            });
            test.equal(
                doc.no_proxy_function,
                'function (){return "test";}'
            );
            test.same(doc.no_proxy_obj, {
                fn1: 'function (){return "one";}',
                fn2: 'function (){return "two";}'
            });
            test.same(doc._attachments['static/test.txt'], {
                    'content_type': 'text/plain',
                    'data': 'dGVzdCBkYXRhCg=='
            });
            // TODO: test value of _attachments['kanso.js']
            test.same(doc.rewrites, [
                {from: '/kanso.js', to: 'kanso.js'},
                {from: '/_db/*', to: '../../*'},
                {from: '/_db', to: '../..'},
                {from: '/', to: '_show/testshow'}
            ]);
            test.done();
        });
    });
};
<|endoftext|>


<commit_before>
Paloquiz.states.Highscores = function(game) {
    this.currentPage = 0;
    this.friendsScores = [];
    this.userPos = undefined;
    this.profileImagesData = [];
    this.numFriendsInPage = 0;
    this.maxPage = 0;

    this.scores = [];
    this.nameTextStyle;
    this.scoreTextStyle;
    this.nameUserTextStyle;
    this.scoreUserTextStyle;
    this.arrowRight;
    this.arrowLeft;
    this.backButton;
};

Paloquiz.states.Highscores.prototype = {

    PAGE_SIZE: 5,
    HIGHSCORE_REGION: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
    },
    HIGHSCORE_ENTRY_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_NAME_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_IMAGE_SIZE: 0,

    init: function() {
        this.HIGHSCORE_REGION.x = this.game.width * .1;
        this.HIGHSCORE_REGION.y = this.game.height * .1;
        this.HIGHSCORE_REGION.width = this.game.width * .8;
        this.HIGHSCORE_REGION.height = this.game.height * .8;

        this.HIGHSCORE_ENTRY_SIZE.width = this.HIGHSCORE_REGION.width;
        this.HIGHSCORE_ENTRY_SIZE.height =
            this.HIGHSCORE_REGION.height / this.PAGE_SIZE;

        this.HIGHSCORE_NAME_SIZE.width = this.HIGHSCORE_REGION.width * .5;
        this.HIGHSCORE_NAME_SIZE.height = this.HIGHSCORE_ENTRY_SIZE.height;

        this.HIGHSCORE_IMAGE_SIZE = .7 * Math.min(
            this.HIGHSCORE_ENTRY_SIZE.width * .25,
            this.HIGHSCORE_ENTRY_SIZE.height);

        // Font styles
        this.createFontStyles();
    },

    preload: function() {
        // Enable cross-origin image loading
        this.load.crossOrigin = 'anonymous';

        this.load.image('noface', 'assets/noface.png');
        this.load.image('arrow', 'assets/arrow.png');
        this.load.image('back', 'assets/back.png');
    },

    create: function() {
        // Set up background
        var background = this.add.image(this.game.world.centerX, this.game.world.centerY, 'background');
        background.anchor.setTo(0.5, 0.5);
        background.width = this.game.width;
        background.height = this.game.height;

        // Create game objects
        this.createHighscoreEntries();
        this.createUI();

        // Handler for loaded profile pictures
        this.load.onLoadComplete.add(this.loadComplete, this);

        // Exit if not logged in
        if (!fbIsLoggedIn()) {
            this.state.start('Main');
            return;
        }
        fbGetFriendsScores(function(friendsScores, userPos) {
            this.friendsScores = friendsScores;
            this.userPos = userPos;
            this.maxPage = Math.floor(this.friendsScores.length / this.PAGE_SIZE);
            this.loadScoresPage();
        }, this);
    },

    shutdown: function() {
        // Remove handlers
        this.load.onLoadComplete.remove(this.loadComplete, this);
    },

    loadComplete: function() {
        for (var i = 0; i < this.numFriendsInPage; i++) {
            if (!this.profileImagesData[i].isSilhouette) {
                this.scores[i].img.loadTexture(i);
            }
            this.scores[i].img.width = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.height = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.anchor.setTo(.5, .5);
            this.scores[i].img.visible = true;
        }
    },

    loadScoresPage: function() {
        // Hide all entries
        this.scores.forEach(function(s) {
            s.img.loadTexture('noface');
            s.img.visible = false;
            s.name.setStyle(this.nameTextStyle);
            s.name.visible = false;
            s.score.setStyle(this.scoreTextStyle);
            s.score.visible = false;
        }, this);

        // Make sure page is in range
        if (this.currentPage < 0) {
            this.currentPage = 0;
        }
        if (this.currentPage > this.maxPage) {
            this.currentPage = this.maxPage;
        }

        // Hide arrows as needed
        this.arrowRight.visible = this.currentPage < this.maxPage;
        this.arrowLeft.visible = this.currentPage > 0;

        // Count friends in page
        var iFriendStart = this.PAGE_SIZE * this.currentPage;
        var iFriendEnd = Math.min(
            this.PAGE_SIZE * (this.currentPage + 1),
            this.friendsScores.length);
        this.numFriendsInPage = iFriendEnd - iFriendStart;
        this.profileImagesData = new Array(this.numFriendsInPage);

        // Function that starts the loader after every friend information is read
        var readFriends = 0;
        var friendInformationRead = function() {
            readFriends++;
            if (readFriends >= this.numFriendsInPage) {
                this.profileImagesData.forEach(function(img, i) {
                    this.load.image(i, img.url);
                }, this);
                this.load.start();
            }
        }
        friendInformationRead = friendInformationRead.bind(this);

        // Iterate through friends
        for (var iFriend = iFriendStart, iScore = 0; iFriend < iFriendEnd; iFriend++, iScore++) {
            // Copy index to use in closure
            var iFriendCpy = iFriend;
            var iScoreCpy = iScore;
            fbGetProfileDetails(this.friendsScores[iFriend].user.id, function(friend) {
                this.scores[iScoreCpy].name.setText(friend.first_name || friend.name);
                this.scores[iScoreCpy].score.setText(this.friendsScores[iFriendCpy].score);
                // Use special style for the user
                if (iFriendCpy == this.userPos) {
                    this.scores[iScoreCpy].name.setStyle(this.nameUserTextStyle);
                    this.scores[iScoreCpy].score.setStyle(this.scoreUserTextStyle);
                }
                this.scores[iScoreCpy].name.visible = true;
                this.scores[iScoreCpy].score.visible = true;
                this.profileImagesData[iScoreCpy] = friend.image;
                friendInformationRead();
            }, this);
        }
    },

    createFontStyles: function () {
        var textSize = Math.floor(this.HIGHSCORE_ENTRY_SIZE.height / 4) + 'px';
        var nameTextSize = textSize;
        var scoreTextSize = textSize;
        this.nameTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'white',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'white',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
        this.nameUserTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'red',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreUserTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'red',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
    },

    createHighscoreEntries: function () {
        for (var i = 0; i < this.PAGE_SIZE; i++) {
            // Y baseline
            var y = this.HIGHSCORE_REGION.y + i * this.HIGHSCORE_ENTRY_SIZE.height;

            // Image occupies the first 25% in X dimension
            var img = this.add.image(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .125,
                y + this.HIGHSCORE_ENTRY_SIZE.height / 2,
                'noface');
            img.visible = false;

            // Name goes after image
            var name = this.add.text(0, 0, '', this.nameTextStyle);
            // Set bounds
            name.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .25,
                y,
                this.HIGHSCORE_REGION.width * .5,
                this.HIGHSCORE_ENTRY_SIZE.height);
            name.visible = false;

            // Score goes at the end
            var score = this.add.text(0, 0, '', this.scoreTextStyle);
            score.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .75,
                y,
                this.HIGHSCORE_REGION.width * .25,
                this.HIGHSCORE_ENTRY_SIZE.height);
            score.visible = false;
            this.scores.push({
                img: img,
                name: name,
                score: score
            });
        }
    },

    createUI: function () {
        // Back button to go back to main state
        var backButton = this.add.button(0, 0, 'back',
            function () {
                this.state.start('Main');
            }, this, 0, 0, 0);
        var backButtonSize= .1 * Math.min(this.game.width, this.game.height);
        backButton.width = backButtonSize;
        backButton.height = backButtonSize;
        backButton.anchor.setTo(0, 0);
        backButton.x = backButton.width * .2;
        backButton.y = backButton.width * .2;

        // Arrows to navigate through pages
        this.arrowRight = this.add.button(
            this.game.width * .9, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage < this.maxPage) {
                    this.currentPage++;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowRight.anchor.setTo(1, 1);
        this.arrowLeft = this.add.button(
            this.game.width * .1, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage > 0) {
                    this.currentPage--;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowLeft.scale.setTo(-1, 1);
        this.arrowLeft.anchor.setTo(1, 1);
    }

};
<commit_msg>Hide arrows by default<commit_after>
Paloquiz.states.Highscores = function(game) {
    this.currentPage = 0;
    this.friendsScores = [];
    this.userPos = undefined;
    this.profileImagesData = [];
    this.numFriendsInPage = 0;
    this.maxPage = 0;

    this.scores = [];
    this.nameTextStyle;
    this.scoreTextStyle;
    this.nameUserTextStyle;
    this.scoreUserTextStyle;
    this.arrowRight;
    this.arrowLeft;
    this.backButton;
};

Paloquiz.states.Highscores.prototype = {

    PAGE_SIZE: 5,
    HIGHSCORE_REGION: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
    },
    HIGHSCORE_ENTRY_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_NAME_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_IMAGE_SIZE: 0,

    init: function() {
        this.HIGHSCORE_REGION.x = this.game.width * .1;
        this.HIGHSCORE_REGION.y = this.game.height * .1;
        this.HIGHSCORE_REGION.width = this.game.width * .8;
        this.HIGHSCORE_REGION.height = this.game.height * .8;

        this.HIGHSCORE_ENTRY_SIZE.width = this.HIGHSCORE_REGION.width;
        this.HIGHSCORE_ENTRY_SIZE.height =
            this.HIGHSCORE_REGION.height / this.PAGE_SIZE;

        this.HIGHSCORE_NAME_SIZE.width = this.HIGHSCORE_REGION.width * .5;
        this.HIGHSCORE_NAME_SIZE.height = this.HIGHSCORE_ENTRY_SIZE.height;

        this.HIGHSCORE_IMAGE_SIZE = .7 * Math.min(
            this.HIGHSCORE_ENTRY_SIZE.width * .25,
            this.HIGHSCORE_ENTRY_SIZE.height);

        // Font styles
        this.createFontStyles();
    },

    preload: function() {
        // Enable cross-origin image loading
        this.load.crossOrigin = 'anonymous';

        this.load.image('noface', 'assets/noface.png');
        this.load.image('arrow', 'assets/arrow.png');
        this.load.image('back', 'assets/back.png');
    },

    create: function() {
        // Set up background
        var background = this.add.image(this.game.world.centerX, this.game.world.centerY, 'background');
        background.anchor.setTo(0.5, 0.5);
        background.width = this.game.width;
        background.height = this.game.height;

        // Create game objects
        this.createHighscoreEntries();
        this.createUI();

        // Handler for loaded profile pictures
        this.load.onLoadComplete.add(this.loadComplete, this);

        // Exit if not logged in
        if (!fbIsLoggedIn()) {
            this.state.start('Main');
            return;
        }
        fbGetFriendsScores(function(friendsScores, userPos) {
            this.friendsScores = friendsScores;
            this.userPos = userPos;
            this.maxPage = Math.floor(this.friendsScores.length / this.PAGE_SIZE);
            this.loadScoresPage();
        }, this);
    },

    shutdown: function() {
        // Remove handlers
        this.load.onLoadComplete.remove(this.loadComplete, this);
    },

    loadComplete: function() {
        for (var i = 0; i < this.numFriendsInPage; i++) {
            if (!this.profileImagesData[i].isSilhouette) {
                this.scores[i].img.loadTexture(i);
            }
            this.scores[i].img.width = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.height = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.anchor.setTo(.5, .5);
            this.scores[i].img.visible = true;
        }
    },

    loadScoresPage: function() {
        // Hide all entries
        this.scores.forEach(function(s) {
            s.img.loadTexture('noface');
            s.img.visible = false;
            s.name.setStyle(this.nameTextStyle);
            s.name.visible = false;
            s.score.setStyle(this.scoreTextStyle);
            s.score.visible = false;
        }, this);

        // Make sure page is in range
        if (this.currentPage < 0) {
            this.currentPage = 0;
        }
        if (this.currentPage > this.maxPage) {
            this.currentPage = this.maxPage;
        }

        // Hide arrows as needed
        this.arrowRight.visible = this.currentPage < this.maxPage;
        this.arrowLeft.visible = this.currentPage > 0;

        // Count friends in page
        var iFriendStart = this.PAGE_SIZE * this.currentPage;
        var iFriendEnd = Math.min(
            this.PAGE_SIZE * (this.currentPage + 1),
            this.friendsScores.length);
        this.numFriendsInPage = iFriendEnd - iFriendStart;
        this.profileImagesData = new Array(this.numFriendsInPage);

        // Function that starts the loader after every friend information is read
        var readFriends = 0;
        var friendInformationRead = function() {
            readFriends++;
            if (readFriends >= this.numFriendsInPage) {
                this.profileImagesData.forEach(function(img, i) {
                    this.load.image(i, img.url);
                }, this);
                this.load.start();
            }
        }
        friendInformationRead = friendInformationRead.bind(this);

        // Iterate through friends
        for (var iFriend = iFriendStart, iScore = 0; iFriend < iFriendEnd; iFriend++, iScore++) {
            // Copy index to use in closure
            var iFriendCpy = iFriend;
            var iScoreCpy = iScore;
            fbGetProfileDetails(this.friendsScores[iFriend].user.id, function(friend) {
                this.scores[iScoreCpy].name.setText(friend.first_name || friend.name);
                this.scores[iScoreCpy].score.setText(this.friendsScores[iFriendCpy].score);
                // Use special style for the user
                if (iFriendCpy == this.userPos) {
                    this.scores[iScoreCpy].name.setStyle(this.nameUserTextStyle);
                    this.scores[iScoreCpy].score.setStyle(this.scoreUserTextStyle);
                }
                this.scores[iScoreCpy].name.visible = true;
                this.scores[iScoreCpy].score.visible = true;
                this.profileImagesData[iScoreCpy] = friend.image;
                friendInformationRead();
            }, this);
        }
    },

    createFontStyles: function () {
        var textSize = Math.floor(this.HIGHSCORE_ENTRY_SIZE.height / 4) + 'px';
        var nameTextSize = textSize;
        var scoreTextSize = textSize;
        this.nameTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'white',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'white',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
        this.nameUserTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'red',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreUserTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'red',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
    },

    createHighscoreEntries: function () {
        for (var i = 0; i < this.PAGE_SIZE; i++) {
            // Y baseline
            var y = this.HIGHSCORE_REGION.y + i * this.HIGHSCORE_ENTRY_SIZE.height;

            // Image occupies the first 25% in X dimension
            var img = this.add.image(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .125,
                y + this.HIGHSCORE_ENTRY_SIZE.height / 2,
                'noface');
            img.visible = false;

            // Name goes after image
            var name = this.add.text(0, 0, '', this.nameTextStyle);
            // Set bounds
            name.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .25,
                y,
                this.HIGHSCORE_REGION.width * .5,
                this.HIGHSCORE_ENTRY_SIZE.height);
            name.visible = false;

            // Score goes at the end
            var score = this.add.text(0, 0, '', this.scoreTextStyle);
            score.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .75,
                y,
                this.HIGHSCORE_REGION.width * .25,
                this.HIGHSCORE_ENTRY_SIZE.height);
            score.visible = false;
            this.scores.push({
                img: img,
                name: name,
                score: score
            });
        }
    },

    createUI: function () {
        // Back button to go back to main state
        var backButton = this.add.button(0, 0, 'back',
            function () {
                this.state.start('Main');
            }, this, 0, 0, 0);
        var backButtonSize= .1 * Math.min(this.game.width, this.game.height);
        backButton.width = backButtonSize;
        backButton.height = backButtonSize;
        backButton.anchor.setTo(0, 0);
        backButton.x = backButton.width * .2;
        backButton.y = backButton.width * .2;

        // Arrows to navigate through pages
        this.arrowRight = this.add.button(
            this.game.width * .9, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage < this.maxPage) {
                    this.currentPage++;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowRight.anchor.setTo(1, 1);
        this.arrowRight.visible = false;

        this.arrowLeft = this.add.button(
            this.game.width * .1, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage > 0) {
                    this.currentPage--;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowLeft.scale.setTo(-1, 1);
        this.arrowLeft.anchor.setTo(1, 1);
        this.arrowLeft.visible = false;
    }

};
<|endoftext|>