<commit_before>var PageController = function(page_id){
	if(localStorage.getItem(page_id)){
			localStorage.setItem(page_id, parseInt(localStorage.getItem(page_id)) + 1);
	}else{
		localStorage.setItem(page_id, 1);
	}
	// quick proof of concept until LocalStorage abstraction is implemented
	if(!localStorage.getItem(page_id + "_time_spent")){
		localStorage.setItem(page_id + "_time_spent", 0);
	}

	this.page_id = parseInt(page_id);
	this.render();
	this.visits = parseInt(localStorage.getItem(page_id));
	this.stopwatch = new StopWatch();
	this.stopwatch.start();
};

PageController.prototype.render = function() {
	var that = this;

	api.getPage(this.page_id).done(function(response){
		if($.isEmptyObject(response)){
			throw "this page doesn't exist:";
		}else{
			that.applyDirectives(response);
			that.composePage(response);
			that.checkVisits();
			that.init();
		}
	});
};

PageController.prototype.composePage = function(pageResponse) {
	var sections = pageResponse.sections,
		choices = pageResponse.choices,
	 	html = "";

	 for (var i = 0; i < sections.length; i++) {
	 	var section_html = tf.fillTemplate({"content": sections[i].content}, "page_section");

	 	if(sections[i].content.search("Mr") > 0 || sections[i].content.search("Ms") > 0){
			 var split = sections[i].content.split(" ");
			 this.patient = split.splice(split.length - 2).join(" ").replace(/(<([^>]+)>)/ig,"");
		 }

	 	if($(section_html).children().eq(0).prop("tagName") === "AUDIO"){
	 		html += "
 There's supposed to be an audio track here, but it's not currently supported. Just move along as normal...
";
	 		continue;
		}

	 	html += section_html;
	 };

	if(this.isPopup){
		smoke.alert($(html).text() + " (this was added to your lab notebook)", function(e){}, {ok: "Okay, thanks!"});
		var last_page = chain.getLastPage(),
			page_id = last_page.page_id;

		localStorage.setItem(page_id, parseInt(localStorage.getItem(page_id)) + 1);
		last_page.visits += 1;
		last_page.checkVisits();
		return;
	}

	this.hasBinary = false;

	 for (var i = 0; i < choices.length; i++) {
	 	var templateType = "";

	 	switch(choices[i].type){
	 		case "question":
	 			templateType = "question_choice";
				 this.hasTextInput = true;
	 			break;
	 		case "binary":
	 			templateType = "binary_choice";
	 			this.hasBinary = true;
	 			break;
	 		case "prompt":
	 			templateType = "prompt_choice";
				 this.hasTextInput = true;
	 			break;
	 	}

	 	//TODO: questions are special because they don't
		var n = tf.fillTemplate({"text": choices[i].text, "id":choices[i].id, "destination": choices[i].destination}, templateType);
		var choices_made = JSON.parse(localStorage.getItem("choices_made"));
		if($.inArray(choices[i].id, choices_made) > -1){
			n = n.replace("choice-binary", "choice-binary disabled");
		}

	 	html += n;
	 };

	 if(this.hasTextInput) html += tf.fillTemplate({"text": "Submit and continue", "id": "submit-btn"}, "btn");
	 if(this.goes_to_page && !this.hasTextInput && choices.length === 0) html += tf.fillTemplate({"text": "Continue", "id": "continue-btn"}, "btn");

	 ps.transitionPage(html);
	 this.$html = $(html);
	 chain.add(this);
};

PageController.prototype.applyDirectives = function(pageResponse) {
	var actions = pageResponse.page_actions,
		modifiers = pageResponse.page_modifiers;

	for (var i = 0; i < actions.length; i++) {
		switch(actions[i].name){
			case "add_to_notebook":
				labnotebook.add(actions[i].value.replace("}", ""));
				break;
			case "minimum_choices_reached":
				this.minimum_choice_page = parseInt(actions[i].value);
				break;
			case "show_all_student_content":
				labnotebook.toggle();
				break;
		}
	}

	for (var i = 0; i < modifiers.length; i++) {
		switch(modifiers[i].name){
			case "goes_to_page":
				this.goes_to_page = parseInt(modifiers[i].value);
				break;

			case "popup_window":
				this.isPopup = true;
				break;

			case "minimum_choices":
				this.minimum_choices = parseInt(modifiers[i].value.replace("}", "").replace("{", ""));
		}
	};
};

PageController.prototype.init = function() {
	$('#go-back').click(function(e){
		e.stopImmediatePropagation();
		chain.goBack();
	});

	$('#continue-btn').click(this.processContinueClick.bind(this));

	$('#submit-btn').click(this.processButtonClick.bind(this));
	$('.choice-binary .well').click(this.processBinaryClick.bind(this));
};

PageController.prototype.checkVisits = function(){
	if(this.minimum_choices === this.visits || this.minimum_choices < this.visits){
		if($('#minimum-choice-continue').length === 0){ // there's a bug that makes it appear multiple times when it shouldn't..
			var button = $(tf.fillTemplate({"id": "minimum-choice-continue", 'text': "I've collected enough data"}, "btn"));
			$('.screen').append(button);
			var that = this;
			console.log(that.minimum_choice_page);
			button.click(function(){
				that.logTimeSpentOnPage();
				publisher.publish('changePage', [PageController, that.minimum_choice_page]);
			});
		}
		return true;
	}
	return false;
};

PageController.prototype.processContinueClick = function(e){
		this.logTimeSpentOnPage();
		publisher.publish('changePage', [PageController, this.goes_to_page]);
};

PageController.prototype.processBinaryClick = function(e) {
	e.stopImmediatePropagation();
	var $elem = $(e.currentTarget);
	if($elem.parent().hasClass("disabled")) return;

	var value = $elem.text(),
		choiceId = $elem.data('choice-id'),
		destinationId = $elem.data('destination'),
		time = this.stopwatch.stop();
		action_string = getActionString(value, choiceId, this.page_id, time),
		user_ids = JSON.parse(localStorage.getItem("user_id"));

		var loggableString = "";

		if(chain.getLastPage().patient){
			loggableString += chain.getLastPage().patient+ ": ";
		}else{
			loggableString += "Question: " + $('.page-section').last().text() + " You said: ";
		}

		loggableString += value;
		labnotebook.add(loggableString.replace("}", ""));

		var choices_made = JSON.parse(localStorage.getItem("choices_made"));
		choices_made.push(choiceId);
		localStorage.setItem("choices_made", JSON.stringify(choices_made));

	if(!$.isArray(user_ids)){
		user_ids = [user_ids];
	}

	var requests = [];
	for (var i = 0; i < user_ids.length; i++) {
		requests.push([this.page_id, user_ids[i], action_string]);
	};

	if(requests.length > 1){
		this.logActions(requests, destinationId);
	}else{
		var that = this;
		api.logUserAction.apply(null, requests[0]).then(function (response) {
			  if(!response.hasOwnProperty("error")){
			  		localStorage.setItem('last_page_id', that.page_id);
			  		that.logTimeSpentOnPage();
					publisher.publish('changePage', [PageController, destinationId]);
			  }
		});
	}

	$elem.parent().addClass("disabled");
};

PageController.prototype.processButtonClick = function(e) {
	e.stopImmediatePropagation();
	var $inputs = $('input'),
		choicesMade = [],
		destination = "";

	for (var i = 0; i < $inputs.length; i++) {
		var input = $inputs.eq(i);
		var obj = {};

		if(input.val().length === 0){
			// TODO: extract this to a form helper class
			input.addClass("has-danger");
			input.attr('placeholder', "This field must be filled.");
			return;
		}

		obj['value'] = input.val();
		obj['id'] = input.data("choice-id");

		destination = this.goes_to_page;
		if(!destination) destination = input.data("destination");

		obj['action_string'] = getActionString(obj['value'], obj['id'], this.page_id, this.stopwatch.stop());

		choicesMade.push(obj);

		//	TODO: this is just a quick fix. what if the request fails?
		var loggableString = "";

		if(chain.getLastPage().patient && chain.getLastPage().patient !== "undefined"){
			loggableString += chain.getLastPage().patient+ ": ";
		}else{
			loggableString += "Question: " + input.prev().text() + "You said: ";
		}

		loggableString += input.val();
		labnotebook.add(loggableString.replace("}", ""));
	};

	// we're going to construct this so that later we can make a parser that generates insightful xls documents based on these.
	var user_ids = JSON.parse(localStorage.getItem("user_id"));

	if(!$.isArray(user_ids)){
		//this means there's just 1 user going through the simulation
		user_ids = [user_ids];
	}

	var requests = [];
	for (var i = 0; i < user_ids.length; i++) {
		for (var j = 0; j < choicesMade.length; j++) {
			requests.push([this.page_id, user_ids[i], choicesMade[j].action_string]);
		};
	};

	this.logActions(requests, destination);
};

PageController.prototype.logActions = function(data, destination) {
	var that = this;

	api.aggregateRequests(api.logUserAction, data).then(function(){

		loader.hide();
		if(destination){
			localStorage.setItem('last_page_id', that.page_id);

			that.logTimeSpentOnPage();
			publisher.publish('changePage', [PageController, destination]); // assume that that's all we need to do on the page
		}
	});
};

PageController.prototype.disableChoices = function() {
	var choices = $('.choice');
	for (var i = 0; i < choices.length; i++) {
		choices.eq(i).addClass("disabled");
	};
};

PageController.prototype.sleep = function(){
	$('.screen').fadeOut().remove();
};

PageController.prototype.revive = function(){
	this.$html.appendTo(".page-container").hide().fadeIn();
	this.init();
	this.visits += 1;
	this.checkVisits();
};

/**
 * Simple function which will log time spent on page in local storage
 */
PageController.prototype.logTimeSpentOnPage = function() {
	var timeSpentOnPage = this.stopwatch.stop();
	var timeSpentOnPageBefore = parseInt(localStorage.getItem(this.page_id + "_time_spent"));
	localStorage.setItem(this.page_id + "_time_spent", timeSpentOnPage + timeSpentOnPageBefore);
};

function getActionString(action, choice_id, page_id, time){
	return "Choice made: "+ action + " on the choice with id: " + choice_id + " on page: " + page_id + " with time: " + time;
}
<commit_msg>Modified PageController class to use the new Logging Abstractions. 	Now, logging through the api is handled through ChoiceLogManager. 	However, we make assumptions that we can use the aggregate requests api function in every situation 	Error handling may need to be added for logging (especially for api calls). 	New changePage method added which will take care of flushing logs and marking time spent on page before changing page for each button click. 	Moved logUserActions functionality to ChoiceLogManager.<commit_after>var PageController = function(page_id){
	if(localStorage.getItem(page_id)){
			localStorage.setItem(page_id, parseInt(localStorage.getItem(page_id)) + 1);
	}else{
		localStorage.setItem(page_id, 1);
	}
	// quick proof of concept until LocalStorage abstraction is implemented
	if(!localStorage.getItem(page_id + "_time_spent")){
		localStorage.setItem(page_id + "_time_spent", 0);
	}

	this.page_id = parseInt(page_id);
	this.render();
	this.visits = parseInt(localStorage.getItem(page_id));
	this.stopwatch = new StopWatch();
	this.stopwatch.start();
	this.notebookLogger = new LabNotebookLogManager();
	this.choiceLogger = new ChoiceLogManager(this.page_id);
};

PageController.prototype.render = function() {
	var that = this;

	api.getPage(this.page_id).done(function(response){
		if($.isEmptyObject(response)){
			throw "this page doesn't exist:";
		}else{
			that.applyDirectives(response);
			that.composePage(response);
			that.checkVisits();
			that.init();
		}
	});
};

PageController.prototype.composePage = function(pageResponse) {
	var sections = pageResponse.sections,
		choices = pageResponse.choices,
	 	html = "";

	 for (var i = 0; i < sections.length; i++) {
	 	var section_html = tf.fillTemplate({"content": sections[i].content}, "page_section");

	 	if(sections[i].content.search("Mr") > 0 || sections[i].content.search("Ms") > 0){
			 var split = sections[i].content.split(" ");
			 this.patient = split.splice(split.length - 2).join(" ").replace(/(<([^>]+)>)/ig,"");
		 }

	 	if($(section_html).children().eq(0).prop("tagName") === "AUDIO"){
	 		html += "
 There's supposed to be an audio track here, but it's not currently supported. Just move along as normal...
";
	 		continue;
		}

	 	html += section_html;
	 };

	if(this.isPopup){
		smoke.alert($(html).text() + " (this was added to your lab notebook)", function(e){}, {ok: "Okay, thanks!"});
		var last_page = chain.getLastPage(),
			page_id = last_page.page_id;

		localStorage.setItem(page_id, parseInt(localStorage.getItem(page_id)) + 1);
		last_page.visits += 1;
		last_page.checkVisits();
		return;
	}

	this.hasBinary = false;

	 for (var i = 0; i < choices.length; i++) {
	 	var templateType = "";

	 	switch(choices[i].type){
	 		case "question":
	 			templateType = "question_choice";
				 this.hasTextInput = true;
	 			break;
	 		case "binary":
	 			templateType = "binary_choice";
	 			this.hasBinary = true;
	 			break;
	 		case "prompt":
	 			templateType = "prompt_choice";
				 this.hasTextInput = true;
	 			break;
	 	}

	 	//TODO: questions are special because they don't
		var n = tf.fillTemplate({"text": choices[i].text, "id":choices[i].id, "destination": choices[i].destination}, templateType);
		var choices_made = JSON.parse(localStorage.getItem("choices_made"));
		if($.inArray(choices[i].id, choices_made) > -1){
			n = n.replace("choice-binary", "choice-binary disabled");
		}

	 	html += n;
	 };

	 if(this.hasTextInput) html += tf.fillTemplate({"text": "Submit and continue", "id": "submit-btn"}, "btn");
	 if(this.goes_to_page && !this.hasTextInput && choices.length === 0) html += tf.fillTemplate({"text": "Continue", "id": "continue-btn"}, "btn");

	 ps.transitionPage(html);
	 this.$html = $(html);
	 chain.add(this);
};

PageController.prototype.applyDirectives = function(pageResponse) {
	var actions = pageResponse.page_actions,
		modifiers = pageResponse.page_modifiers;

	for (var i = 0; i < actions.length; i++) {
		switch(actions[i].name){
			case "add_to_notebook":
				labnotebook.add(actions[i].value.replace("}", ""));
				break;
			case "minimum_choices_reached":
				this.minimum_choice_page = parseInt(actions[i].value);
				break;
			case "show_all_student_content":
				labnotebook.toggle();
				break;
		}
	}

	for (var i = 0; i < modifiers.length; i++) {
		switch(modifiers[i].name){
			case "goes_to_page":
				this.goes_to_page = parseInt(modifiers[i].value);
				break;

			case "popup_window":
				this.isPopup = true;
				break;

			case "minimum_choices":
				this.minimum_choices = parseInt(modifiers[i].value.replace("}", "").replace("{", ""));
		}
	};
};

PageController.prototype.init = function() {
	$('#go-back').click(function(e){
		e.stopImmediatePropagation();
		chain.goBack();
	});

	$('#continue-btn').click(this.processContinueClick.bind(this));

	$('#submit-btn').click(this.processButtonClick.bind(this));
	$('.choice-binary .well').click(this.processBinaryClick.bind(this));
};

PageController.prototype.checkVisits = function(){
	if(this.minimum_choices === this.visits || this.minimum_choices < this.visits){
		if($('#minimum-choice-continue').length === 0){ // there's a bug that makes it appear multiple times when it shouldn't..
			var button = $(tf.fillTemplate({"id": "minimum-choice-continue", 'text': "I've collected enough data"}, "btn"));
			$('.screen').append(button);
			var that = this;
			console.log(that.minimum_choice_page);
			button.click(function(){
				that.logTimeSpentOnPage();
				publisher.publish('changePage', [PageController, that.minimum_choice_page]);
			});
		}
		return true;
	}
	return false;
};

PageController.prototype.processContinueClick = function(e){
	this.changePage(this.goes_to_page);
};

PageController.prototype.processBinaryClick = function(e) {
	e.stopImmediatePropagation();
	var $elem = $(e.currentTarget);
	if($elem.parent().hasClass("disabled")) return;

	var value = $elem.text(),
		choiceId = $elem.data('choice-id'),
		destinationId = $elem.data('destination'),
		time = this.stopwatch.stop(),
		action_string = getActionString(value, choiceId, this.page_id, time);

	var loggableString = "";

	if(chain.getLastPage().patient){
		loggableString += chain.getLastPage().patient+ ": ";
	}else{
		loggableString += "Question: " + $('.page-section').last().text() + " You said: ";
	}

	loggableString += value;
	this.notebookLogger.writeToLog(loggableString);

	var choices_made = JSON.parse(localStorage.getItem("choices_made"));
	choices_made.push(choiceId);
	localStorage.setItem("choices_made", JSON.stringify(choices_made));

	this.choiceLogger.writeToLog(action_string);

	this.changePage(destinationId);

	$elem.parent().addClass("disabled");
};

PageController.prototype.processButtonClick = function(e) {
	e.stopImmediatePropagation();
	var $inputs = $('input'),
		choicesMade = [],
		destination = "";

	for (var i = 0; i < $inputs.length; i++) {
		var input = $inputs.eq(i);
		var value = input.val(),
			id = input.data("choice-id"),
			time = this.stopwatch.stop();
			actionString = getActionString(value, id, this.page_id, time);

		if(input.val().length === 0){
			// TODO: extract this to a form helper class
			input.addClass("has-danger");
			input.attr('placeholder', "This field must be filled.");
			return;
		}

		destination = this.goes_to_page;
		if(!destination) destination = input.data("destination");

		this.choiceLogger.writeToLog(actionString);

		//	TODO: this is just a quick fix. what if the request fails?
		var loggableString = "";

		if(chain.getLastPage().patient && chain.getLastPage().patient !== "undefined"){
			loggableString += chain.getLastPage().patient+ ": ";
		}else{
			loggableString += "Question: " + input.prev().text() + "You said: ";
		}

		loggableString += input.val();
		this.notebookLogger.writeToLog(loggableString);
		this.notebookLogger.flushLog();
	}

	this.changePage(destination);
};

PageController.prototype.disableChoices = function() {
	var choices = $('.choice');
	for (var i = 0; i < choices.length; i++) {
		choices.eq(i).addClass("disabled");
	};
};

PageController.prototype.sleep = function(){
	$('.screen').fadeOut().remove();
};

PageController.prototype.revive = function(){
	this.$html.appendTo(".page-container").hide().fadeIn();
	this.init();
	this.visits += 1;
	this.checkVisits();
};

/**
 * Simple function which will log time spent on page in local storage
 */
PageController.prototype.logTimeSpentOnPage = function() {
	var timeSpentOnPage = this.stopwatch.stop();
	var timeSpentOnPageBefore = parseInt(localStorage.getItem(this.page_id + "_time_spent"));
	localStorage.setItem(this.page_id + "_time_spent", timeSpentOnPage + timeSpentOnPageBefore);
};

/**
 * Helper function to change page. Assumes we will only be using PageController
 * to handle page changes. May need to be changed.
 * @param  {int} destination [destination id]
 */
PageController.prototype.changePage = function(destination) {
	// TODO: Need to implement try-catches for handling log flushing errors.
	this.choiceLogger.flushLog();
	this.notebookLogger.flushLog();
	if(destination) {
		localStorage.setItem('last_page_id', this.page_id);
		this.logTimeSpentOnPage();
		publisher.publish('changePage', [PageController, destination]);
	}
};

function getActionString(action, choice_id, page_id, time){
	return "Choice made: "+ action + " on the choice with id: " + choice_id + " on page: " + page_id + " with time: " + time;
}
<|endoftext|>


<commit_before>"use strict";

/**
 * Copyright 2017 XcooBee LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const chalk = require("chalk");

const runTest = (argv, callback) => {
    const path = require("path");
    const fs = require("fs-extra");
    const util = require("./util");

    // The default path to the parameters.json file, not necessary it exists
    let parametersFilePath = path.resolve("./parameters.json");

    // The default path for the workFiles and output directories
    let outputPath = path.resolve(".");

    // The full path to workFiles directory
    let workFilesPath = null;

    // The full path to output directory
    let outputFilesPath = null;

    // The default ttl, changes with size
    let ttl = 30000;
    // How long have we been running?
    let ellapsed = 0;

    // The amount of time reserved for clean up tasks
    const timeToCleanUp = 5000;

    // Track the number of open write streams
    const streamArray = [];

    // Flag to determine if the bee timed-out
    let callbackCalled = false;

    // mail object to be written
    const mailObject = [];

    // log object to be written
    const logObject = [];

    // processing object to be written
    const processingObject = [];

    // contains information about input files
    let filesInfo = {};

    // path to info file
    let infoFilePath = null;

    // bee system params
    let beeSystemParams = {};

    const closeStreams = () => {
        streamArray.forEach((value) => {
            value.close();
        });
    };

    let globalSequence = 1;
    const getNextId = () => {
        const current = globalSequence;
        globalSequence += 1;
        return current;
    };

    // The number of requested streams in the output folder
    let requestedStreamsInOutput = 0;

    const createServices = (inputFilePath) => {
        // TODO: Validation for fileName, what should happen if the user requests a writeStream for an
        // already created fileName
        // Create the services object
        debugger;
        const readStream = fs.createReadStream(inputFilePath);
        const services = {
            // Just log a message to the system console
            log: (message, type, replacement = {}) => {
                logObject.push({ date: Date.now(), type, message, replacement });
            },
            // email service to mock the sending of an email
            mail: (recipient, template, replacement) => {
                mailObject.push({ recipient, template, replacement });
            },
            getNextId,
            addParam: (key, value) => {
                processingObject.push({ key, value });
            },
            timeToRun: () => ttl - ellapsed,
            // The default streams for reading and writing
            readStream: () => readStream,
            // TODO: Should overwrite the files
            writeStreamManager: {
                getWriteStream: (fileName, type) => {
                    const filesBlackList = [
                        "xcoobeemail.json",
                        "xcoobeelog.json",
                        "xcoobeeparam.json",
                    ];

                    if (filesBlackList.indexOf(fileName.toLowerCase()) !== -1) {
                        return callback(Error(`Attempted to use reserved file name '${fileName}'`));
                    }

                    if (type === "output") {
                        requestedStreamsInOutput += 1;
                    }

                    const typePath = type === "wip" ? "workFiles" : "output";
                    debugger;
                    const stream = fs.createWriteStream(`${outputPath}${path.sep}${typePath}${path.sep}${fileName}`);
                    streamArray.push(stream);
                    return stream;
                },
                getReadStream: (fileName, type) => {
                    debugger;
                    const typePath = type === "wip" ? "workFiles" : "output";
                    const stream = fs.createReadStream(`${outputPath}${path.sep}${typePath}${path.sep}${fileName}`);
                    return stream;
                },
            },
            getFileType: filename => (filesInfo[filename] && filesInfo[filename].file_type) || 999,
            getFileTags: filename => (filesInfo[filename] && filesInfo[filename].file_tags) || ["one", "two", "three"],
            validationError: field => services.log(`Input value for '${field}' is not correct`, "error"),
            setBalanceLock: params => Promise.resolve(true),
            getBeeParam: param => beeSystemParams[param],
        };

        return services;
    };

    const showHelp = argv.indexOf("--help") !== -1;
    if (showHelp) {
        const marked = require("marked");
        const TerminalRenderer = require("marked-terminal");
        marked.setOptions({
            renderer: new TerminalRenderer(),
            gfm: true,
            tables: true,
            breaks: false,
            pedantic: false,
            sanitize: false,
            smartLists: true,
            smartypants: false,
        });
        const helpText = fs.readFileSync("./src/assets/help.md");
        console.log(marked(helpText.toString("utf8")));
        return callback();
    }

    const overWriteFiles = argv.indexOf("-o") !== -1;

    // Assume we are on the node project directory
    // unless otherwise specified by the --bee switch
    let beeModule = null;
    let pathToBee;
    try {
        const beeIndex = argv.indexOf("--bee");
        if (beeIndex !== -1) {
            pathToBee = path.resolve(argv[beeIndex + 1]);
            beeModule = require(pathToBee);
        } else {
            // TODO: Don't rely on cwd
            beeModule = require(process.cwd());
        }
    } catch (err) {
        callback(new Error("It was not possible to load the bee, make sure you are inside a valid node project or use the '--bee' switch"));
    }

    // Set the size instance in which this bee will be run
    // We use the size to, among other things, choose the proper ttl
    const sizeIndex = argv.indexOf("--size");
    let size = "s";
    if (sizeIndex !== -1) {
        const validSizes = ["s", "m", "l"];
        size = argv[sizeIndex + 1].toLowerCase();

        if (validSizes.indexOf(size) === -1) {
            callback(new Error(`'${size}' is not a valid size, must be one of [s, m, l]`));
        }

        if (size === "s") {
            ttl = 30000;
        } else if (size === "m") {
            ttl = 150000;
        } else {
            ttl = 300000;
        }
    }

    // Substract the timeToCleanUp from ttl to get the net time the bee got available
    ttl -= timeToCleanUp;

    // If no params file are provided we assume the bee won't require integrations
    // nor parameters, unless the '--params' flag is used in which case it is, probably,
    // an user error pointing to an unexistent file
    const paramsIndex = argv.indexOf("--params");
    if (paramsIndex !== -1) {
        parametersFilePath = path.resolve(argv[paramsIndex + 1]);

        if (!fs.existsSync(parametersFilePath)) {
            callback(Error(`${parametersFilePath} doesn't exist`));
        }
    }

    // receive additional information about files
    const infoIndex = argv.indexOf("--info");
    if (infoIndex !== -1) {
        infoFilePath = path.resolve(argv[infoIndex + 1]);

        if (!fs.existsSync(infoFilePath)) {
            callback(Error(`${infoFilePath} doesn't exist`));
        }
    }

    // If the user specifies the output as a directory, use a default file name 'output' with no extension
    // The output directory must exists, the utility WON'T create it
    const outputIndex = argv.indexOf("--out");
    if (outputIndex !== -1) {
        outputPath = path.resolve(argv[outputIndex + 1]);

        try {
            const stats = fs.lstatSync(outputPath);

            if (!stats.isDirectory()) {
                callback(new Error(`${outputPath} is not a valid directory`));
            }
        } catch (err) {
            callback(new Error(`${outputPath} is unknown`));
        }
    }

    // Be extremely careful to not delete a sensitive folder
    outputFilesPath = `${outputPath}${path.sep}output`;
    workFilesPath = `${outputPath}${path.sep}workFiles`;

    if (!fs.existsSync(outputFilesPath)) {
        fs.mkdirSync(outputFilesPath);
    } else if (overWriteFiles) {
        fs.emptyDirSync(outputFilesPath);
    }

    if (!fs.existsSync(workFilesPath)) {
        fs.mkdirSync(workFilesPath);
    } else if (overWriteFiles) {
        fs.emptyDirSync(workFilesPath);
    }

    // The input file must be the first argument
    const inputFilePath = path.resolve(argv[2]);

    if (!fs.existsSync(inputFilePath)) {
        callback(new Error(`Input file '${inputFilePath}' doesn't exist`));
    }

    const services = createServices(inputFilePath);

    const data = {};

    const defaultUserData =  {
        first_name: "John",
        last_name: "Testerson",
        xcoobee_id: "~johnt",
        locale: "en-us"
    };

    let parametersContent = null;

    if (fs.existsSync(parametersFilePath)) {
        try {
            parametersContent = JSON.parse(fs.readFileSync(parametersFilePath, "utf8"));
            data.integrations = parametersContent.integrations;
            data.parameters = parametersContent.parameters;
            data.flightprocessing = parametersContent.flightprocessing;
            data.user_data = parametersContent.user_data || defaultUserData;
            data.transaction_key = parametersContent.transaction_key;
            beeSystemParams = parametersContent.bee_system_params || {};
        } catch (err) {
            callback(new Error(`${parametersFilePath} is not a valid JSON file`));
        }
    }

    if (infoFilePath && fs.existsSync(infoFilePath)) {
        try {
            filesInfo = JSON.parse(fs.readFileSync(infoFilePath, "utf8"));
        } catch (err) {
            callback(new Error(`${infoFilePath} is not a valid JSON file`));
        }
    }

    const outputXcoobeeObjects = () => {
        if (logObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeelog.json"), JSON.stringify(logObject, null, 2));
        }

        if (mailObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeemail.json"), JSON.stringify(mailObject, null, 2));
        }

        if (processingObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeeparam.json"), JSON.stringify(processingObject, null, 2));
        }
    };

    setInterval(() => {
        ellapsed += 2;
    }, 2);

    setTimeout(() => {
        if (!callbackCalled) {
            clearInterval();
            closeStreams();
            outputXcoobeeObjects();
            util.prune(outputFilesPath);
            util.prune(workFilesPath);
            callback(new Error("Timed-out"));
        }
    }, ttl);

    const beeCallback = (err, result) => {
        callbackCalled = true;
        clearInterval();
        closeStreams();

        util.prune(outputFilesPath);
        util.prune(workFilesPath);

        outputXcoobeeObjects();

        const outputFolderSize = util.sizeOfFolder(outputFilesPath);
        const workFolderSize = util.sizeOfFolder(workFilesPath);

        const totalSize = outputFolderSize + workFolderSize;
        const issueSizeWarning = totalSize >= 536870912;
        console.log(`
==========================================
Bee test result completed
==========================================
status: Success
instance: ${size}
time: ${chalk.green(ellapsed)}ms
space: ${issueSizeWarning ? chalk.red(totalSize) : chalk.green(totalSize)} bytes ${issueSizeWarning ? "--->WARNING" : ""}
        `);

        if (requestedStreamsInOutput > 1) {
            console.log(chalk.yellow(`*More than one write stream requested in module ${pathToBee}, make sure to mark it as splitter.`));
        }

        if (err) {
            return callback(err);
        }

        return callback(null, result);
    };
    beeModule.flight(services, data, beeCallback);
};

if (require.main === module) {
    const callback = (err, result) => {
        if (err) {
            console.log(`
==========================================
Bee test result completed
==========================================
status: ${chalk.red("Failure")}
reason: ${err.message}
        `);
            return process.exit(1);
        }
        return process.exit(0);
    };
    runTest(process.argv, callback);
}

module.exports.runTest = (argv, callback) => {
    runTest(argv, callback);
};
<commit_msg>Remove debuggers from test app<commit_after>"use strict";

/**
 * Copyright 2017 XcooBee LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const chalk = require("chalk");

const runTest = (argv, callback) => {
    const path = require("path");
    const fs = require("fs-extra");
    const util = require("./util");

    // The default path to the parameters.json file, not necessary it exists
    let parametersFilePath = path.resolve("./parameters.json");

    // The default path for the workFiles and output directories
    let outputPath = path.resolve(".");

    // The full path to workFiles directory
    let workFilesPath = null;

    // The full path to output directory
    let outputFilesPath = null;

    // The default ttl, changes with size
    let ttl = 30000;
    // How long have we been running?
    let ellapsed = 0;

    // The amount of time reserved for clean up tasks
    const timeToCleanUp = 5000;

    // Track the number of open write streams
    const streamArray = [];

    // Flag to determine if the bee timed-out
    let callbackCalled = false;

    // mail object to be written
    const mailObject = [];

    // log object to be written
    const logObject = [];

    // processing object to be written
    const processingObject = [];

    // contains information about input files
    let filesInfo = {};

    // path to info file
    let infoFilePath = null;

    // bee system params
    let beeSystemParams = {};

    const closeStreams = () => {
        streamArray.forEach((value) => {
            value.close();
        });
    };

    let globalSequence = 1;
    const getNextId = () => {
        const current = globalSequence;
        globalSequence += 1;
        return current;
    };

    // The number of requested streams in the output folder
    let requestedStreamsInOutput = 0;

    const createServices = (inputFilePath) => {
        // TODO: Validation for fileName, what should happen if the user requests a writeStream for an
        // already created fileName
        // Create the services object
        const readStream = fs.createReadStream(inputFilePath);
        const services = {
            // Just log a message to the system console
            log: (message, type, replacement = {}) => {
                logObject.push({ date: Date.now(), type, message, replacement });
            },
            // email service to mock the sending of an email
            mail: (recipient, template, replacement) => {
                mailObject.push({ recipient, template, replacement });
            },
            getNextId,
            addParam: (key, value) => {
                processingObject.push({ key, value });
            },
            timeToRun: () => ttl - ellapsed,
            // The default streams for reading and writing
            readStream: () => readStream,
            // TODO: Should overwrite the files
            writeStreamManager: {
                getWriteStream: (fileName, type) => {
                    const filesBlackList = [
                        "xcoobeemail.json",
                        "xcoobeelog.json",
                        "xcoobeeparam.json",
                    ];

                    if (filesBlackList.indexOf(fileName.toLowerCase()) !== -1) {
                        return callback(Error(`Attempted to use reserved file name '${fileName}'`));
                    }

                    if (type === "output") {
                        requestedStreamsInOutput += 1;
                    }

                    const typePath = type === "wip" ? "workFiles" : "output";
                    const stream = fs.createWriteStream(`${outputPath}${path.sep}${typePath}${path.sep}${fileName}`);
                    streamArray.push(stream);
                    return stream;
                },
                getReadStream: (fileName, type) => {
                    const typePath = type === "wip" ? "workFiles" : "output";
                    const stream = fs.createReadStream(`${outputPath}${path.sep}${typePath}${path.sep}${fileName}`);
                    return stream;
                },
            },
            getFileType: filename => (filesInfo[filename] && filesInfo[filename].file_type) || 999,
            getFileTags: filename => (filesInfo[filename] && filesInfo[filename].file_tags) || ["one", "two", "three"],
            validationError: field => services.log(`Input value for '${field}' is not correct`, "error"),
            setBalanceLock: params => Promise.resolve(true),
            getBeeParam: param => beeSystemParams[param],
        };

        return services;
    };

    const showHelp = argv.indexOf("--help") !== -1;
    if (showHelp) {
        const marked = require("marked");
        const TerminalRenderer = require("marked-terminal");
        marked.setOptions({
            renderer: new TerminalRenderer(),
            gfm: true,
            tables: true,
            breaks: false,
            pedantic: false,
            sanitize: false,
            smartLists: true,
            smartypants: false,
        });
        const helpText = fs.readFileSync("./src/assets/help.md");
        console.log(marked(helpText.toString("utf8")));
        return callback();
    }

    const overWriteFiles = argv.indexOf("-o") !== -1;

    // Assume we are on the node project directory
    // unless otherwise specified by the --bee switch
    let beeModule = null;
    let pathToBee;
    try {
        const beeIndex = argv.indexOf("--bee");
        if (beeIndex !== -1) {
            pathToBee = path.resolve(argv[beeIndex + 1]);
            beeModule = require(pathToBee);
        } else {
            // TODO: Don't rely on cwd
            beeModule = require(process.cwd());
        }
    } catch (err) {
        callback(new Error("It was not possible to load the bee, make sure you are inside a valid node project or use the '--bee' switch"));
    }

    // Set the size instance in which this bee will be run
    // We use the size to, among other things, choose the proper ttl
    const sizeIndex = argv.indexOf("--size");
    let size = "s";
    if (sizeIndex !== -1) {
        const validSizes = ["s", "m", "l"];
        size = argv[sizeIndex + 1].toLowerCase();

        if (validSizes.indexOf(size) === -1) {
            callback(new Error(`'${size}' is not a valid size, must be one of [s, m, l]`));
        }

        if (size === "s") {
            ttl = 30000;
        } else if (size === "m") {
            ttl = 150000;
        } else {
            ttl = 300000;
        }
    }

    // Substract the timeToCleanUp from ttl to get the net time the bee got available
    ttl -= timeToCleanUp;

    // If no params file are provided we assume the bee won't require integrations
    // nor parameters, unless the '--params' flag is used in which case it is, probably,
    // an user error pointing to an unexistent file
    const paramsIndex = argv.indexOf("--params");
    if (paramsIndex !== -1) {
        parametersFilePath = path.resolve(argv[paramsIndex + 1]);

        if (!fs.existsSync(parametersFilePath)) {
            callback(Error(`${parametersFilePath} doesn't exist`));
        }
    }

    // receive additional information about files
    const infoIndex = argv.indexOf("--info");
    if (infoIndex !== -1) {
        infoFilePath = path.resolve(argv[infoIndex + 1]);

        if (!fs.existsSync(infoFilePath)) {
            callback(Error(`${infoFilePath} doesn't exist`));
        }
    }

    // If the user specifies the output as a directory, use a default file name 'output' with no extension
    // The output directory must exists, the utility WON'T create it
    const outputIndex = argv.indexOf("--out");
    if (outputIndex !== -1) {
        outputPath = path.resolve(argv[outputIndex + 1]);

        try {
            const stats = fs.lstatSync(outputPath);

            if (!stats.isDirectory()) {
                callback(new Error(`${outputPath} is not a valid directory`));
            }
        } catch (err) {
            callback(new Error(`${outputPath} is unknown`));
        }
    }

    // Be extremely careful to not delete a sensitive folder
    outputFilesPath = `${outputPath}${path.sep}output`;
    workFilesPath = `${outputPath}${path.sep}workFiles`;

    if (!fs.existsSync(outputFilesPath)) {
        fs.mkdirSync(outputFilesPath);
    } else if (overWriteFiles) {
        fs.emptyDirSync(outputFilesPath);
    }

    if (!fs.existsSync(workFilesPath)) {
        fs.mkdirSync(workFilesPath);
    } else if (overWriteFiles) {
        fs.emptyDirSync(workFilesPath);
    }

    // The input file must be the first argument
    const inputFilePath = path.resolve(argv[2]);

    if (!fs.existsSync(inputFilePath)) {
        callback(new Error(`Input file '${inputFilePath}' doesn't exist`));
    }

    const services = createServices(inputFilePath);

    const data = {};

    const defaultUserData =  {
        first_name: "John",
        last_name: "Testerson",
        xcoobee_id: "~johnt",
        locale: "en-us"
    };

    let parametersContent = null;

    if (fs.existsSync(parametersFilePath)) {
        try {
            parametersContent = JSON.parse(fs.readFileSync(parametersFilePath, "utf8"));
            data.integrations = parametersContent.integrations;
            data.parameters = parametersContent.parameters;
            data.flightprocessing = parametersContent.flightprocessing;
            data.user_data = parametersContent.user_data || defaultUserData;
            data.transaction_key = parametersContent.transaction_key;
            beeSystemParams = parametersContent.bee_system_params || {};
        } catch (err) {
            callback(new Error(`${parametersFilePath} is not a valid JSON file`));
        }
    }

    if (infoFilePath && fs.existsSync(infoFilePath)) {
        try {
            filesInfo = JSON.parse(fs.readFileSync(infoFilePath, "utf8"));
        } catch (err) {
            callback(new Error(`${infoFilePath} is not a valid JSON file`));
        }
    }

    const outputXcoobeeObjects = () => {
        if (logObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeelog.json"), JSON.stringify(logObject, null, 2));
        }

        if (mailObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeemail.json"), JSON.stringify(mailObject, null, 2));
        }

        if (processingObject.length > 0) {
            fs.writeFileSync(path.join(outputFilesPath, "xcoobeeparam.json"), JSON.stringify(processingObject, null, 2));
        }
    };

    setInterval(() => {
        ellapsed += 2;
    }, 2);

    setTimeout(() => {
        if (!callbackCalled) {
            clearInterval();
            closeStreams();
            outputXcoobeeObjects();
            util.prune(outputFilesPath);
            util.prune(workFilesPath);
            callback(new Error("Timed-out"));
        }
    }, ttl);

    const beeCallback = (err, result) => {
        callbackCalled = true;
        clearInterval();
        closeStreams();

        util.prune(outputFilesPath);
        util.prune(workFilesPath);

        outputXcoobeeObjects();

        const outputFolderSize = util.sizeOfFolder(outputFilesPath);
        const workFolderSize = util.sizeOfFolder(workFilesPath);

        const totalSize = outputFolderSize + workFolderSize;
        const issueSizeWarning = totalSize >= 536870912;
        console.log(`
==========================================
Bee test result completed
==========================================
status: Success
instance: ${size}
time: ${chalk.green(ellapsed)}ms
space: ${issueSizeWarning ? chalk.red(totalSize) : chalk.green(totalSize)} bytes ${issueSizeWarning ? "--->WARNING" : ""}
        `);

        if (requestedStreamsInOutput > 1) {
            console.log(chalk.yellow(`*More than one write stream requested in module ${pathToBee}, make sure to mark it as splitter.`));
        }

        if (err) {
            return callback(err);
        }

        return callback(null, result);
    };
    beeModule.flight(services, data, beeCallback);
};

if (require.main === module) {
    const callback = (err, result) => {
        if (err) {
            console.log(`
==========================================
Bee test result completed
==========================================
status: ${chalk.red("Failure")}
reason: ${err.message}
        `);
            return process.exit(1);
        }
        return process.exit(0);
    };
    runTest(process.argv, callback);
}

module.exports.runTest = (argv, callback) => {
    runTest(argv, callback);
};
<|endoftext|>


<commit_before>window.$(document).ready(function ($) {
  var $toggle = $('#nav-toggle');
  var $menu = $('#nav-menu');

  $toggle.click(function() {
    $(this).toggleClass('is-active');
    $menu.toggleClass('is-active');
    console.log()
  });

  $('.modal-button').click(function() {
    var target = $(this).data('target');
    $('html').addClass('is-clipped');
    $(target).addClass('is-active');
  });

  $('.modal-background, .modal-close').click(function() {
    $('html').removeClass('is-clipped');
    $(this).parent().removeClass('is-active');
  });

  $('.modal-card-head .delete, .modal-card-foot .button').click(function() {
    $('html').removeClass('is-clipped');
    $('#modal-ter').removeClass('is-active');
  });

  var $highlights = $('.highlight');

  $highlights.each(function() {
    var $el = $(this);
    var copy = '<button class="copy">Copy</button>';
    var expand = '<button class="expand">Expand</button>';
    $el.append(copy);

    if ($el.find('pre code').innerHeight() > 600) {
      $el.append(expand);
    }
  });

  var $highlightButtons = $('.highlight .copy, .highlight .expand');

  $highlightButtons.hover(function() {
    $(this).parent().css('box-shadow', '0 0 0 1px #ed6c63');
  }, function() {
    $(this).parent().css('box-shadow', 'none');
  });

  $('.highlight .expand').click(function() {
    $(this).parent().children('pre').css('max-height', 'none');
  });
})

($ => {
  $(function() {
    // set active radio to address bar
    $(document).on('click', '.tabs .tabs-nav label', function() {
      var hash = '#' + $(this).attr('for');
      window.history.replaceState('', '', hash)
    });
    // select active radio based on hash
    $(document.location.hash).prop('checked', true);
  })
})(window.$);
<commit_msg>Claeanup<commit_after>  window.jQuery(document).ready(function ($) {
    var $toggle = $('#nav-toggle');
    var $menu = $('#nav-menu');

    $toggle.click(function() {
      $(this).toggleClass('is-active');
      $menu.toggleClass('is-active');
      console.log()
    });

    $('.modal-button').click(function() {
      var target = $(this).data('target');
      $('html').addClass('is-clipped');
      $(target).addClass('is-active');
    });

    $('.modal-background, .modal-close').click(function() {
      $('html').removeClass('is-clipped');
      $(this).parent().removeClass('is-active');
    });

    $('.modal-card-head .delete, .modal-card-foot .button').click(function() {
      $('html').removeClass('is-clipped');
      $('#modal-ter').removeClass('is-active');
    });

    var $highlights = $('.highlight');

    $highlights.each(function() {
      var $el = $(this);
      var copy = '<button class="copy">Copy</button>';
      var expand = '<button class="expand">Expand</button>';
      $el.append(copy);

      if ($el.find('pre code').innerHeight() > 600) {
        $el.append(expand);
      }
    });

    var $highlightButtons = $('.highlight .copy, .highlight .expand');

    $highlightButtons.hover(function() {
      $(this).parent().css('box-shadow', '0 0 0 1px #ed6c63');
    }, function() {
      $(this).parent().css('box-shadow', 'none');
    });

    $('.highlight .expand').click(function() {
      $(this).parent().children('pre').css('max-height', 'none');
    });
  })

($ => {
  $(function() {
    // set active radio to address bar
    $(document).on('click', '.tabs .tabs-nav label', function() {
      var hash = '#' + $(this).attr('for');
      window.history.replaceState('', '', hash)
    });
    // select active radio based on hash
    $(document.location.hash).prop('checked', true);
  })
})(window.jQuery)
<|endoftext|>


<commit_before>function initGraphics( container_id ) {
	scene = new THREE.Scene();
	projector = new THREE.Projector();

	canvas_width = $(window).width();
	canvas_height = $('#'+ container_id).height();

	camera = new THREE.PerspectiveCamera( 40, canvas_width/canvas_height, 1, 10000 );
	camera.position.y = 500;

	renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
	renderer.setSize( canvas_width, canvas_height);

	plane = new THREE.Mesh(
		new THREE.PlaneGeometry( VOXEL_SIZE * GRID_SIZE, VOXEL_SIZE * GRID_SIZE, GRID_SIZE, GRID_SIZE ),
		new THREE.MeshBasicMaterial( { color: 0x2c364f, wireframe: true } )
	);
	plane.rotation.x = - Math.PI / 2;
	plane.matrixAutoUpdate = false;
	plane.updateMatrix();
	scene.add( plane );

	initGeometry();
	initLighting();

	//APPEND CANVAS
	container = document.getElementById( container_id );
	container.appendChild( renderer.domElement );

	//picking
	container.addEventListener( 'mousemove', onMouseMove , false );
	container.addEventListener( 'mousedown', onMouseDown, false );
	document.addEventListener( 'keydown', onKeyDown, false );
	document.addEventListener( 'keyup', onKeyUp , false );
	window.addEventListener ('resize', resizeViewport, false);
}

function resizeViewport() {
	canvas_width = $(window).width();
	camera.aspect = canvas_width/canvas_height;
	camera.updateProjectionMatrix();
	renderer.setSize( canvas_width, canvas_height);
}

function initGeometry(){
	// objLoader = new THREE.OBJLoader();
	JSONLoader = new THREE.JSONLoader();

	// roll-over Mesh
	//rollOverGeo = new THREE.CubeGeometry( VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE );
	cubeGeo = new THREE.CubeGeometry( VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE );
	rollOverMaterial = new THREE.MeshLambertMaterial( { color: 0xfcd87f, ambient: 0xfcd87f, opacity: 0.5, transparent: true } );
	defaultMaterial = new THREE.MeshLambertMaterial( { color: 0x384463, ambient: 0x384463, shading: THREE.FlatShading });
	rollOverMesh = new THREE.Mesh(cubeGeo, rollOverMaterial);
	rollOverMesh.position = objectWorldPosition;
	scene.add(rollOverMesh);
}

function initLighting(){
	var ambientLight = new THREE.AmbientLight( 0x606060 );
	scene.add( ambientLight );
	var directionalLight = new THREE.DirectionalLight( 0xddddff );
	directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
	scene.add( directionalLight );
}

function loadObjects ( objects ) {
	NObjectsToLoad = objects.length;
	for (var i in objects) {
		var obj = objects[i];
		loadObject(obj);
	}
}

function loadObject( obj ) {
	objectMeshes[obj.id] =
   	{
    	geometry : cubeGeo,
    	block_num: obj.block_num,
    	blocks: obj.blocks,
		category: obj.category,
		scale: new THREE.Vector3(1,1,1),
		dimensions: obj.dimensions,
		offsets: []
    };

    var offset =  new THREE.Vector3( obj.dimensions[0] - 1,  obj.dimensions[2] - 1, obj.dimensions[1] - 1);
	offset.multiplyScalar(VOXEL_SIZE * 0.5);

	objectMeshes[obj.id].offsets[0] = offset.clone();
	if (obj.block_num > 1) {
		objectMeshes[obj.id].offsets[1] = new THREE.Vector3( -offset.z, offset.y, offset.x );
	}

	if (obj.obj_file != "" ){
		JSONLoader.load( "obj/" + obj.obj_file, function(geometry, material) {
			loadJSONGeometry (obj.id, geometry,material) ;
			NObjectsToLoad--;
			if (NObjectsToLoad <= 0) startAnimation();
		} );
		return;
	}

	NObjectsToLoad--;
	if (NObjectsToLoad <= 0) startAnimation();
}

function loadJSONGeometry( id, geo, mat) {
	THREE.GeometryUtils.center(geo);
    objectMeshes[id].geometry = geo;

    if (objectMeshes[id].category == 'gadget')
    	objectMeshes[id].scale.set(VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE)
    else {
    	objectMeshes[id].scale.set(
			objectMeshes[id].dimensions[0]/(geo.boundingBox.max.x - geo.boundingBox.min.x),
			objectMeshes[id].dimensions[2]/(geo.boundingBox.max.y - geo.boundingBox.min.y),
			objectMeshes[id].dimensions[1]/(geo.boundingBox.max.z - geo.boundingBox.min.z)
		);
		objectMeshes[id].scale.multiplyScalar(VOXEL_SIZE);
	}
}

<commit_msg>scaling everything<commit_after>function initGraphics( container_id ) {
	scene = new THREE.Scene();
	projector = new THREE.Projector();

	canvas_width = $(window).width();
	canvas_height = $('#'+ container_id).height();

	camera = new THREE.PerspectiveCamera( 40, canvas_width/canvas_height, 1, 10000 );
	camera.position.y = 500;

	renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
	renderer.setSize( canvas_width, canvas_height);

	plane = new THREE.Mesh(
		new THREE.PlaneGeometry( VOXEL_SIZE * GRID_SIZE, VOXEL_SIZE * GRID_SIZE, GRID_SIZE, GRID_SIZE ),
		new THREE.MeshBasicMaterial( { color: 0x2c364f, wireframe: true } )
	);
	plane.rotation.x = - Math.PI / 2;
	plane.matrixAutoUpdate = false;
	plane.updateMatrix();
	scene.add( plane );

	initGeometry();
	initLighting();

	//APPEND CANVAS
	container = document.getElementById( container_id );
	container.appendChild( renderer.domElement );

	//picking
	container.addEventListener( 'mousemove', onMouseMove , false );
	container.addEventListener( 'mousedown', onMouseDown, false );
	document.addEventListener( 'keydown', onKeyDown, false );
	document.addEventListener( 'keyup', onKeyUp , false );
	window.addEventListener ('resize', resizeViewport, false);
}

function resizeViewport() {
	canvas_width = $(window).width();
	camera.aspect = canvas_width/canvas_height;
	camera.updateProjectionMatrix();
	renderer.setSize( canvas_width, canvas_height);
}

function initGeometry(){
	// objLoader = new THREE.OBJLoader();
	JSONLoader = new THREE.JSONLoader();

	// roll-over Mesh
	//rollOverGeo = new THREE.CubeGeometry( VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE );
	cubeGeo = new THREE.CubeGeometry( VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE );
	rollOverMaterial = new THREE.MeshLambertMaterial( { color: 0xfcd87f, ambient: 0xfcd87f, opacity: 0.5, transparent: true } );
	defaultMaterial = new THREE.MeshLambertMaterial( { color: 0x384463, ambient: 0x384463, shading: THREE.FlatShading });
	rollOverMesh = new THREE.Mesh(cubeGeo, rollOverMaterial);
	rollOverMesh.position = objectWorldPosition;
	scene.add(rollOverMesh);
}

function initLighting(){
	var ambientLight = new THREE.AmbientLight( 0x606060 );
	scene.add( ambientLight );
	var directionalLight = new THREE.DirectionalLight( 0xddddff );
	directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
	scene.add( directionalLight );
}

function loadObjects ( objects ) {
	NObjectsToLoad = objects.length;
	for (var i in objects) {
		var obj = objects[i];
		loadObject(obj);
	}
}

function loadObject( obj ) {
	objectMeshes[obj.id] =
   	{
    	geometry : cubeGeo,
    	block_num: obj.block_num,
    	blocks: obj.blocks,
		category: obj.category,
		scale: new THREE.Vector3(1,1,1),
		dimensions: obj.dimensions,
		offsets: []
    };

    var offset =  new THREE.Vector3( obj.dimensions[0] - 1,  obj.dimensions[2] - 1, obj.dimensions[1] - 1);
	offset.multiplyScalar(VOXEL_SIZE * 0.5);

	objectMeshes[obj.id].offsets[0] = offset.clone();
	if (obj.block_num > 1) {
		objectMeshes[obj.id].offsets[1] = new THREE.Vector3( -offset.z, offset.y, offset.x );
	}

	if (obj.obj_file != "" ){
		JSONLoader.load( "obj/" + obj.obj_file, function(geometry, material) {
			loadJSONGeometry (obj.id, geometry,material) ;
			NObjectsToLoad--;
			if (NObjectsToLoad <= 0) startAnimation();
		} );
		return;
	}

	NObjectsToLoad--;
	if (NObjectsToLoad <= 0) startAnimation();
}

function loadJSONGeometry( id, geo, mat) {
	THREE.GeometryUtils.center(geo);
    objectMeshes[id].geometry = geo;

	objectMeshes[id].scale.set(
		objectMeshes[id].dimensions[0]/(geo.boundingBox.max.x - geo.boundingBox.min.x),
		objectMeshes[id].dimensions[2]/(geo.boundingBox.max.y - geo.boundingBox.min.y),
		objectMeshes[id].dimensions[1]/(geo.boundingBox.max.z - geo.boundingBox.min.z)
	);
	objectMeshes[id].scale.multiplyScalar(VOXEL_SIZE);
}

<|endoftext|>


<commit_before>Blacklight.onLoad(function () {
    $('#homeTabs a, #myTab a').click(function (e) {
        e.preventDefault();
        $(this).tab('show');
    });
    $('#homeTabs a:first, #myTab a:first').tab('show'); // Select first tab
});
<commit_msg>Show tab using anchor<commit_after>Blacklight.onLoad(function () {
  $('#homeTabs a, #myTab a').click(function (e) {
    e.preventDefault();
    $(this).tab('show');
  });
  $('#homeTabs a:first, #myTab a:first').tab('show'); // Select first tab

  // Show the tabs in GenericFile#edit given an anchor
  switch (window.location.hash.substring(1)) {
    case 'versioning_display':
      $('#edit_versioning_link a').tab('show');
      break;
    case 'descriptions_display':
      $('#edit_descriptions_link a').tab('show');
      break;
    case 'permissions_display':
      $('#edit_permissions_link a').tab('show');
      break;
  }
});
<|endoftext|>


<commit_before>$(function(){
  $('#new_borrow_submit').click(function(){
    $form = $("form#new_borrow");
    $form.submit();
  });

  $('.loanable-button').click(function(e){
    //e.preventDefault();
    $('#myModal').modal();
  });

});

<commit_msg>Removed notworking modal<commit_after>$(function() {
    $('#new_borrow_submit').click(function() {
        $form = $("form#new_borrow");
        $form.submit();
    });

    $('.loanable-button').click(function(e) {
        //e.preventDefault();
        // $('#myModal').modal();
    });

});<|endoftext|>


<commit_before>'use strict';

var Workstream = require('./workstream.model');
var objectAssign = require('object-assign');

function handleError(res, err) {
    return res.send(500, err);
}

exports.index = function (req, res) {
    Workstream.all(function (err, workstreams) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstreams);
    });
};

exports.create = function (req, res) {
    Workstream.create(req.body, function (err) {

        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(req.body);
    });
};

exports.delete = function (req, res) {
    Workstream.delete(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.updateWorkstream = function (req, res) {
    delete req.body.__v;
    delete req.body._id;

    Workstream.update(req.params.workstream, req.body, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.getWorkstream = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.getWorkstreamTasks = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream.tasks);
    });
};

exports.saveWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        workstream.tasks.push(req.body);
        var task = workstream.tasks[workstream.tasks.length - 1];

        workstream.save(function (err) {
            if (err) {
                return res.status(400).json(err);
            }

            return res.status(200).json(task);
        })
    });
};

exports.updateWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(400);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        var updatedTask = objectAssign(task, req.body);

        workstream.save(function (err) {
            if (err) {
                return res.status(400).json(err);
            }

            return res.status(200).json(updatedTask);
        });
    });
};

exports.deleteWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        task.remove();
        workstream.save(function (err) {
            if (err) {
                return res.status(400).json(err);
            }

            return res.json(200);
        });
    });
};

exports.getWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(400);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        return res.status(200).json(task);
    });
};
<commit_msg>fixed a bug<commit_after>'use strict';

var Workstream = require('./workstream.model');
var objectAssign = require('object-assign');

function handleError(res, err) {
    return res.send(500, err);
}

exports.index = function (req, res) {
    Workstream.all(function (err, workstreams) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstreams);
    });
};

exports.create = function (req, res) {
    Workstream.create(req.body, function (err) {

        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(req.body);
    });
};

exports.delete = function (req, res) {
    Workstream.delete(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.updateWorkstream = function (req, res) {
    delete req.body.__v;
    delete req.body._id;

    Workstream.update(req.params.workstream, req.body, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.getWorkstream = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream);
    });
};

exports.getWorkstreamTasks = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        return res.status(200).json(workstream.tasks);
    });
};

exports.saveWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function(err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        workstream.tasks.push(req.body);

        workstream.save().then(function (task) {
            return res.status(200).json(task);
        }).catch(function(){
            return res.status(400).json(err);
        })

    });
};

exports.updateWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(400);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        var updatedTask = objectAssign(task, req.body);

        workstream.save(function (err) {
            if (err) {
                return res.status(400).json(err);
            }

            return res.status(200).json(updatedTask);
        });
    });
};

exports.deleteWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(err);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        task.remove();
        workstream.save(function (err) {
            if (err) {
                return res.status(400).json(err);
            }

            return res.json(200);
        });
    });
};

exports.getWorkstreamTask = function (req, res) {
    Workstream.findById(req.params.workstream, function (err, workstream) {
        if (err) {
            return res.status(400).json(400);
        }

        var task = workstream.tasks.id(req.params.task);

        if (!task) {
            return res.json(404);
        }

        return res.status(200).json(task);
    });
};
<|endoftext|>


<commit_before>const _ = require('lodash')
const cheerio = require('cheerio')
const uslug = require('uslug')

/* global WIKI */

module.exports = {
  async render() {
    const $ = cheerio.load(this.input)

    if ($.root().children().length < 1) {
      return ''
    }

    for (let child of this.children) {
      const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
      renderer.init($, child.config)
    }

    // --------------------------------
    // Detect internal / external links
    // --------------------------------

    let internalRefs = []
    const reservedPrefixes = /^\/[a-z]\//gi

    const isHostSet = WIKI.config.host.length > 7 && WIKI.config.host !== 'http://'
    if (!isHostSet) {
      WIKI.logger.warn('Host is not set. You must set the Site Host under General in the Administration Area!')
    }

    $('a').each((i, elm) => {
      let href = $(elm).attr('href')

      // -> Ignore empty / anchor links
      if (!href || href.length < 1 || href.indexOf('#') === 0 || href.indexOf('mailto:') === 0) {
        return
      }

      // -> Strip host from local links
      if (isHostSet && href.indexOf(WIKI.config.host) === 0) {
        href = href.replace(WIKI.config.host, '')
      }

      // -> Assign local / external tag
      if (href.indexOf('://') < 0) {
        // -> Remove trailing slash
        if (_.endsWith('/')) {
          href = href.slice(0, -1)
        }

        // -> Check for system prefix
        if (!reservedPrefixes.test(href)) {
          $(elm).addClass(`is-internal-link`)

          // -> Reformat paths
          if (href.indexOf('/') !== 0) {
            href = `/${this.page.localeCode}/${this.page.path}/${href}`
          } else if (href.charAt(3) !== '/') {
            href = `/${this.page.localeCode}${href}`
          }

          // -> Save internal references
          internalRefs.push({
            localeCode: href.substring(1, 3),
            path: _.head(href.substring(4).split('#'))
          })
        } else {
          $(elm).addClass(`is-system-link`)
        }
      } else {
        $(elm).addClass(`is-external-link`)
      }

      // -> Update element
      $(elm).attr('href', href)
    })

    // --------------------------------
    // Detect internal link states
    // --------------------------------

    const pastLinks = await this.page.$relatedQuery('links')

    if (internalRefs.length > 0) {
      // -> Find matching pages
      const results = await WIKI.models.pages.query().column('id', 'path', 'localeCode').where(builder => {
        internalRefs.forEach((ref, idx) => {
          if (idx < 1) {
            builder.where(ref)
          } else {
            builder.orWhere(ref)
          }
        })
      })

      // -> Apply tag to internal links for found pages
      $('a.is-internal-link').each((i, elm) => {
        const href = $(elm).attr('href')
        const hrefObj = {
          localeCode: href.substring(1, 3),
          path: _.head(href.substring(4).split('#'))
        }
        if (_.some(results, r => {
          return r.localeCode === hrefObj.localeCode && r.path === hrefObj.path
        })) {
          $(elm).addClass(`is-valid-page`)
        } else {
          $(elm).addClass(`is-invalid-page`)
        }
      })

      // -> Add missing links
      const missingLinks = _.differenceWith(internalRefs, pastLinks, (nLink, pLink) => {
        return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path
      })
      if (missingLinks.length > 0) {
        if (WIKI.config.db.type === 'postgres') {
          await WIKI.models.pageLinks.query().insert(missingLinks.map(lnk => ({
            pageId: this.page.id,
            path: lnk.path,
            localeCode: lnk.localeCode
          })))
        } else {
          for (const lnk of missingLinks) {
            await WIKI.models.pageLinks.query().insert({
              pageId: this.page.id,
              path: lnk.path,
              localeCode: lnk.localeCode
            })
          }
        }
      }
    }

    // -> Remove outdated links
    if (pastLinks) {
      const outdatedLinks = _.differenceWith(pastLinks, internalRefs, (nLink, pLink) => {
        return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path
      })
      if (outdatedLinks.length > 0) {
        await WIKI.models.pageLinks.query().delete().whereIn('id', _.map(outdatedLinks, 'id'))
      }
    }

    // --------------------------------
    // Add header handles
    // --------------------------------

    let headers = []
    $('h1,h2,h3,h4,h5,h6').each((i, elm) => {
      if ($(elm).attr('id')) {
        return
      }
      let headerSlug = uslug($(elm).text())

      // -> Cannot start with a number (CSS selector limitation)
      if (headerSlug.match(/^\d/)) {
        headerSlug = `h-${headerSlug}`
      }

      // -> Make sure header is unique
      if (headers.indexOf(headerSlug) >= 0) {
        let isUnique = false
        let hIdx = 1
        while (!isUnique) {
          const headerSlugTry = `${headerSlug}-${hIdx}`
          if (headers.indexOf(headerSlugTry) < 0) {
            isUnique = true
            headerSlug = headerSlugTry
          }
          hIdx++
        }
      }

      // -> Add anchor
      $(elm).attr('id', headerSlug).addClass('toc-header')
      $(elm).prepend(`<a class="toc-anchor" href="#${headerSlug}">¶</a> `)

      headers.push(headerSlug)
    })

    return $.html('body').replace('', '').replace('', '')
  }
}
<commit_msg>fix: handle link reformatting when not using namespacing + 5 letters locales support (#1165, #1166)<commit_after>const _ = require('lodash')
const cheerio = require('cheerio')
const uslug = require('uslug')
const pageHelper = require('../../../helpers/page')
const URL = require('url').URL

/* global WIKI */

module.exports = {
  async render() {
    const $ = cheerio.load(this.input)

    if ($.root().children().length < 1) {
      return ''
    }

    for (let child of this.children) {
      const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
      renderer.init($, child.config)
    }

    // --------------------------------
    // Detect internal / external links
    // --------------------------------

    let internalRefs = []
    const reservedPrefixes = /^\/[a-z]\//gi
    const exactReservedPaths = /^\/[a-z]$/gi

    const isHostSet = WIKI.config.host.length > 7 && WIKI.config.host !== 'http://'
    if (!isHostSet) {
      WIKI.logger.warn('Host is not set. You must set the Site Host under General in the Administration Area!')
    }

    $('a').each((i, elm) => {
      let href = $(elm).attr('href')

      // -> Ignore empty / anchor links
      if (!href || href.length < 1 || href.indexOf('#') === 0 || href.indexOf('mailto:') === 0) {
        return
      }

      // -> Strip host from local links
      if (isHostSet && href.indexOf(WIKI.config.host) === 0) {
        href = href.replace(WIKI.config.host, '')
      }

      // -> Assign local / external tag
      if (href.indexOf('://') < 0) {
        // -> Remove trailing slash
        if (_.endsWith('/')) {
          href = href.slice(0, -1)
        }

        // -> Check for system prefix
        if (!reservedPrefixes.test(href) && !exactReservedPaths.test(href)) {
          let pagePath = null

          // -> Add locale prefix if using namespacing
          if (WIKI.config.lang.namespacing) {
            // -> Reformat paths
            if (href.indexOf('/') !== 0) {
              href = `/${this.page.localeCode}/${this.page.path}/${href}`
            } else if (href.charAt(3) !== '/') {
              href = `/${this.page.localeCode}${href}`
            }

            try {
              const parsedUrl = new URL(`http://x${href}`)
              pagePath = pageHelper.parsePath(parsedUrl.pathname)
            } catch (err) {
              return
            }
          } else {
            // -> Reformat paths
            if (href.indexOf('/') !== 0) {
              href = `/${this.page.path}/${href}`
            }

            try {
              const parsedUrl = new URL(`http://x${href}`)
              pagePath = pageHelper.parsePath(parsedUrl.pathname)
            } catch (err) {
              return
            }
          }
          // -> Save internal references
          internalRefs.push({
            localeCode: pagePath.locale,
            path: pagePath.path
          })

          $(elm).addClass(`is-internal-link`)
        } else {
          $(elm).addClass(`is-system-link`)
        }
      } else {
        $(elm).addClass(`is-external-link`)
      }

      // -> Update element
      $(elm).attr('href', href)
    })

    // --------------------------------
    // Detect internal link states
    // --------------------------------

    const pastLinks = await this.page.$relatedQuery('links')

    if (internalRefs.length > 0) {
      // -> Find matching pages
      const results = await WIKI.models.pages.query().column('id', 'path', 'localeCode').where(builder => {
        internalRefs.forEach((ref, idx) => {
          if (idx < 1) {
            builder.where(ref)
          } else {
            builder.orWhere(ref)
          }
        })
      })

      // -> Apply tag to internal links for found pages
      $('a.is-internal-link').each((i, elm) => {
        const href = $(elm).attr('href')
        let hrefObj = {}
        try {
          const parsedUrl = new URL(`http://x${href}`)
          hrefObj = pageHelper.parsePath(parsedUrl.pathname)
        } catch (err) {
          return
        }
        if (_.some(results, r => {
          return r.localeCode === hrefObj.locale && r.path === hrefObj.path
        })) {
          $(elm).addClass(`is-valid-page`)
        } else {
          $(elm).addClass(`is-invalid-page`)
        }
      })

      // -> Add missing links
      const missingLinks = _.differenceWith(internalRefs, pastLinks, (nLink, pLink) => {
        return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path
      })
      if (missingLinks.length > 0) {
        if (WIKI.config.db.type === 'postgres') {
          await WIKI.models.pageLinks.query().insert(missingLinks.map(lnk => ({
            pageId: this.page.id,
            path: lnk.path,
            localeCode: lnk.localeCode
          })))
        } else {
          for (const lnk of missingLinks) {
            await WIKI.models.pageLinks.query().insert({
              pageId: this.page.id,
              path: lnk.path,
              localeCode: lnk.localeCode
            })
          }
        }
      }
    }

    // -> Remove outdated links
    if (pastLinks) {
      const outdatedLinks = _.differenceWith(pastLinks, internalRefs, (nLink, pLink) => {
        return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path
      })
      if (outdatedLinks.length > 0) {
        await WIKI.models.pageLinks.query().delete().whereIn('id', _.map(outdatedLinks, 'id'))
      }
    }

    // --------------------------------
    // Add header handles
    // --------------------------------

    let headers = []
    $('h1,h2,h3,h4,h5,h6').each((i, elm) => {
      if ($(elm).attr('id')) {
        return
      }
      let headerSlug = uslug($(elm).text())

      // -> Cannot start with a number (CSS selector limitation)
      if (headerSlug.match(/^\d/)) {
        headerSlug = `h-${headerSlug}`
      }

      // -> Make sure header is unique
      if (headers.indexOf(headerSlug) >= 0) {
        let isUnique = false
        let hIdx = 1
        while (!isUnique) {
          const headerSlugTry = `${headerSlug}-${hIdx}`
          if (headers.indexOf(headerSlugTry) < 0) {
            isUnique = true
            headerSlug = headerSlugTry
          }
          hIdx++
        }
      }

      // -> Add anchor
      $(elm).attr('id', headerSlug).addClass('toc-header')
      $(elm).prepend(`<a class="toc-anchor" href="#${headerSlug}">¶</a> `)

      headers.push(headerSlug)
    })

    return $.html('body').replace('', '').replace('', '')
  }
}
<|endoftext|>


<commit_before>"use strict";
// TODO: fix high density x axis
// TODO: bring back tooltips

(function() {
    var tooltip = function(element) {
        this.element = element;
    };
    tooltip.prototype.show = function(x, y, dataPoint) {
        if (x && y && dataPoint) {
            this.element.querySelector('.highest-bidder-value').innerHTML = renderCoins(dataPoint.buyStatistics.maxPrice);
            this.element.querySelector('.avg-bidder-value').innerHTML = renderCoins(dataPoint.buyStatistics.average);
            this.element.querySelector('.lowest-seller-value').innerHTML = renderCoins(dataPoint.sellStatistics.minPrice);
            this.element.querySelector('.avg-seller-value').innerHTML = renderCoins(dataPoint.sellStatistics.average);
            this.element.style.left = (x  - this.element.clientWidth - 30) + "px";
            this.element.style.top = (y  - (this.element.clientHeight / 2)) + "px";
        } else {
        this.element.style.display = '';
        }
    };
    tooltip.prototype.hide = function() {
        this.element.style.display = 'none';
    };

    var bisectDate = d3.bisector(function(d) { return d.timestamp; }).left;

    var renderCoins = function(coins) {
        coins = Math.floor(coins);
        var copper = coins % 100;
        coins = Math.floor(coins / 100.0);
        var silver = coins % 100;
        coins = Math.floor(coins / 100.0);
        var gold = coins;

        var html = '';
        if (gold > 0) {
            html += '' + gold + '';
        }
        if (silver > 0) {
            html += '' + silver + '';
        }
        html += '' + copper + '';

        return html;
    };

    var chart = function(element) {
        var margin = {
            top: 20,
            right: 150,
            bottom: 30,
            left: 50
        },
        width = element[0][0].clientWidth - margin.left - margin.right,
        height = 400 - margin.top - margin.bottom,
        tickFormat = d3.time.format.multi([
                ["%H:%M", function(d) { return d.getHours(); }],
                ["%e %b", function(d) { return true; }]
            ]),
        self = this;

        this.x = d3.time.scale().range([0, width]);
        this.xAxis = d3.svg.axis().scale(this.x).orient("bottom").tickFormat(tickFormat).ticks(d3.time.hours, 8);
        this.xFn = function(d) { return self.x(d.timestamp); };

        this.y = d3.scale.linear().range([height, 0]);
        this.yAxis = d3.svg.axis().scale(this.y).orient("left");

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

        svg.append("g")
            .attr("class", "gw2-charts-x gw2-charts-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(this.xAxis);
        svg.append("g")
            .attr("class", "gw2-charts-y gw2-charts-axis")
            .call(this.yAxis);

        svg.append("rect")
            .attr("class", "gw2-charts-overlay")
            .attr("width", width)
            .attr("height", height)
            .on("mouseover", function() {
                self.lines.forEach(function(line) {
                    line.focus.style("display", null);
                });
            })
            .on("mouseout", function() {
                self.lines.forEach(function(line) {
                    line.focus.style("display", "none");
                });
            })
            .on("mousemove", function() {
                if (!self.data || self.data.length < 2) {
                    return;
                }
                var x0 = self.x.invert(d3.mouse(this)[0]),
                    i = Math.min(self.data.length - 1, bisectDate(self.data, x0, 1)),
                    d0 = self.data[i - 1],
                    d1 = self.data[i],
                    d = x0 - d0.timestamp > d1.timestamp - x0 ? d1 : d0,
                    x = self.x(d.timestamp);

                self.lines.forEach(function(line) {
                    line.focus.attr("transform", "translate(" + x + "," + line.yFn(d, self.y) + ")");
                });
            });

        this.svg = svg;
        this.lines = [];
    };
    chart.prototype.add = function(conf) {
        var x = this.x,
            y = this.y,
            yFnWrapper = function(d) {
                return conf.yFn(d, y);
            },
            line = d3.svg.line().x(this.xFn).y(yFnWrapper),
            path = this.svg.append("path").attr("class", conf.cls),
            focusCircle = this.svg.append("g").attr("class", conf.focusCls).style("display", "none"),
            lineLabel = this.svg.append("text").attr("dy", ".35em").attr('class', conf.cls).attr("text-anchor", "start").text(conf.label);

        focusCircle.append("circle").attr("r", 3);

        this.lines.push({
            line: line,
            path: path,
            focus: focusCircle,
            lineLabel, lineLabel,
            yFn: conf.yFn
        });
    };
    chart.prototype.update = function(data, xRange, yRange) {
        this.data = data;

        this.x.domain(xRange);
        this.y.domain(yRange);

        var svg = this.svg.transition(),
            lastDataPoint = this.data[this.data.length - 1],
            self = this;

        svg.select(".gw2-charts-x").call(this.xAxis);
        svg.select(".gw2-charts-y").call(this.yAxis);

        this.lines.forEach(function(line) {
            line.path.attr('d', line.line(data));
            line.lineLabel.attr("transform", "translate(" + (self.x(lastDataPoint.timestamp) + 10) + "," + line.yFn(lastDataPoint, self.y) + ")");
        });
    };

    window.gw2charts = {
        Tooltip: tooltip,
        Chart: chart
    };
})();

(function() {
    var renderDate = function(date) {
        var isoStr = date.toISOString();
        // Cut off the milliseconds and timezone.
        return isoStr.substring(0, isoStr.length - 5);
    },
    yScalePriceHistoryFn = function(data) {
        var maxPrice = d3.max(data, function(d) {
            return Math.max(
                Math.max(d.buyStatistics.maxPrice, d.sellStatistics.minPrice),
                Math.max(d.buyStatistics.average, d.sellStatistics.average)
                );
            });

        return [ 0, maxPrice * 1.1 ];
    },
    yScaleSupplyDemandFn = function(data) {
        var totalAmount = d3.max(data, function(d) {
            return Math.max(
                    d.buyStatistics.totalAmount,
                    d.sellStatistics.totalAmount
            );
        });

        return [ 0, totalAmount ];
    },
    createPriceHistoryChart = function() {
        var chart = new gw2charts.Chart(d3.select("#priceHistory"));
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.minPrice); },
            label: 'Lowest sellers',
            cls: "gw2-history-sellers",
            focusCls: "gw2-history-sellers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.average); },
            label: 'Avg. sellers',
            cls: "gw2-history-sellers-avg",
            focusCls: "gw2-history-sellers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.maxPrice); },
            label: 'Highest buyers',
            cls: "gw2-history-buyers",
            focusCls: "gw2-history-buyers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.average); },
            label: 'Avg. buyers',
            cls: "gw2-history-buyers-avg",
            focusCls: "gw2-history-buyers-focus"
        });

        return chart;
    },
    createSupplyDemandChart = function() {
        var chart = new gw2charts.Chart(d3.select("#supplyDemand"));
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.totalAmount); },
            label: 'Sellers',
            cls: 'gw2-history-sellers',
            focusCls: 'gw2-history-sellers-focus'
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.totalAmount); },
            label: 'Buyers',
            cls: 'gw2-history-buyers',
            focusCls: 'gw2-history-buyers-focus'
        });

        return chart;
    };

    var priceHistory = createPriceHistoryChart(),
        supplyDemand = createSupplyDemandChart();

    var onTimeselectorClick = function(btn) {
        var offset = btn.getAttribute('data-offset').split(/([0-9]*)/g),
            to = new Date(),
            from = new Date();

        // offset should look like this now: [ "", "1", "d" ] or [ "", "13", "m", "37", "d" ]
        if (offset.length > 1 && offset.length % 2 == 1) {
            for (var i = 1; i < offset.length; i += 2) {
                var amount = parseInt(offset[i], 10);
                switch (offset[i + 1]) {
                    case 'm':
                        from.setMonth(from.getMonth() - amount);
                        break;
                    case 'd':
                        from.setDate(from.getDate() - amount);
                        break;
                }
            }
        }

        var fromStr = renderDate(from),
            toStr = renderDate(to);

        d3.json('/api/history/' + gw2scope.itemId + '?from=' + fromStr + '&to=' + toStr, function(err, data) {
            var yScale = yScalePriceHistoryFn(data);
            priceHistory.update(data, [ from, to ], yScale);

            yScale = yScaleSupplyDemandFn(data);
            supplyDemand.update(data, [ from, to ], yScale);
        });
    };

    var timeframeSelectors = document.getElementById("chart-timeframe-selectors").querySelectorAll('button[data-offset]'),
        initialTimeframeSelector;
    for (var i = 0; i < timeframeSelectors.length; i++) {
        if (timeframeSelectors[i].getAttribute('data-initial') === 'true') {
            initialTimeframeSelector = timeframeSelectors[i];
        }
        timeframeSelectors[i].addEventListener('click', function() {
            onTimeselectorClick(this);
        });
    };

    if (initialTimeframeSelector) {
        onTimeselectorClick(initialTimeframeSelector);
    }
})();
<commit_msg>the x/time axis scales nicer now<commit_after>"use strict";
// TODO: bring back tooltips

(function() {
    var tooltip = function(element) {
        this.element = element;
    };
    tooltip.prototype.show = function(x, y, dataPoint) {
        if (x && y && dataPoint) {
            this.element.querySelector('.highest-bidder-value').innerHTML = renderCoins(dataPoint.buyStatistics.maxPrice);
            this.element.querySelector('.avg-bidder-value').innerHTML = renderCoins(dataPoint.buyStatistics.average);
            this.element.querySelector('.lowest-seller-value').innerHTML = renderCoins(dataPoint.sellStatistics.minPrice);
            this.element.querySelector('.avg-seller-value').innerHTML = renderCoins(dataPoint.sellStatistics.average);
            this.element.style.left = (x  - this.element.clientWidth - 30) + "px";
            this.element.style.top = (y  - (this.element.clientHeight / 2)) + "px";
        } else {
        this.element.style.display = '';
        }
    };
    tooltip.prototype.hide = function() {
        this.element.style.display = 'none';
    };

    var bisectDate = d3.bisector(function(d) { return d.timestamp; }).left;

    var renderCoins = function(coins) {
        coins = Math.floor(coins);
        var copper = coins % 100;
        coins = Math.floor(coins / 100.0);
        var silver = coins % 100;
        coins = Math.floor(coins / 100.0);
        var gold = coins;

        var html = '';
        if (gold > 0) {
            html += '' + gold + '';
        }
        if (silver > 0) {
            html += '' + silver + '';
        }
        html += '' + copper + '';

        return html;
    };

    var chart = function(element) {
        var margin = {
            top: 20,
            right: 150,
            bottom: 30,
            left: 50
        },
        width = element[0][0].clientWidth - margin.left - margin.right,
        height = 400 - margin.top - margin.bottom,
        tickFormat = d3.time.format.multi([
                ["%H:%M", function(d) { return d.getHours(); }],
                ["%e %b", function(d) { return true; }]
            ]),
        self = this;

        this.x = d3.time.scale().range([0, width]);
        this.xAxis = d3.svg.axis().scale(this.x).orient("bottom").tickFormat(tickFormat).ticks(d3.time.hours, 8);
        this.xFn = function(d) { return self.x(d.timestamp); };

        this.y = d3.scale.linear().range([height, 0]);
        this.yAxis = d3.svg.axis().scale(this.y).orient("left");

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

        svg.append("g")
            .attr("class", "gw2-charts-x gw2-charts-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(this.xAxis);
        svg.append("g")
            .attr("class", "gw2-charts-y gw2-charts-axis")
            .call(this.yAxis);

        svg.append("rect")
            .attr("class", "gw2-charts-overlay")
            .attr("width", width)
            .attr("height", height)
            .on("mouseover", function() {
                self.lines.forEach(function(line) {
                    line.focus.style("display", null);
                });
            })
            .on("mouseout", function() {
                self.lines.forEach(function(line) {
                    line.focus.style("display", "none");
                });
            })
            .on("mousemove", function() {
                if (!self.data || self.data.length < 2) {
                    return;
                }
                var x0 = self.x.invert(d3.mouse(this)[0]),
                    i = Math.min(self.data.length - 1, bisectDate(self.data, x0, 1)),
                    d0 = self.data[i - 1],
                    d1 = self.data[i],
                    d = x0 - d0.timestamp > d1.timestamp - x0 ? d1 : d0,
                    x = self.x(d.timestamp);

                self.lines.forEach(function(line) {
                    line.focus.attr("transform", "translate(" + x + "," + line.yFn(d, self.y) + ")");
                });
            });

        this.svg = svg;
        this.lines = [];
    };
    chart.prototype.add = function(conf) {
        var x = this.x,
            y = this.y,
            yFnWrapper = function(d) {
                return conf.yFn(d, y);
            },
            line = d3.svg.line().x(this.xFn).y(yFnWrapper),
            path = this.svg.append("path").attr("class", conf.cls),
            focusCircle = this.svg.append("g").attr("class", conf.focusCls).style("display", "none"),
            lineLabel = this.svg.append("text").attr("dy", ".35em").attr('class', conf.cls).attr("text-anchor", "start").text(conf.label);

        focusCircle.append("circle").attr("r", 3);

        this.lines.push({
            line: line,
            path: path,
            focus: focusCircle,
            lineLabel, lineLabel,
            yFn: conf.yFn
        });
    };
    chart.prototype.update = function(data, xRange, yRange) {
        this.data = data;

        var timeDelta = xRange[1].getTime() - xRange[0].getTime(),
            // an approx. delta of days is sufficient here
            daysDelta = timeDelta / (24 * 60 * 60 * 1000);

        if (daysDelta > 30) {
            this.xAxis.ticks(d3.time.days, 7);
        } else if (daysDelta > 10) {
            this.xAxis.ticks(d3.time.days, 1);
        } else if (daysDelta > 2) {
            this.xAxis.ticks(d3.time.hours, 8);
        } else {
            this.xAxis.ticks(d3.time.hours, 1);
        }
        this.x.domain(xRange);
        this.y.domain(yRange);

        var svg = this.svg.transition(),
            lastDataPoint = this.data[this.data.length - 1],
            self = this;

        svg.select(".gw2-charts-x").call(this.xAxis);
        svg.select(".gw2-charts-y").call(this.yAxis);

        this.lines.forEach(function(line) {
            line.path.attr('d', line.line(data));
            line.lineLabel.attr("transform", "translate(" + (self.x(lastDataPoint.timestamp) + 10) + "," + line.yFn(lastDataPoint, self.y) + ")");
        });
    };

    window.gw2charts = {
        Tooltip: tooltip,
        Chart: chart
    };
})();

(function() {
    var renderDate = function(date) {
        var isoStr = date.toISOString();
        // Cut off the milliseconds and timezone.
        return isoStr.substring(0, isoStr.length - 5);
    },
    yScalePriceHistoryFn = function(data) {
        var maxPrice = d3.max(data, function(d) {
            return Math.max(
                Math.max(d.buyStatistics.maxPrice, d.sellStatistics.minPrice),
                Math.max(d.buyStatistics.average, d.sellStatistics.average)
                );
            });

        return [ 0, maxPrice * 1.1 ];
    },
    yScaleSupplyDemandFn = function(data) {
        var totalAmount = d3.max(data, function(d) {
            return Math.max(
                    d.buyStatistics.totalAmount,
                    d.sellStatistics.totalAmount
            );
        });

        return [ 0, totalAmount ];
    },
    createPriceHistoryChart = function() {
        var chart = new gw2charts.Chart(d3.select("#priceHistory"));
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.minPrice); },
            label: 'Lowest sellers',
            cls: "gw2-history-sellers",
            focusCls: "gw2-history-sellers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.average); },
            label: 'Avg. sellers',
            cls: "gw2-history-sellers-avg",
            focusCls: "gw2-history-sellers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.maxPrice); },
            label: 'Highest buyers',
            cls: "gw2-history-buyers",
            focusCls: "gw2-history-buyers-focus"
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.average); },
            label: 'Avg. buyers',
            cls: "gw2-history-buyers-avg",
            focusCls: "gw2-history-buyers-focus"
        });

        return chart;
    },
    createSupplyDemandChart = function() {
        var chart = new gw2charts.Chart(d3.select("#supplyDemand"));
        chart.add({
            yFn: function(d, y) { return y(d.sellStatistics.totalAmount); },
            label: 'Sellers',
            cls: 'gw2-history-sellers',
            focusCls: 'gw2-history-sellers-focus'
        });
        chart.add({
            yFn: function(d, y) { return y(d.buyStatistics.totalAmount); },
            label: 'Buyers',
            cls: 'gw2-history-buyers',
            focusCls: 'gw2-history-buyers-focus'
        });

        return chart;
    };

    var priceHistory = createPriceHistoryChart(),
        supplyDemand = createSupplyDemandChart();

    var onTimeselectorClick = function(btn) {
        var offset = btn.getAttribute('data-offset').split(/([0-9]*)/g),
            to = new Date(),
            from = new Date();

        // offset should look like this now: [ "", "1", "d" ] or [ "", "13", "m", "37", "d" ]
        if (offset.length > 1 && offset.length % 2 == 1) {
            for (var i = 1; i < offset.length; i += 2) {
                var amount = parseInt(offset[i], 10);
                switch (offset[i + 1]) {
                    case 'm':
                        from.setMonth(from.getMonth() - amount);
                        break;
                    case 'd':
                        from.setDate(from.getDate() - amount);
                        break;
                }
            }
        }

        var fromStr = renderDate(from),
            toStr = renderDate(to);

        d3.json('/api/history/' + gw2scope.itemId + '?from=' + fromStr + '&to=' + toStr, function(err, data) {
            var yScale = yScalePriceHistoryFn(data);
            priceHistory.update(data, [ from, to ], yScale);

            yScale = yScaleSupplyDemandFn(data);
            supplyDemand.update(data, [ from, to ], yScale);
        });
    };

    var timeframeSelectors = document.getElementById("chart-timeframe-selectors").querySelectorAll('button[data-offset]'),
        initialTimeframeSelector;
    for (var i = 0; i < timeframeSelectors.length; i++) {
        if (timeframeSelectors[i].getAttribute('data-initial') === 'true') {
            initialTimeframeSelector = timeframeSelectors[i];
        }
        timeframeSelectors[i].addEventListener('click', function() {
            onTimeselectorClick(this);
        });
    };

    if (initialTimeframeSelector) {
        onTimeselectorClick(initialTimeframeSelector);
    }
})();
<|endoftext|>