<commit_before>

function capture(video, canvas, image, captureButton, stopButton, snapshotButton) {
	var ctx = canvas.getContext('2d');
	var localMediaStream = null;
	var track = null;

	captureButton.disabled = true;
	stopButton.disabled = false;
	snapshotButton.disabled = false;

	stopButton.onclick = function () {
		stop(video);
	};

	window.URL = window.URL || window.webkitURL;
	navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia ||
						  navigator.mozGetUserMedia || navigator.msGetUserMedia;

	var constraints = {
		video: true
	}

	var successCallback = function(mediaStream) {
		video.src = (window.URL && window.URL.createObjectURL(mediaStream)) || mediaStream;
		video.addEventListener("loadedmetadata", function(e) {
			video.style.display = "block";
			localMediaStream = mediaStream;
			track = localMediaStream.getTracks()[0];
			snapshotButton.onclick = function(event) {
				takePhoto();
				//Added code for saving image
				var dataURL = canvas.toDataURL("image/png");
				//console.log(dataURL);
				//window.open(dataURL, "toDataURL() image", "width=600, height=200");
				//var ajax = new XMLHttpRequest();
				//ajax.open("POST",'index.php',false);
				//ajax.setRequestHeader('Content-Type', 'application/upload');
				//ajax.send(dataURL);

				//Confirmation for photo
				notie.confirm('Do you want to use this as your Photo of the Day?', 'Yes', 'Cancel', function()
                {
                	var currentDate = new Date();
					var dd = currentDate.getDate();
					var mm = currentDate.getMonth(); //January is 0!
					var yyyy = currentDate.getFullYear();

					var images;

					if(localStorage.getItem('images')===null)
					{
						images = [];
					}
					else
					{
						images = JSON.parse(localStorage.getItem('images'));
					}

                	//dataURL.replace(/^data:image\/(png|jpg);base64,/,"");
					//localStorage.setItem('image2', dataURL);
					if(localStorage.getItem('images')===null)
					{
						var newPhoto = new Object();
						newPhoto["id"] = "image"+0;
						newPhoto["image"] = dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["day"] = dd;
						newPhoto["month"] = mm;
						newPhoto["year"] = yyyy;
						newPhoto["description"] = "";
						//var newImage = new Image();
						//newImage.src = dataURL;
						images.push(newPhoto);
						//console.log(JSON.stringify(images));
						localStorage.setItem('images', JSON.stringify(images));
					}
					else if((dd !== images[images.length-1]["day"]) && (mm !== images[images.length-1]["month"]) && (yyyy !== images[images.length-1]["year"]))
					{
						var newPhoto = new Object();
						newPhoto["id"] = "image"+(images.length);
						newPhoto["image"] = dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["day"] = dd;
						newPhoto["month"] = mm;
						newPhoto["year"] = yyyy;
						newPhoto["description"] = "";
						//console.log(images.src);
						//var newImage = new Image();
						//newImage.src = dataURL;
						images.push(newPhoto);
						//console.log(images);
						//console.log(JSON.stringify(images));
						localStorage.setItem('images', JSON.stringify(images));
					}
					else
					{
						console.log("DENIED");
					}
					var data1;
			      	/*$.getJSON("images.json", function(data)
			      	{
			       	 	//console.log('My JSON data: ', data);
			        	//$('#myData').text(JSON.stringify(data))

			        	console.log(data);
						var idN = "image"+data.images.length++;

						var newPhoto = new Object();
						newPhoto["id"] = idN;
						newPhoto["image"] = "data:image/png;base64," + dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["date"] = Date();
						newPhoto["description"] = "";

						data["images"].push(newPhoto);
						console.log(data);
			      	});*/

					//COOKIE ATTEMPT
					/*if( typeof Cookies.getJSON('images') === 'undefined')
					{
						var imagesJSON = {
								"id": "image" + 0,
								"image": dataURL, //PROBLEM: dataURL too big?
								"thumb": "thumb-01.jpg"
						}
						Cookies.set('images', imagesJSON);
						console.log(Cookies.getJSON());
					}
					else
					{
						var imagesJSON = Cookies.getJSON('images');
						var newImage = {
							"id": "image"+(++imagesJSON.images.length),
							"image": dataURL,
							"thumb": "thumb-01.jpg"
						}
						imagesJSON.push(newImage);
						console.log("UPDATED NE: " + imagesJSON);
						Cookies.set('images', imagesJSON);
					}*/

                	notie.alert(1, 'Good choice!', 2);
                });
			}
		});
	};

	var errorCallback = function() {
		console.log("failure to get media");
	};

	var takePhoto = function () {
		ctx.drawImage(video, 0, 0, 320, 240);
		canvas.style.display = "block";
		showImage();
	};

	var showImage = function () {
		image.src = canvas.toDataURL('image/webp');
		image.style.display = "none";
	};

	var stop = function () {
		if (localMediaStream) {
			track.stop(); /* TODO: it doesn't work in Opera Mobile 12 */
		}

		captureButton.disabled = false;
		stopButton.disabled = true;
		snapshotButton.disabled = true;
	};

	if (navigator.getUserMedia) {
		navigator.getUserMedia(constraints, successCallback, errorCallback);
	} else {
		console.log("getUserMedia not supported");
	}
}

function getConfirmation(){
   var retVal = confirm("Do you want to use this photo for your Photo of the Day?");
   if( retVal == true ){
      //document.write ("User wants to use photo!");
      return true;
   }
   else{
      //document.write ("User does not want to use photo!");
      return false;
   }
}

function upload(file) {
  var form = new FormData(),
      xhr = new XMLHttpRequest();

  form.append('image', file);
  xhr.open('post', 'server.php', true);
  xhr.send(form);
}

function init() {
	var video = document.querySelector('video#getUserMedia');
	var canvas = document.querySelector('#canvas');
	var snapshot = document.querySelector('#snapshot');
	var captureButton = document.querySelector('#captureButton');
	var stopButton = document.querySelector('#stopButton');
	var snapshotButton = document.querySelector('#snapshotButton');

	captureButton.onclick = function () {
		capture(video, canvas, snapshot, captureButton, stopButton, snapshotButton);
	};
}<commit_msg>added error message for trying to add multiple daily photos<commit_after>

function capture(video, canvas, image, captureButton, stopButton, snapshotButton) {
	var ctx = canvas.getContext('2d');
	var localMediaStream = null;
	var track = null;

	captureButton.disabled = true;
	stopButton.disabled = false;
	snapshotButton.disabled = false;

	stopButton.onclick = function () {
		stop(video);
	};

	window.URL = window.URL || window.webkitURL;
	navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia ||
						  navigator.mozGetUserMedia || navigator.msGetUserMedia;

	var constraints = {
		video: true
	}

	var successCallback = function(mediaStream) {
		video.src = (window.URL && window.URL.createObjectURL(mediaStream)) || mediaStream;
		video.addEventListener("loadedmetadata", function(e) {
			video.style.display = "block";
			localMediaStream = mediaStream;
			track = localMediaStream.getTracks()[0];
			snapshotButton.onclick = function(event) {
				takePhoto();
				//Added code for saving image
				var dataURL = canvas.toDataURL("image/png");
				//console.log(dataURL);
				//window.open(dataURL, "toDataURL() image", "width=600, height=200");
				//var ajax = new XMLHttpRequest();
				//ajax.open("POST",'index.php',false);
				//ajax.setRequestHeader('Content-Type', 'application/upload');
				//ajax.send(dataURL);

				//Confirmation for photo
				notie.confirm('Do you want to use this as your Photo of the Day?', 'Yes', 'Cancel', function()
                {
                	var currentDate = new Date();
					var dd = currentDate.getDate();
					var mm = currentDate.getMonth(); //January is 0!
					var yyyy = currentDate.getFullYear();

					var images;

					if(localStorage.getItem('images')===null)
					{
						images = [];
					}
					else
					{
						images = JSON.parse(localStorage.getItem('images'));
					}

                	//dataURL.replace(/^data:image\/(png|jpg);base64,/,"");
					//localStorage.setItem('image2', dataURL);
					if(localStorage.getItem('images')===null)
					{
						var newPhoto = new Object();
						newPhoto["id"] = "image"+0;
						newPhoto["image"] = dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["day"] = dd;
						newPhoto["month"] = mm;
						newPhoto["year"] = yyyy;
						newPhoto["description"] = "";
						//var newImage = new Image();
						//newImage.src = dataURL;
						images.push(newPhoto);
						//console.log(JSON.stringify(images));
						localStorage.setItem('images', JSON.stringify(images));
					}
					else if((dd !== images[images.length-1]["day"]) && (mm !== images[images.length-1]["month"]) && (yyyy !== images[images.length-1]["year"]))
					{
						var newPhoto = new Object();
						newPhoto["id"] = "image"+(images.length);
						newPhoto["image"] = dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["day"] = dd;
						newPhoto["month"] = mm;
						newPhoto["year"] = yyyy;
						newPhoto["description"] = "";
						//console.log(images.src);
						//var newImage = new Image();
						//newImage.src = dataURL;
						images.push(newPhoto);
						//console.log(images);
						//console.log(JSON.stringify(images));
						localStorage.setItem('images', JSON.stringify(images));
					}
					else
					{
						notie.alert(3, "Error!
 You've already taken your daily photo for today!
Come back tomorrow!" , 3);
						console.log("DENIED");
					}
					var data1;
			      	/*$.getJSON("images.json", function(data)
			      	{
			       	 	//console.log('My JSON data: ', data);
			        	//$('#myData').text(JSON.stringify(data))

			        	console.log(data);
						var idN = "image"+data.images.length++;

						var newPhoto = new Object();
						newPhoto["id"] = idN;
						newPhoto["image"] = "data:image/png;base64," + dataURL;
						newPhoto["thumb"] = "thumb-01.jpg";
						newPhoto["date"] = Date();
						newPhoto["description"] = "";

						data["images"].push(newPhoto);
						console.log(data);
			      	});*/

					//COOKIE ATTEMPT
					/*if( typeof Cookies.getJSON('images') === 'undefined')
					{
						var imagesJSON = {
								"id": "image" + 0,
								"image": dataURL, //PROBLEM: dataURL too big?
								"thumb": "thumb-01.jpg"
						}
						Cookies.set('images', imagesJSON);
						console.log(Cookies.getJSON());
					}
					else
					{
						var imagesJSON = Cookies.getJSON('images');
						var newImage = {
							"id": "image"+(++imagesJSON.images.length),
							"image": dataURL,
							"thumb": "thumb-01.jpg"
						}
						imagesJSON.push(newImage);
						console.log("UPDATED NE: " + imagesJSON);
						Cookies.set('images', imagesJSON);
					}*/

                	notie.alert(1, 'Good choice!', 2);
                });
			}
		});
	};

	var errorCallback = function() {
		console.log("failure to get media");
	};

	var takePhoto = function () {
		ctx.drawImage(video, 0, 0, 320, 240);
		canvas.style.display = "block";
		showImage();
	};

	var showImage = function () {
		image.src = canvas.toDataURL('image/webp');
		image.style.display = "none";
	};

	var stop = function () {
		if (localMediaStream) {
			track.stop(); /* TODO: it doesn't work in Opera Mobile 12 */
		}

		captureButton.disabled = false;
		stopButton.disabled = true;
		snapshotButton.disabled = true;
	};

	if (navigator.getUserMedia) {
		navigator.getUserMedia(constraints, successCallback, errorCallback);
	} else {
		console.log("getUserMedia not supported");
	}
}

function getConfirmation(){
   var retVal = confirm("Do you want to use this photo for your Photo of the Day?");
   if( retVal == true ){
      //document.write ("User wants to use photo!");
      return true;
   }
   else{
      //document.write ("User does not want to use photo!");
      return false;
   }
}

function upload(file) {
  var form = new FormData(),
      xhr = new XMLHttpRequest();

  form.append('image', file);
  xhr.open('post', 'server.php', true);
  xhr.send(form);
}

function init() {
	var video = document.querySelector('video#getUserMedia');
	var canvas = document.querySelector('#canvas');
	var snapshot = document.querySelector('#snapshot');
	var captureButton = document.querySelector('#captureButton');
	var stopButton = document.querySelector('#stopButton');
	var snapshotButton = document.querySelector('#snapshotButton');

	captureButton.onclick = function () {
		capture(video, canvas, snapshot, captureButton, stopButton, snapshotButton);
	};
}<|endoftext|>


<commit_before>#!/usr/bin/env node
/*****************************************************************************\
 *                                                                           *
 *   .d8888b.          d8888 88888888888 888     888 8888888b.  888b    888  *
 *  d88P  Y88b        d88888     888     888     888 888   Y88b 8888b   888  *
 *  Y88b.            d88P888     888     888     888 888    888 88888b  888  *
 *   "Y888b.        d88P 888     888     888     888 888   d88P 888Y88b 888  *
 *      "Y88b.     d88P  888     888     888     888 8888888P"  888 Y88b888  *
 *        "888    d88P   888     888     888     888 888 T88b   888  Y88888  *
 *  Y88b  d88P   d8888888888     888     Y88b. .d88P 888  T88b  888   Y8888  *
 *   "Y8888P"   d88P     888     888      "Y88888P"  888   T88b 888    Y888  *
 *                                                                           *
 *                                   Saturn                                  *
 *     A general-purpose game engine for the Nintendo® Game Boy Advance™     *
 *                                                                           *
 *                     Copyright © 2016-2017 Nicholatian                     *
 *                                                                           *
 *  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.                            *
 *                                                                           *
\*****************************************************************************/

'use strict'

const chproc = require('child_process')
const path   = require('path')

const cfg    = require('./config')
const colour = require('./colour')
const conv   = require('./conv')

const cc = (file, debug) => {
    try {
        console.log('Compiling'.cyan + '       ' + file.grey + '...')
        const options = cfg.cFlags.concat([file, '-o', path.join(cfg.buildDir,
            'code', file.replace(/\//g, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o'))])
        chproc.execFileSync(cfg.toolchain.cCompiler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The C compiler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const cxx = (file, debug) => {
    try {
        console.log('Compiling'.blue + '       ' + file.grey + '...')
        const options = cfg.cxxFlags.concat([file, '-o', path.join(
            cfg.buildDir, 'code', file.replace(/\//g, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o'))])
        chproc.execFileSync(cfg.toolchain.cxxCompiler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The C++ compiler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const as = (file) => {
    try {
        console.log('Assembling'.green + '      ' + file.grey + '...')
        const options = cfg.sFlags.concat(['-o', path.join(cfg.buildDir,
            'code', file.replace(/\//g, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o')), file])
        chproc.execFileSync(cfg.toolchain.assembler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The assembler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const grit = (file, depth, tiled, algo, reduce) => {
    let flags = []
    if(algo === 'lz77') {
        flags.push('-Zl')
    } else if(algo === 'huff') {
        flags.push('-Zh')
    } else if(algo === 'rle') {
        flags.push('-Zr')
    } else if(algo === 'unc') {
        flags.push('-Z!')
    } else {
        console.error('Invalid compression algorithm passed to grit!'.red.bold)
        return
    }
    if(depth !== 1 && depth !== 2 && depth !== 4 && depth !== 8 &&
    depth !== 16) {
        console.error('Invalid bit depth passed to grit!'.red.bold)
        return
    }
    flags.push('-gB' + depth.toString())
    if(depth === 16 || !tiled) {
        flags.push('-gb')
    } else {
        flags.push('-gt')
    }
    if(reduce) {
        flags.push('-mRtf')
    } else {
        flags.push('-mR!')
    }
    flags.push('-o' + path.join(cfg.buildDir, 'data', 'image',
        file.replace(/.*\//g, '')) + '.s')
    try {
        console.log('Transmogrifying'.yellow + ' ' + file.grey + '...')
        let options = [file.replace(process.cwd() + '/', '')]
        options = options.concat(cfg.gritFlags, flags)
        chproc.execFileSync(cfg.toolchain.grit, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
        const opts2 = cfg.sFlags.concat(['-o', path.join(cfg.buildDir, 'data',
            'image', file.replace(/.*\//g, '')).replace(
            /\.png$/, '.o'), path.join(cfg.buildDir,
            'data', 'image', file.replace(/.*\//g, '')) + '.s'])
        chproc.execFileSync(cfg.toolchain.assembler, opts2, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('Grit returned a nonzero exit code!'.red.bold)
        console.error(err.message)
    }
};

process.on('message', (m) => {
    if(m.die === true) {
        process.exit(0)
    }
    if(m.type === 'c') {
        cc(m.file, m.debug)
        process.send({ done: true })
    } else if(m.type === 'cpp') {
        cxx(m.file, m.debug)
        process.send({ done: true })
    } else if(m.type === 'asm') {
        as(m.file)
        process.send({ done: true })
    } else if(m.type === 'image') {
        grit(m.file, m.depth, m.tiled, m.algo, m.reduce)
        process.send({ done: true })
    } else if(m.type === 'npal') {
        conv.palette(m.file)
        process.send({ done: true })
    } else {
        console.error('Unsupported input for worker node!'.red.bold)
        process.exit(1)
    }
})
<commit_msg>Fix path separator issues<commit_after>#!/usr/bin/env node
/*****************************************************************************\
 *                                                                           *
 *   .d8888b.          d8888 88888888888 888     888 8888888b.  888b    888  *
 *  d88P  Y88b        d88888     888     888     888 888   Y88b 8888b   888  *
 *  Y88b.            d88P888     888     888     888 888    888 88888b  888  *
 *   "Y888b.        d88P 888     888     888     888 888   d88P 888Y88b 888  *
 *      "Y88b.     d88P  888     888     888     888 8888888P"  888 Y88b888  *
 *        "888    d88P   888     888     888     888 888 T88b   888  Y88888  *
 *  Y88b  d88P   d8888888888     888     Y88b. .d88P 888  T88b  888   Y8888  *
 *   "Y8888P"   d88P     888     888      "Y88888P"  888   T88b 888    Y888  *
 *                                                                           *
 *                                   Saturn                                  *
 *     A general-purpose game engine for the Nintendo® Game Boy Advance™     *
 *                                                                           *
 *                     Copyright © 2016-2017 Nicholatian                     *
 *                                                                           *
 *  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.                            *
 *                                                                           *
\*****************************************************************************/

'use strict'

const chproc = require('child_process')
const path   = require('path')

const cfg    = require('./config')
const colour = require('./colour')
const conv   = require('./conv')

const cc = (file, debug) => {
    try {
        console.log('Compiling'.cyan + '       ' + file.grey + '...')
        if(process.platform === 'win32') {
            var pathRegexp = /\\/g
        } else {
            var pathRegexp = /\//g
        }
        const options = cfg.cFlags.concat([file, '-o', path.join(cfg.buildDir,
            'code', file.replace(pathRegexp, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o'))])
        chproc.execFileSync(cfg.toolchain.cCompiler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The C compiler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const cxx = (file, debug) => {
    try {
        console.log('Compiling'.blue + '       ' + file.grey + '...')
        if(process.platform === 'win32') {
            var pathRegexp = /\\/g
        } else {
            var pathRegexp = /\//g
        }
        const options = cfg.cxxFlags.concat([file, '-o', path.join(
            cfg.buildDir, 'code', file.replace(pathRegexp, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o'))])
        chproc.execFileSync(cfg.toolchain.cxxCompiler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The C++ compiler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const as = (file) => {
    try {
        console.log('Assembling'.green + '      ' + file.grey + '...')
        if(process.platform === 'win32') {
            var pathRegexp = /\\/g
        } else {
            var pathRegexp = /\//g
        }
        const options = cfg.sFlags.concat(['-o', path.join(cfg.buildDir,
            'code', file.replace(pathRegexp, '+').replace(
            /\.(asm|c|cc|cpp|cxx|c\+\+|s)$/, '.o')), file])
        chproc.execFileSync(cfg.toolchain.assembler, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('The assembler exited with a nonzero code!'.red.bold)
        console.error(err.message)
    }
};

const grit = (file, depth, tiled, algo, reduce) => {
    let flags = []
    if(algo === 'lz77') {
        flags.push('-Zl')
    } else if(algo === 'huff') {
        flags.push('-Zh')
    } else if(algo === 'rle') {
        flags.push('-Zr')
    } else if(algo === 'unc') {
        flags.push('-Z!')
    } else {
        console.error('Invalid compression algorithm passed to grit!'.red.bold)
        return
    }
    if(depth !== 1 && depth !== 2 && depth !== 4 && depth !== 8 &&
    depth !== 16) {
        console.error('Invalid bit depth passed to grit!'.red.bold)
        return
    }
    flags.push('-gB' + depth.toString())
    if(depth === 16 || !tiled) {
        flags.push('-gb')
    } else {
        flags.push('-gt')
    }
    if(reduce) {
        flags.push('-mRtf')
    } else {
        flags.push('-mR!')
    }
    if(process.platform === 'win32') {
        var pathRegexp = /.*\\/g
    } else {
        var pathRegexp = /.*\//g
    }
    flags.push('-o' + path.join(cfg.buildDir, 'data', 'image',
        file.replace(pathRegexp, '')) + '.s')
    try {
        console.log('Transmogrifying'.yellow + ' ' + file.grey + '...')
        let options = [file.replace(process.cwd() + path.sep, '')]
        options = options.concat(cfg.gritFlags, flags)
        chproc.execFileSync(cfg.toolchain.grit, options, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
        const opts2 = cfg.sFlags.concat(['-o', path.join(cfg.buildDir, 'data',
            'image', file.replace(pathRegexp, '')).replace(
            /\.png$/, '.o'), path.join(cfg.buildDir,
            'data', 'image', file.replace(pathRegexp, '')) + '.s'])
        chproc.execFileSync(cfg.toolchain.assembler, opts2, {
            cwd: process.cwd(),
            encoding: 'utf8'
        })
    } catch(err) {
        console.error('Grit returned a nonzero exit code!'.red.bold)
        console.error(err.message)
    }
};

process.on('message', (m) => {
    if(m.die === true) {
        process.exit(0)
    }
    if(m.type === 'c') {
        cc(m.file, m.debug)
        process.send({ done: true })
    } else if(m.type === 'cpp') {
        cxx(m.file, m.debug)
        process.send({ done: true })
    } else if(m.type === 'asm') {
        as(m.file)
        process.send({ done: true })
    } else if(m.type === 'image') {
        grit(m.file, m.depth, m.tiled, m.algo, m.reduce)
        process.send({ done: true })
    } else if(m.type === 'npal') {
        conv.palette(m.file)
        process.send({ done: true })
    } else {
        console.error('Unsupported input for worker node!'.red.bold)
        process.exit(1)
    }
})
<|endoftext|>


<commit_before>import FileSystemUtilities from "../FileSystemUtilities";
import Command from "../Command";
import objectAssignSorted from "object-assign-sorted";
import objectAssign from "object-assign";

export default class InitCommand extends Command {
  // don't do any of this.
  runValidations() {}
  runPreparations() {}

  initialize(callback) {
    // Nothing to do...
    callback(null, true);
  }

  execute(callback) {
    this.ensurePackagesDirectory();
    this.ensurePackageJSON();
    this.ensureLernaJson();
    this.ensureNoVersionFile();
    this.logger.success("Successfully created Lerna files");
    callback(null, true);
  }

  ensurePackagesDirectory() {
    const packagesLocation = this.repository.packagesLocation;
    if (!FileSystemUtilities.existsSync(packagesLocation)) {
      this.logger.info("Creating packages folder.");
      FileSystemUtilities.mkdirSync(packagesLocation);
    }
  }

  ensurePackageJSON() {
    let {packageJsonLocation, packageJson} = this.repository;

    if (!packageJson) packageJson = {};
    // if (!packageJson.private) packageJson.private = true;
    if (!packageJson.devDependencies) packageJson.devDependencies = {};

    objectAssignSorted(packageJson.devDependencies, {
      lerna: this.lernaVersion
    });

    if (!packageJson) {
      this.logger.info("Creating package.json.");
    } else {
      this.logger.info("Updating package.json.");
    }

    FileSystemUtilities.writeFileSync(packageJsonLocation, JSON.stringify(packageJson, null, "  "));
  }

  ensureLernaJson() {
    let {versionLocation, lernaJsonLocation, lernaJson} = this.repository;

    let version;

    if (this.flags.independent) {
      version = "independent";
    } else if (FileSystemUtilities.existsSync(versionLocation)) {
      version = FileSystemUtilities.readFileSync(versionLocation);
    } else if (lernaJson && lernaJson.version) {
      version = lernaJson.version;
    } else {
      version = "0.0.0";
    }

    if (!lernaJson) {
      this.logger.info("Creating lerna.json.");
      lernaJson = {};
    } else {
      this.logger.info("Updating lerna.json.");
    }

    objectAssign(lernaJson, {
      lerna: this.lernaVersion,
      version: version
    });

    FileSystemUtilities.writeFileSync(lernaJsonLocation, JSON.stringify(lernaJson, null, "  "));
  }

  ensureNoVersionFile() {
    const versionLocation = this.repository.versionLocation;
    if (FileSystemUtilities.existsSync(versionLocation)) {
      this.logger.info("Removing old VERSION file.");
      FileSystemUtilities.unlinkSync(versionLocation);
    }
  }
}
<commit_msg>fix: lerna init assumes lerna in devDependencies (#370)<commit_after>import FileSystemUtilities from "../FileSystemUtilities";
import Command from "../Command";
import objectAssignSorted from "object-assign-sorted";
import objectAssign from "object-assign";

export default class InitCommand extends Command {
  // don't do any of this.
  runValidations() {}
  runPreparations() {}

  initialize(callback) {
    // Nothing to do...
    callback(null, true);
  }

  execute(callback) {
    this.ensurePackagesDirectory();
    this.ensurePackageJSON();
    this.ensureLernaJson();
    this.ensureNoVersionFile();
    this.logger.success("Successfully created Lerna files");
    callback(null, true);
  }

  ensurePackagesDirectory() {
    const packagesLocation = this.repository.packagesLocation;
    if (!FileSystemUtilities.existsSync(packagesLocation)) {
      this.logger.info("Creating packages folder.");
      FileSystemUtilities.mkdirSync(packagesLocation);
    }
  }

  ensurePackageJSON() {
    let {packageJsonLocation, packageJson} = this.repository;

    if (!packageJson) {
      packageJson = {};
      this.logger.info("Creating package.json.");
    } else {
      this.logger.info("Updating package.json.");
    }
    // if (!packageJson.private) packageJson.private = true;

    let targetDependencies;
    if (packageJson.dependencies && packageJson.dependencies.lerna) {
      // lerna is a dependency in the current project
      targetDependencies = packageJson.dependencies;
    } else {
      // lerna is a devDependency or no dependency, yet
      if (!packageJson.devDependencies) packageJson.devDependencies = {};
      targetDependencies = packageJson.devDependencies;
    }

    objectAssignSorted(targetDependencies, {
      lerna: this.lernaVersion
    });

    FileSystemUtilities.writeFileSync(packageJsonLocation, JSON.stringify(packageJson, null, "  "));
  }

  ensureLernaJson() {
    let {versionLocation, lernaJsonLocation, lernaJson} = this.repository;

    let version;

    if (this.flags.independent) {
      version = "independent";
    } else if (FileSystemUtilities.existsSync(versionLocation)) {
      version = FileSystemUtilities.readFileSync(versionLocation);
    } else if (lernaJson && lernaJson.version) {
      version = lernaJson.version;
    } else {
      version = "0.0.0";
    }

    if (!lernaJson) {
      this.logger.info("Creating lerna.json.");
      lernaJson = {};
    } else {
      this.logger.info("Updating lerna.json.");
    }

    objectAssign(lernaJson, {
      lerna: this.lernaVersion,
      version: version
    });

    FileSystemUtilities.writeFileSync(lernaJsonLocation, JSON.stringify(lernaJson, null, "  "));
  }

  ensureNoVersionFile() {
    const versionLocation = this.repository.versionLocation;
    if (FileSystemUtilities.existsSync(versionLocation)) {
      this.logger.info("Removing old VERSION file.");
      FileSystemUtilities.unlinkSync(versionLocation);
    }
  }
}
<|endoftext|>


<commit_before>
/*
A set of rendering and date-related methods for a visual component comprised of one or more rows of day columns.
Prerequisite: the object being mixed into needs to be a *Grid*
*/
var DayTableMixin = FC.DayTableMixin = {

	breakOnWeeks: false, // should create a new row for each week?
	dayDates: null, // whole-day dates for each column. left to right
	dayIndices: null, // for each day from start, the offset
	daysPerRow: null,
	rowCnt: null,
	colCnt: null,
	colHeadFormat: null,

	// Populates internal variables used for date calculation and rendering
	updateDayTable: function() {
		var view = this.view;
		var date = this.start.clone();
		var dayIndex = -1;
		var dayIndices = [];
		var dayDates = [];
		var daysPerRow;
		var firstDay;
		var rowCnt;

		while (date.isBefore(this.end)) { // loop each day from start to end
			if (view.isHiddenDay(date)) {
				dayIndices.push(dayIndex + 0.5); // mark that it's between indices
			}
			else {
				dayIndex++;
				dayIndices.push(dayIndex);
				dayDates.push(date.clone());
			}
			date.add(1, 'days');
		}

		if (this.breakOnWeeks) {
			// count columns until the day-of-week repeats
			firstDay = dayDates[0].day();
			for (daysPerRow = 1; daysPerRow < dayDates.length; daysPerRow++) {
				if (dayDates[daysPerRow].day() == firstDay) {
					break;
				}
			}
			rowCnt = Math.ceil(dayDates.length / daysPerRow);
		}
		else {
			rowCnt = 1;
			daysPerRow = dayDates.length;
		}

		this.dayDates = dayDates;
		this.dayIndices = dayIndices;
		this.daysPerRow = daysPerRow;
		this.rowCnt = rowCnt;

		this.updateDayTableCols();
	},

	// Computes and assigned the colCnt property and updates any options that may be computed from it
	updateDayTableCols: function() {
		this.colCnt = this.computeColCnt();
		this.colHeadFormat = this.view.opt('columnFormat') || this.computeColHeadFormat();
	},

	// Determines how many columns there should be in the table
	computeColCnt: function() {
		return this.daysPerRow;
	},

	// Computes the ambiguously-timed moment for the given cell
	getCellDate: function(row, col) {
		return this.dayDates[
				this.getCellDayIndex(row, col)
			].clone();
	},

	// Computes the ambiguously-timed date range for the given cell
	getCellRange: function(row, col) {
		var start = this.getCellDate(row, col);
		var end = start.clone().add(1, 'days');

		return { start: start, end: end };
	},

	// Returns the number of day cells, chronologically, from the first of the grid (0-based)
	getCellDayIndex: function(row, col) {
		return row * this.daysPerRow + this.getColDayIndex(col);
	},

	// Returns the numner of day cells, chronologically, from the first cell in *any given row*
	getColDayIndex: function(col) {
		if (this.isRTL) {
			return this.colCnt - 1 - col;
		}
		else {
			return col;
		}
	},

	// Given a date, returns its chronolocial cell-index from the first cell of the grid.
	// If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
	// If before the first offset, returns a negative number.
	// If after the last offset, returns an offset past the last cell offset.
	// Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
	getDateDayIndex: function(date) {
		var dayIndices = this.dayIndices;
		var dayOffset = date.diff(this.start, 'days');

		if (dayOffset < 0) {
			return dayIndices[0] - 1;
		}
		else if (dayOffset >= dayIndices.length) {
			return dayIndices[dayIndices.length - 1] + 1;
		}
		else {
			return dayIndices[dayOffset];
		}
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default column header formatting string if `colFormat` is not explicitly defined
	computeColHeadFormat: function() {
		// if more than one week row, or if there are a lot of columns with not much space,
		// put just the day numbers will be in each cell
		if (this.rowCnt > 1 || this.colCnt > 10) {
			return 'ddd'; // "Sat"
		}
		// multiple days, so full single date string WON'T be in title text
		else if (this.colCnt > 1) {
			return this.view.opt('dayOfMonthFormat'); // "Sat 12/10"
		}
		// single day, so full single date string will probably be in title text
		else {
			return 'dddd'; // "Saturday"
		}
	},

	/* Slicing
	------------------------------------------------------------------------------------------------------------------*/

	// Slices up a date range into a segment for every week-row it intersects with
	sliceRangeByRow: function(range) {
		var daysPerRow = this.daysPerRow;
		var normalRange = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		var rangeFirst = this.getDateDayIndex(normalRange.start); // inclusive first index
		var rangeLast = this.getDateDayIndex(normalRange.end.clone().subtract(1, 'days')); // inclusive last index
		var segs = [];
		var row;
		var rowFirst, rowLast; // inclusive day-index range for current row
		var segFirst, segLast; // inclusive day-index range for segment

		for (row = 0; row < this.rowCnt; row++) {
			rowFirst = row * daysPerRow;
			rowLast = rowFirst + daysPerRow - 1;

			// intersect segment's offset range with the row's
			segFirst = Math.max(rangeFirst, rowFirst);
			segLast = Math.min(rangeLast, rowLast);

			// deal with in-between indices
			segFirst = Math.ceil(segFirst); // in-between starts round to next cell
			segLast = Math.floor(segLast); // in-between ends round to prev cell

			if (segFirst <= segLast) { // was there any intersection with the current row?
				segs.push({
					row: row,

					// normalize to start of row
					firstRowDayIndex: segFirst - rowFirst,
					lastRowDayIndex: segLast - rowFirst,

					// must be matching integers to be the segment's start/end
					isStart: segFirst === rangeFirst,
					isEnd: segLast === rangeLast
				});
			}
		}

		return segs;
	},

	// Slices up a date range into a segment for every day-cell it intersects with.
	// TODO: make more DRY with sliceRangeByRow somehow.
	sliceRangeByDay: function(range) {
		var daysPerRow = this.daysPerRow;
		var normalRange = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		var rangeFirst = this.getDateDayIndex(normalRange.start); // inclusive first index
		var rangeLast = this.getDateDayIndex(normalRange.end.clone().subtract(1, 'days')); // inclusive last index
		var segs = [];
		var row;
		var rowFirst, rowLast; // inclusive day-index range for current row
		var i;
		var segFirst, segLast; // inclusive day-index range for segment

		for (row = 0; row < this.rowCnt; row++) {
			rowFirst = row * daysPerRow;
			rowLast = rowFirst + daysPerRow - 1;

			for (i = rowFirst; i <= rowLast; i++) {

				// intersect segment's offset range with the row's
				segFirst = Math.max(rangeFirst, i);
				segLast = Math.min(rangeLast, i);

				// deal with in-between indices
				segFirst = Math.ceil(segFirst); // in-between starts round to next cell
				segLast = Math.floor(segLast); // in-between ends round to prev cell

				if (segFirst <= segLast) { // was there any intersection with the current row?
					segs.push({
						row: row,

						// normalize to start of row
						firstRowDayIndex: segFirst - rowFirst,
						lastRowDayIndex: segLast - rowFirst,

						// must be matching integers to be the segment's start/end
						isStart: segFirst === rangeFirst,
						isEnd: segLast === rangeLast
					});
				}
			}
		}

		return segs;
	},

	/* Header Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderHeadHtml: function() {
		var view = this.view;

		return '' +
			'
' +
				'
' +
					'
' +
						this.renderHeadTrHtml() +
					'
' +
				'
' +
			'
';
	},

	renderHeadIntroHtml: function() {
		return this.renderIntroHtml(); // fall back to generic
	},

	renderHeadTrHtml: function() {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderHeadIntroHtml()) +
				this.renderHeadDateCellsHtml() +
				(this.isRTL ? this.renderHeadIntroHtml() : '') +
			'
';
	},

	renderHeadDateCellsHtml: function() {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(0, col);
			htmls.push(this.renderHeadDateCellHtml(date));
		}

		return htmls.join('');
	},

	// TODO: when internalApiVersion, accept an object for HTML attributes
	// (colspan should be no different)
	renderHeadDateCellHtml: function(date, colspan, otherAttrs) {
		var view = this.view;

		return '' +
			' 1 ?
					' colspan="' + colspan + '"' :
					'') +
				(otherAttrs ?
					' ' + otherAttrs :
					'') +
			'>' +
				htmlEscape(date.format(this.colHeadFormat)) +
			'	';
	},

	/* Background Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderBgTrHtml: function(row) {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderBgIntroHtml(row)) +
				this.renderBgCellsHtml(row) +
				(this.isRTL ? this.renderBgIntroHtml(row) : '') +
			'
';
	},

	renderBgIntroHtml: function(row) {
		return this.renderIntroHtml(); // fall back to generic
	},

	renderBgCellsHtml: function(row) {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(row, col);
			htmls.push(this.renderBgCellHtml(date));
		}

		return htmls.join('');
	},

	renderBgCellHtml: function(date) {
		var view = this.view;
		var classes = this.getDayClasses(date);

		classes.unshift('fc-day', view.widgetContentClass);

		return '	';
	},

	/* Generic
	------------------------------------------------------------------------------------------------------------------*/

	// Generates the default HTML intro for any row. User classes should override
	renderIntroHtml: function() {
	},

	/* Utils
	------------------------------------------------------------------------------------------------------------------*/

	// Applies the generic "intro" and "outro" HTML to the given cells.
	// Intro means the leftmost cell when the calendar is LTR and the rightmost cell when RTL. Vice-versa for outro.
	bookendCells: function(trEl) {
		var introHtml = this.renderIntroHtml();

		if (introHtml) {
			if (this.isRTL) {
				trEl.append(introHtml);
			}
			else {
				trEl.prepend(introHtml);
			}
		}
	}

};
<commit_msg>add a note to self<commit_after>
/*
A set of rendering and date-related methods for a visual component comprised of one or more rows of day columns.
Prerequisite: the object being mixed into needs to be a *Grid*
*/
var DayTableMixin = FC.DayTableMixin = {

	breakOnWeeks: false, // should create a new row for each week?
	dayDates: null, // whole-day dates for each column. left to right
	dayIndices: null, // for each day from start, the offset
	daysPerRow: null,
	rowCnt: null,
	colCnt: null,
	colHeadFormat: null,

	// Populates internal variables used for date calculation and rendering
	updateDayTable: function() {
		var view = this.view;
		var date = this.start.clone();
		var dayIndex = -1;
		var dayIndices = [];
		var dayDates = [];
		var daysPerRow;
		var firstDay;
		var rowCnt;

		while (date.isBefore(this.end)) { // loop each day from start to end
			if (view.isHiddenDay(date)) {
				dayIndices.push(dayIndex + 0.5); // mark that it's between indices
			}
			else {
				dayIndex++;
				dayIndices.push(dayIndex);
				dayDates.push(date.clone());
			}
			date.add(1, 'days');
		}

		if (this.breakOnWeeks) {
			// count columns until the day-of-week repeats
			firstDay = dayDates[0].day();
			for (daysPerRow = 1; daysPerRow < dayDates.length; daysPerRow++) {
				if (dayDates[daysPerRow].day() == firstDay) {
					break;
				}
			}
			rowCnt = Math.ceil(dayDates.length / daysPerRow);
		}
		else {
			rowCnt = 1;
			daysPerRow = dayDates.length;
		}

		this.dayDates = dayDates;
		this.dayIndices = dayIndices;
		this.daysPerRow = daysPerRow;
		this.rowCnt = rowCnt;

		this.updateDayTableCols();
	},

	// Computes and assigned the colCnt property and updates any options that may be computed from it
	updateDayTableCols: function() {
		this.colCnt = this.computeColCnt();
		this.colHeadFormat = this.view.opt('columnFormat') || this.computeColHeadFormat();
	},

	// Determines how many columns there should be in the table
	computeColCnt: function() {
		return this.daysPerRow;
	},

	// Computes the ambiguously-timed moment for the given cell
	getCellDate: function(row, col) {
		return this.dayDates[
				this.getCellDayIndex(row, col)
			].clone();
	},

	// Computes the ambiguously-timed date range for the given cell
	getCellRange: function(row, col) {
		var start = this.getCellDate(row, col);
		var end = start.clone().add(1, 'days');

		return { start: start, end: end };
	},

	// Returns the number of day cells, chronologically, from the first of the grid (0-based)
	getCellDayIndex: function(row, col) {
		return row * this.daysPerRow + this.getColDayIndex(col);
	},

	// Returns the numner of day cells, chronologically, from the first cell in *any given row*
	getColDayIndex: function(col) {
		if (this.isRTL) {
			return this.colCnt - 1 - col;
		}
		else {
			return col;
		}
	},

	// Given a date, returns its chronolocial cell-index from the first cell of the grid.
	// If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
	// If before the first offset, returns a negative number.
	// If after the last offset, returns an offset past the last cell offset.
	// Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
	getDateDayIndex: function(date) {
		var dayIndices = this.dayIndices;
		var dayOffset = date.diff(this.start, 'days');

		if (dayOffset < 0) {
			return dayIndices[0] - 1;
		}
		else if (dayOffset >= dayIndices.length) {
			return dayIndices[dayIndices.length - 1] + 1;
		}
		else {
			return dayIndices[dayOffset];
		}
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default column header formatting string if `colFormat` is not explicitly defined
	computeColHeadFormat: function() {
		// if more than one week row, or if there are a lot of columns with not much space,
		// put just the day numbers will be in each cell
		if (this.rowCnt > 1 || this.colCnt > 10) {
			return 'ddd'; // "Sat"
		}
		// multiple days, so full single date string WON'T be in title text
		else if (this.colCnt > 1) {
			return this.view.opt('dayOfMonthFormat'); // "Sat 12/10"
		}
		// single day, so full single date string will probably be in title text
		else {
			return 'dddd'; // "Saturday"
		}
	},

	/* Slicing
	------------------------------------------------------------------------------------------------------------------*/

	// Slices up a date range into a segment for every week-row it intersects with
	sliceRangeByRow: function(range) {
		var daysPerRow = this.daysPerRow;
		var normalRange = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		var rangeFirst = this.getDateDayIndex(normalRange.start); // inclusive first index
		var rangeLast = this.getDateDayIndex(normalRange.end.clone().subtract(1, 'days')); // inclusive last index
		var segs = [];
		var row;
		var rowFirst, rowLast; // inclusive day-index range for current row
		var segFirst, segLast; // inclusive day-index range for segment

		for (row = 0; row < this.rowCnt; row++) {
			rowFirst = row * daysPerRow;
			rowLast = rowFirst + daysPerRow - 1;

			// intersect segment's offset range with the row's
			segFirst = Math.max(rangeFirst, rowFirst);
			segLast = Math.min(rangeLast, rowLast);

			// deal with in-between indices
			segFirst = Math.ceil(segFirst); // in-between starts round to next cell
			segLast = Math.floor(segLast); // in-between ends round to prev cell

			if (segFirst <= segLast) { // was there any intersection with the current row?
				segs.push({
					row: row,

					// normalize to start of row
					firstRowDayIndex: segFirst - rowFirst,
					lastRowDayIndex: segLast - rowFirst,

					// must be matching integers to be the segment's start/end
					isStart: segFirst === rangeFirst,
					isEnd: segLast === rangeLast
				});
			}
		}

		return segs;
	},

	// Slices up a date range into a segment for every day-cell it intersects with.
	// TODO: make more DRY with sliceRangeByRow somehow.
	sliceRangeByDay: function(range) {
		var daysPerRow = this.daysPerRow;
		var normalRange = this.view.computeDayRange(range); // make whole-day range, considering nextDayThreshold
		var rangeFirst = this.getDateDayIndex(normalRange.start); // inclusive first index
		var rangeLast = this.getDateDayIndex(normalRange.end.clone().subtract(1, 'days')); // inclusive last index
		var segs = [];
		var row;
		var rowFirst, rowLast; // inclusive day-index range for current row
		var i;
		var segFirst, segLast; // inclusive day-index range for segment

		for (row = 0; row < this.rowCnt; row++) {
			rowFirst = row * daysPerRow;
			rowLast = rowFirst + daysPerRow - 1;

			for (i = rowFirst; i <= rowLast; i++) {

				// intersect segment's offset range with the row's
				segFirst = Math.max(rangeFirst, i);
				segLast = Math.min(rangeLast, i);

				// deal with in-between indices
				segFirst = Math.ceil(segFirst); // in-between starts round to next cell
				segLast = Math.floor(segLast); // in-between ends round to prev cell

				if (segFirst <= segLast) { // was there any intersection with the current row?
					segs.push({
						row: row,

						// normalize to start of row
						firstRowDayIndex: segFirst - rowFirst,
						lastRowDayIndex: segLast - rowFirst,

						// must be matching integers to be the segment's start/end
						isStart: segFirst === rangeFirst,
						isEnd: segLast === rangeLast
					});
				}
			}
		}

		return segs;
	},

	/* Header Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderHeadHtml: function() {
		var view = this.view;

		return '' +
			'
' +
				'
' +
					'
' +
						this.renderHeadTrHtml() +
					'
' +
				'
' +
			'
';
	},

	renderHeadIntroHtml: function() {
		return this.renderIntroHtml(); // fall back to generic
	},

	renderHeadTrHtml: function() {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderHeadIntroHtml()) +
				this.renderHeadDateCellsHtml() +
				(this.isRTL ? this.renderHeadIntroHtml() : '') +
			'
';
	},

	renderHeadDateCellsHtml: function() {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(0, col);
			htmls.push(this.renderHeadDateCellHtml(date));
		}

		return htmls.join('');
	},

	// TODO: when internalApiVersion, accept an object for HTML attributes
	// (colspan should be no different)
	renderHeadDateCellHtml: function(date, colspan, otherAttrs) {
		var view = this.view;

		return '' +
			' 1 ?
					' colspan="' + colspan + '"' :
					'') +
				(otherAttrs ?
					' ' + otherAttrs :
					'') +
			'>' +
				htmlEscape(date.format(this.colHeadFormat)) +
			'	';
	},

	/* Background Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderBgTrHtml: function(row) {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderBgIntroHtml(row)) +
				this.renderBgCellsHtml(row) +
				(this.isRTL ? this.renderBgIntroHtml(row) : '') +
			'
';
	},

	renderBgIntroHtml: function(row) {
		return this.renderIntroHtml(); // fall back to generic
	},

	renderBgCellsHtml: function(row) {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(row, col);
			htmls.push(this.renderBgCellHtml(date));
		}

		return htmls.join('');
	},

	renderBgCellHtml: function(date) {
		var view = this.view;
		var classes = this.getDayClasses(date);

		classes.unshift('fc-day', view.widgetContentClass);

		return '	';
	},

	/* Generic
	------------------------------------------------------------------------------------------------------------------*/

	// Generates the default HTML intro for any row. User classes should override
	renderIntroHtml: function() {
	},

	// TODO: a generic method for dealing with
, RTL, intro
	// when increment internalApiVersion
	// wrapTr (scheduler)

	/* Utils
	------------------------------------------------------------------------------------------------------------------*/

	// Applies the generic "intro" and "outro" HTML to the given cells.
	// Intro means the leftmost cell when the calendar is LTR and the rightmost cell when RTL. Vice-versa for outro.
	bookendCells: function(trEl) {
		var introHtml = this.renderIntroHtml();

		if (introHtml) {
			if (this.isRTL) {
				trEl.append(introHtml);
			}
			else {
				trEl.prepend(introHtml);
			}
		}
	}

};
<|endoftext|>


<commit_before>
/************************************
         Mapping Logic
 ************************************/

var Visualizer = {};

/************************************
         Global Variables
 ************************************/

// Global scale factor. Helps to max points (ranging
// from -10,000 to 10,000) to their coordinate points
// on a geo projection.
Visualizer.scaleFactor = 200;

// Feature Group representation of data
Visualizer.layers = [];

// Target API url
Visualizer.API_url = (window.location.href.indexOf("herokuapp.com") > -1) ? "http://g-player.herokuapp.com/api/" : "http://localhost:5000/api/";

// Take data from settings.data.
// Add to the map.
// Draw lines on the map
Visualizer.update = function(){

  // Group data by PlayerID
  var players = _.groupBy(settings.data, 'playerID');

  // Store current index
  var count = 0;

  // Iterate through players
  _.each(players, function(player){

      // Render each player onto the map
      Visualizer.draw(player, count++);
  });

  // Loading complete
  UI.loading(false, "Success. " + settings.data.length + " points loaded.");
}

// Draw a player onto the map. Positions are rendered
// as lines, while actions are clickable points.
Visualizer.draw = function(entries, index){

    // Get selected color.
    var color = Visualizer.getColor(index);

    // Ensure chronological order
    entries = sortBy(entries, "timestamp");

    /********************************
             POSITIONS
     ********************************/

    // get list of positions
    var positions = _.filter(entries, function(d){

      // Do we not have an action key?
      return !d.action;
    });

    // Create list of latLng ojects
    var positions = _.map(positions, function(point){

      // Return formatted latLng point
      return toLatLng(point);
    });

    var options = {
      stroke: true,
      color: color,
      weight: 2,
      opacity: 1,
    }

    // Create polyline
    var polyline = L.polyline(positions, options)

    var featureGroup = new L.FeatureGroup().addLayer(polyline);

    if (settings.paths) addFeatureGroup(featureGroup);

    /********************************
             ACTIONS
     ********************************/

    // Get list of actions
    var actions = _.filter(entries, function(d){

      // Do we have an action key?
      return (d.action) ? true : false;
    });

    var markers = new L.FeatureGroup();

    _.each(actions, function(p){

      var latLng = [p.latitude, p.longitude]

      var circle = L.circleMarker(latLng, {
        stroke: true,
        weight: 4,
        opacity: 1,
        color: color,
        fill: true,
        fillColor: "#fff",
        fillOpacity: 1,
        radius: 5,
      })

      // Create a new popup object
      var popup = L.popup()
        .setLatLng(latLng)
        .setContent(convertJSONtoHTML(p))

      // Add popup to the circle
      circle.bindPopup(popup);

      // Provide hover of JUST the action
      // circle.on('mouseover', function(e) {

      //   // $("#legend").html(p.action);
      //   console.log(p.action)

      // });

      circle.bindLabel(p.action, { className : "tooltip-hover" })

      markers.addLayer(circle);

    });

    addFeatureGroup(markers);

}

// Clears the active data set. Resets map
Visualizer.clear = function(){

    // Clear active data sets
    _.each(Visualizer.layers, function(layer){

        // Remove each active layer
        map.removeLayer(layer);
    })
}

// Adds a marker at the provided location
Visualizer.addMarker = function(lat, long, title){

  // Optional Title
  var title = (title) ? title : "";

  // Place marker on the map
  L.marker([lat, long], {title : title}).addTo(map);

}

// Get Data from API
Visualizer.loadData = function(playerIDs){

  UI.loading(true, "Loading Data....");

  var opts = Visualizer.getContext();

  if (playerIDs) opts.playerIDs = playerIDs;

  // Hit API
  $.get(Visualizer.API_url + "entries", opts, function(data){

    // Validate data. Ignore non-spacial data
    data = _.filter(data, function(p){
      return containsRequiredKeys(p);
    })

    // Convert data points into plottable data
    data = _.map(data, function(p){
      return Visualizer.formatData(p);
    })

    // Save data for future reference
    settings.data = data;

    // Update our map with new data.
    Visualizer.update();

  })
};

// Convert the (x,y) values from the player logs
// into Latitude and Longitude positions on a map.
Visualizer.formatData = function(data){

  // Global scale factor. Helps to max points (ranging
  // from -10,000 to 10,000) to their coordinate points
  // on a geo projection.

  var scale = Visualizer.scaleFactor;

  // Create a latitude & longitude field.
  // Maps the (x,y) position to a coordinate
  // on the earth. Makes plotting MUCH easier

  data['latitude']  = data.posY / scale;
  data['longitude'] = data.posX / scale;

  return data;
}

// Convert a lat/long to it's raw X/Y values
Visualizer.unformatData = function(latLong){

  var scale = Visualizer.scaleFactor;

  return {
    posX : latLong['longitude'] * scale,
    posY : latLong['latitude'] * scale,
  }
}

Visualizer.getColor = function(i){

  var colors = ["#d73027", "#f46d43", "#fdae61",
                "#fee090", "#ffffbf", "#e0f3f8",
                "#abd9e9", "#74add1", "#4575b4"];

  return (i < colors.length - 1) ? colors[i] : "#000000";
}

// Returns a representation of the current state of the
// map. This object provided context for what data
// the API should return in order to be mapped.
Visualizer.getContext = function(callback){

  var obj = {
    game : settings.game,
    area : settings.map.name,
    fidelity : 1,
    playerIDs : options.players,
    actions : settings.actions,

  }

  if (obj.actions.length < 1) obj.actions = ["Quest"];

  console.log("\nCurrent Settings of the Map:");
  console.log(obj);

  return obj;
}

// Update the map's view port, as to
// center the current data set.
Visualizer.focus = function(){

    var sample = Visualizer.layers[0]

    if (sample) map.fitBounds(sample.getBounds());

    // TODO: Set max/min zoom levels dynamically
    // map.minZoom()
    // map.maxZoom()
}

/**************************************
         HELPER FUNCTIONS
     ... these should *never* be
   called from the User Interface.
 **************************************/

// Sort the provided list by the provided key
function sortBy (list, key){
  return _.sortBy(list, function(l){
        return l[key];
      })
}

// Convert the provided JSON object into an
// HTML representation
function convertJSONtoHTML(JSON){
  var acc = "";

  _.each(JSON, function(val, key){

    // Lets ignore a few keys
    // These keys are automatically generated
    // by the system, so the end user
    // won't miss them.

    if (key != "__v" &&
        key != "latitude" &&
        key != "longitude"){

      acc += "
" + key + " : " + val + "
";
    }

  })

  return acc;
}

// Does the provided object contain the required keys?
function containsRequiredKeys(obj){

  // Required keys
  var keys = ["playerID", "area", "posX", "posY", "timestamp"];

  var acc = true;

  _.each(keys, function(key){
    var containsKey = (obj && obj[key]) ? true : false;
    acc = acc && containsKey;
  })

  return acc;
}

// Adds the provided featureGroup to the map.
// Feature groups are objects representing
// "bulked", or otherwise grouped vector
// objects as a single layer on the map.
function addFeatureGroup (featureGroup){

    // Save layer to settings.
    // We must save a reference to each layer,
    // as it's the most effective way to iterate
    // through and remove layers.

    Visualizer.layers.push(featureGroup);

    // Save layer for reference
    map.addLayer(featureGroup);
}

// Convert the JSON Object to a leaflet LatLong object
function toLatLng (point){

  // Extract keys. May be 'latitude' or 'lat'.
  var lat  = (point.latitude)  ? point.latitude  : point.lat;

  // Extract keys. May be 'longitude' or 'long'.
  var long = (point.longitude) ? point.longitude : point.long;

  // Return lat/long if they exist.
  if (lat && long) { return L.latLng(lat, long); }

};

// Return the key mapping given the
// game name, and the event name
// ex: getKeyMapping("Fallout New Vegas", "Attacked")
var getKeyMapping = function(game, eventName){

  // Get the type of event from the lookup table
  var type = options.lookup_table[eventName];

  // Find mapping
  var mapping = _.findWhere(options.mappings, {game : settings.game, type : type});

  if (!mapping || !type) {
    console.error("Unable to find key mapping for: " + eventName);
    return;
  }

  return mapping;
}

// assignKeys() returns a JSON object, where
// the values of the 'values' array are assigned
// to the column names provided in the 'mapping' array.

// For example:
// values : ["apple", "orange", "pear"]
// columns : ["fruit", "color", "shape"]

// Results in:
// { fruit : "Apple", color : "orange", shape : "pear" }

var assignKeys = function(values, columns){
  var acc = {};

  // Check data. Make sure we have enough keys for our data.
  if (columns.length != values.length){
    console.error("Warning: Mismatch in key mapping. Amount of keys and values differ." + columns.length + " Columns, " + values.length + " Values");
    console.log(columns);
    console.log(values);
    console.log("\n");
  }

  _.each(columns, function(value, key){
    // Ensure data exists. If not, make it null for DB.
    if (!values[key]) {
      values[key] = null;
    };

    // Create key/value pair
    acc[value] = values[key];
  });

  acc["game"] = settings.game;
  return acc;
  // ex: assignKeys(["apple", "orange", "pear"], ["fruit", "color", "shape"])
}

// Given a list of arrays, convert the data into JSON objects.
//   game : String of game name
//   eventName : the event name for this table
//   data : multudimensional array container player data

var assignKeysForEntireTable = function(game, eventName, data) {
  var acc = [];

  // Key the key mapping
  var mapping = getKeyMapping(game, eventName).columns;

  // Apply key mapping to each object in the data array
  _.each(data, function(d){
    var json = assigKeys(d, mapping);
    if (json) acc.push(json);
  })

  return acc;
}
<commit_msg>Tooltip should always be on right<commit_after>
/************************************
         Mapping Logic
 ************************************/

var Visualizer = {};

/************************************
         Global Variables
 ************************************/

// Global scale factor. Helps to max points (ranging
// from -10,000 to 10,000) to their coordinate points
// on a geo projection.
Visualizer.scaleFactor = 200;

// Feature Group representation of data
Visualizer.layers = [];

// Target API url
Visualizer.API_url = (window.location.href.indexOf("herokuapp.com") > -1) ? "http://g-player.herokuapp.com/api/" : "http://localhost:5000/api/";

// Take data from settings.data.
// Add to the map.
// Draw lines on the map
Visualizer.update = function(){

  // Group data by PlayerID
  var players = _.groupBy(settings.data, 'playerID');

  // Store current index
  var count = 0;

  // Iterate through players
  _.each(players, function(player){

      // Render each player onto the map
      Visualizer.draw(player, count++);
  });

  // Loading complete
  UI.loading(false, "Success. " + settings.data.length + " points loaded.");
}

// Draw a player onto the map. Positions are rendered
// as lines, while actions are clickable points.
Visualizer.draw = function(entries, index){

    // Get selected color.
    var color = Visualizer.getColor(index);

    // Ensure chronological order
    entries = sortBy(entries, "timestamp");

    /********************************
             POSITIONS
     ********************************/

    // get list of positions
    var positions = _.filter(entries, function(d){

      // Do we not have an action key?
      return !d.action;
    });

    // Create list of latLng ojects
    var positions = _.map(positions, function(point){

      // Return formatted latLng point
      return toLatLng(point);
    });

    var options = {
      stroke: true,
      color: color,
      weight: 2,
      opacity: 1,
    }

    // Create polyline
    var polyline = L.polyline(positions, options)

    var featureGroup = new L.FeatureGroup().addLayer(polyline);

    if (settings.paths) addFeatureGroup(featureGroup);

    /********************************
             ACTIONS
     ********************************/

    // Get list of actions
    var actions = _.filter(entries, function(d){

      // Do we have an action key?
      return (d.action) ? true : false;
    });

    var markers = new L.FeatureGroup();

    _.each(actions, function(p){

      var latLng = [p.latitude, p.longitude]

      var circle = L.circleMarker(latLng, {
        stroke: true,
        weight: 4,
        opacity: 1,
        color: color,
        fill: true,
        fillColor: "#fff",
        fillOpacity: 1,
        radius: 5,
      })

      // Create a new popup object
      var popup = L.popup()
        .setLatLng(latLng)
        .setContent(convertJSONtoHTML(p))

      // Add popup to the circle
      circle.bindPopup(popup);

      // Hover label for displaying action
      circle.bindLabel(p.action, { className : "tooltip-hover", direction : 'right' })

      markers.addLayer(circle);

    });

    addFeatureGroup(markers);

}

// Clears the active data set. Resets map
Visualizer.clear = function(){

    // Clear active data sets
    _.each(Visualizer.layers, function(layer){

        // Remove each active layer
        map.removeLayer(layer);
    })
}

// Adds a marker at the provided location
Visualizer.addMarker = function(lat, long, title){

  // Optional Title
  var title = (title) ? title : "";

  // Place marker on the map
  L.marker([lat, long], {title : title}).addTo(map);

}

// Get Data from API
Visualizer.loadData = function(playerIDs){

  UI.loading(true, "Loading Data....");

  var opts = Visualizer.getContext();

  if (playerIDs) opts.playerIDs = playerIDs;

  // Hit API
  $.get(Visualizer.API_url + "entries", opts, function(data){

    // Validate data. Ignore non-spacial data
    data = _.filter(data, function(p){
      return containsRequiredKeys(p);
    })

    // Convert data points into plottable data
    data = _.map(data, function(p){
      return Visualizer.formatData(p);
    })

    // Save data for future reference
    settings.data = data;

    // Update our map with new data.
    Visualizer.update();

  })
};

// Convert the (x,y) values from the player logs
// into Latitude and Longitude positions on a map.
Visualizer.formatData = function(data){

  // Global scale factor. Helps to max points (ranging
  // from -10,000 to 10,000) to their coordinate points
  // on a geo projection.

  var scale = Visualizer.scaleFactor;

  // Create a latitude & longitude field.
  // Maps the (x,y) position to a coordinate
  // on the earth. Makes plotting MUCH easier

  data['latitude']  = data.posY / scale;
  data['longitude'] = data.posX / scale;

  return data;
}

// Convert a lat/long to it's raw X/Y values
Visualizer.unformatData = function(latLong){

  var scale = Visualizer.scaleFactor;

  return {
    posX : latLong['longitude'] * scale,
    posY : latLong['latitude'] * scale,
  }
}

Visualizer.getColor = function(i){

  var colors = ["#d73027", "#f46d43", "#fdae61",
                "#fee090", "#ffffbf", "#e0f3f8",
                "#abd9e9", "#74add1", "#4575b4"];

  return (i < colors.length - 1) ? colors[i] : "#000000";
}

// Returns a representation of the current state of the
// map. This object provided context for what data
// the API should return in order to be mapped.
Visualizer.getContext = function(callback){

  var obj = {
    game : settings.game,
    area : settings.map.name,
    fidelity : 1,
    playerIDs : options.players,
    actions : settings.actions,

  }

  if (obj.actions.length < 1) obj.actions = ["Quest"];

  console.log("\nCurrent Settings of the Map:");
  console.log(obj);

  return obj;
}

// Update the map's view port, as to
// center the current data set.
Visualizer.focus = function(){

    var sample = Visualizer.layers[0]

    if (sample) map.fitBounds(sample.getBounds());

    // TODO: Set max/min zoom levels dynamically
    // map.minZoom()
    // map.maxZoom()
}

/**************************************
         HELPER FUNCTIONS
     ... these should *never* be
   called from the User Interface.
 **************************************/

// Sort the provided list by the provided key
function sortBy (list, key){
  return _.sortBy(list, function(l){
        return l[key];
      })
}

// Convert the provided JSON object into an
// HTML representation
function convertJSONtoHTML(JSON){
  var acc = "";

  _.each(JSON, function(val, key){

    // Lets ignore a few keys
    // These keys are automatically generated
    // by the system, so the end user
    // won't miss them.

    if (key != "__v" &&
        key != "latitude" &&
        key != "longitude"){

      acc += "
" + key + " : " + val + "
";
    }

  })

  return acc;
}

// Does the provided object contain the required keys?
function containsRequiredKeys(obj){

  // Required keys
  var keys = ["playerID", "area", "posX", "posY", "timestamp"];

  var acc = true;

  _.each(keys, function(key){
    var containsKey = (obj && obj[key]) ? true : false;
    acc = acc && containsKey;
  })

  return acc;
}

// Adds the provided featureGroup to the map.
// Feature groups are objects representing
// "bulked", or otherwise grouped vector
// objects as a single layer on the map.
function addFeatureGroup (featureGroup){

    // Save layer to settings.
    // We must save a reference to each layer,
    // as it's the most effective way to iterate
    // through and remove layers.

    Visualizer.layers.push(featureGroup);

    // Save layer for reference
    map.addLayer(featureGroup);
}

// Convert the JSON Object to a leaflet LatLong object
function toLatLng (point){

  // Extract keys. May be 'latitude' or 'lat'.
  var lat  = (point.latitude)  ? point.latitude  : point.lat;

  // Extract keys. May be 'longitude' or 'long'.
  var long = (point.longitude) ? point.longitude : point.long;

  // Return lat/long if they exist.
  if (lat && long) { return L.latLng(lat, long); }

};

// Return the key mapping given the
// game name, and the event name
// ex: getKeyMapping("Fallout New Vegas", "Attacked")
var getKeyMapping = function(game, eventName){

  // Get the type of event from the lookup table
  var type = options.lookup_table[eventName];

  // Find mapping
  var mapping = _.findWhere(options.mappings, {game : settings.game, type : type});

  if (!mapping || !type) {
    console.error("Unable to find key mapping for: " + eventName);
    return;
  }

  return mapping;
}

// assignKeys() returns a JSON object, where
// the values of the 'values' array are assigned
// to the column names provided in the 'mapping' array.

// For example:
// values : ["apple", "orange", "pear"]
// columns : ["fruit", "color", "shape"]

// Results in:
// { fruit : "Apple", color : "orange", shape : "pear" }

var assignKeys = function(values, columns){
  var acc = {};

  // Check data. Make sure we have enough keys for our data.
  if (columns.length != values.length){
    console.error("Warning: Mismatch in key mapping. Amount of keys and values differ." + columns.length + " Columns, " + values.length + " Values");
    console.log(columns);
    console.log(values);
    console.log("\n");
  }

  _.each(columns, function(value, key){
    // Ensure data exists. If not, make it null for DB.
    if (!values[key]) {
      values[key] = null;
    };

    // Create key/value pair
    acc[value] = values[key];
  });

  acc["game"] = settings.game;
  return acc;
  // ex: assignKeys(["apple", "orange", "pear"], ["fruit", "color", "shape"])
}

// Given a list of arrays, convert the data into JSON objects.
//   game : String of game name
//   eventName : the event name for this table
//   data : multudimensional array container player data

var assignKeysForEntireTable = function(game, eventName, data) {
  var acc = [];

  // Key the key mapping
  var mapping = getKeyMapping(game, eventName).columns;

  // Apply key mapping to each object in the data array
  _.each(data, function(d){
    var json = assigKeys(d, mapping);
    if (json) acc.push(json);
  })

  return acc;
}
<|endoftext|>


<commit_before>'use strict';

var localstorage = localstorage || {};

localstorage.dataPoints = [];

function DataPoint (date, prescription, dosage, tLevel, eLevel, logEntry){
  this.date = date;
  this.prescription = prescription;
  this.dosage = dosage;
  this.tLevel = tLevel;
  this.eLevel = eLevel;
  this.logEntry = logEntry;
  localstorage.dataPoints.push(this);
};

DataPoint.prototype.toHtml = function (){
   //manipulate template strings here so that the dom renders them to #log section

};

$('#submit-button').on('click', function(e){
  e.preventDefault();
  var obj = new DataPoint($('#date').val(), $('#prescription').val(), parseInt($('#dosage').val()), parseInt($('#tLevel').val()), parseInt($('#eLevel').val()), $('#entry-form').val());
  $.post('/submit', obj).then(console.log('post complete')).catch(console.error);
});
<commit_msg>changed localstorage to localStorage obj, deleted localstorage var declaration at the top of the page<commit_after>'use strict';

localStorage.dataPoints = [];

function DataPoint (date, prescription, dosage, tLevel, eLevel, logEntry){
  this.date = date;
  this.prescription = prescription;
  this.dosage = dosage;
  this.tLevel = tLevel;
  this.eLevel = eLevel;
  this.logEntry = logEntry;
  localstorage.dataPoints.push(this);
};

DataPoint.prototype.toHtml = function (){
   //manipulate template strings here so that the dom renders them to #log section

};

$('#submit-button').on('click', function(e){
  e.preventDefault();
  var obj = new DataPoint($('#date').val(), $('#prescription').val(), parseInt($('#dosage').val()), parseInt($('#tLevel').val()), parseInt($('#eLevel').val()), $('#entry-form').val());
  $.post('/submit', obj).then(console.log('post complete')).catch(console.error);
});
<|endoftext|>


<commit_before>/**
 * This code allows to create complex form in the twig module
 * You need in place modification
 *
 * In the twig templat eyou will have something like:
 *
 *
 *   And the template will be like:
 *
 *

 *

 *
 *           Kind
 *           Firstname
 *           Lastname
 *           Nationalities
 *

 *

 *
 *               <select data-field='kind'>
 *                   <option value=''></option>
 *                   <option value='author'>Author</option>
 *                   <option value='editor'>Editor</option>
 *              </select>
 *
 *
 *               <input type='text' size=10 data-field='firstname'>
 *
 *
 *               <input type='text' size=10 data-field='lastname'>
 *
 *
 *

 *

 *
 *                           <input placeholder="Nationality" type='text' size=10 data-field=''>
 *
 *

 *

 *
 *

 *

 */
 define(['src/util/api'], function (API) {

    function AdvancedForm(divID, variableName, options={}) {
        var data = API.getData(variableName);
        // we will initialise the form
        var dom=$(document.getElementById(divID));
        // Add the buttons ADD / REMOVE
        dom.find('[data-repeat]').prepend(`

        `);

        // Add the style
        dom.parent().prepend(
            ``
        );

        // need to replicate rows
        var counter=0;
        do {
            var elements=dom.find('[data-repeat]:not([data-index])');
            elements.each(function(index, row) {
                row=$(row);
                var jpath=getJpath(row);
                var variable=data.getChildSync(jpath);
                var table=row.closest('table');
                var length=0;
                var empty=false;
                if (!variable || variable.length===0 ) {
                    length=1;
                    empty=true;
                } else if (Array.isArray(variable)) {
                    length=variable.length;
                } else {
                    console.log('Wrong variable type', variable);
                }
                for (var i=0; i<length; i++) {
                    if (i==0) {
                        var currentRow=row;
                    } else {
                        var currentRow = row.clone();
                        table.append(currentRow);
                    }
                    currentRow.attr('data-index',i);
                    renameRow(currentRow, jpath, i, empty);
                }
                rename(table);
            });
        } while (elements.length>0)

        // we force the incorporation of the data in the form
        data.triggerChange();

        // rename the attributes 'name' or 'name-empty' of one specific row based on the jpath
        function renameRow(row, jpath, rowIndex, empty) {
            row=$(row);
            row.children('td:not(:has(table))').find('[data-field]').each(function(index, element) {
                element=$(element);
                var name=jpath.join('.');
                name+='.'+rowIndex;
                var attr=element.attr('data-field');
                if (attr) name+='.'+attr;
                if (empty) {
                    element.attr('name-empty', name);
                } else {
                    element.attr('name', name);
                }
            })
        }

        // get the jpath from one element based on the attributes 'data-repeat' and 'data-index'
        // the jpath is returned as an array
        function getJpath(element) {
            var jpath=[];
            while ($(element).length) {
                if ($(element).attr('data-index')) {
                    jpath.unshift($(element).attr('data-index'));
                }
                var repeatName=$(element).attr('data-repeat');
                console.log('repeatName', repeatName)
                if (repeatName) jpath.unshift(repeatName);
                element=$(element).parent().closest('[data-repeat]');
            }
            return jpath;
        }

        /*
            Rename the the full table
        */
        function rename(tbody) {
            var base=getBase(tbody).base;
            var search=new RegExp(base+'.[0-9]+');
            var rows=tbody.children('tr:has(td)');
            rows.each(function(rowIndex, row) {
                var replace=base+'.'+rowIndex;
                for (var attr of ['name','name-empty']) {
                    $(row).find('['+attr+']').each(
                        function(index, element) {
                            element=$(element);
                            var name=element.attr(attr);
                            name=name.replace(search,replace);
                            element.attr(attr, name);
                        }
                    );
                }
            });
        }

        function getBase(element) {
            var names=[];
            element.find('[name]').each(function(index,element) {
                names.push($(element).attr('name'));
            });
            if (names.length===0) {
                return '';
            }
            names.sort();
            return {
                base: names[0].replace(/(.*)\.([0-9]+).*/,'$1'),
                index: names[0].replace(/(.*)\.([0-9]+).*/,'$2'),
            };
        }

        if (options.debug) {
            document.getElementById(divID).addEventListener('mouseover', function(event) {
                var target=$(event.target);
                if (target.attr('name')) {
                    console.log('Name',target.attr('name'));
                }
            });
            document.getElementById(divID).addEventListener('mouseover', function(event) {
                var target=$(event.target);
                if (target.attr('name-empty')) {
                    console.log('Empty',target.attr('name'));
                }
            });
        }

        // when the value of a row change we should rename property if it was hidden
        document.getElementById(divID).addEventListener('change', function(event) {
            var target=$(event.target);
            if (target.attr('name-empty')) {
                var empties=target.closest('tr').children('td:not(:has(table))').find('[name-empty]');
                empties.each( (index,element) => {
                    $(element).attr('name',$(element).attr('name-empty'));
                    $(element).removeAttr('name-empty');
                });
            }
        });

        document.getElementById(divID).addEventListener('click', function(event) {
            var from=event.target;
            var table=$(from).closest('tbody');
            var tr=$(from).closest('tr');
            switch (from.className) {
                case 'addRow':
                    // if we try to add a row we should check if
                    // there is already an empty one
                    var empties=table.children('tr').children('td:not(:has(table))').find('[name-empty]');
                    if (empties.length>0) {
                        empties[0].focus();
                        return;
                    }
                    var clone = tr.clone();
                    clone.find('select, input, textarea').val('');
                    clone.find('tr:not(:first-child)').remove();
                    var fields=clone.find('[name]');
                    // rename attribute 'name' to 'name-empty'
                    fields.each( (index,element) => {
                        $(element).attr('name-empty',$(element).attr('name'));
                        $(element).removeAttr('name');
                    });
                    table.append(clone);
                    clone.find('[name-empty]')[0].focus();
                    rename(table);
                    break;
                case 'removeRow':
                    var base=getBase(tr);
                    if (base) {
                        var jpath=base.base.split('.');
                        var data = API.getData(variableName);
                        var variable=data.getChildSync(jpath)
                        variable.splice(base.index, 1);
                        data.triggerChange();
                    }
                    // need to throw an event to remove this entry
                    if(table.children('tr:has(td)').length>1) {
                        tr.remove();
                    } else {
                        tr.find('select, input, textarea').val('');
                        tr.find('tr:not(:first-child)').remove();
                        var fields=tr.find('[name]');
                        fields.each( (index,element) => {
                            $(element).attr('name-empty',$(element).attr('name'));
                            $(element).removeAttr('name');
                        });
                        tr.find('[name-empty]')[0].focus();
                    }
                    rename(table);
                    break;
                }
            })
        }
        return AdvancedForm;
    }
 );
<commit_msg>Minor fix<commit_after>/**
 * This code allows to create complex form in the twig module
 * You need in place modification
 *
 * In the twig templat eyou will have something like:
 *
 *
 *   And the template will be like:
 *
 *

 *

 *
 *           Kind
 *           Firstname
 *           Lastname
 *           Nationalities
 *

 *

 *
 *               <select data-field='kind'>
 *                   <option value=''></option>
 *                   <option value='author'>Author</option>
 *                   <option value='editor'>Editor</option>
 *              </select>
 *
 *
 *               <input type='text' size=10 data-field='firstname'>
 *
 *
 *               <input type='text' size=10 data-field='lastname'>
 *
 *
 *

 *

 *
 *                           <input placeholder="Nationality" type='text' size=10 data-field=''>
 *
 *

 *

 *
 *

 *

 */
 define(['src/util/api'], function (API) {

    function AdvancedForm(divID, variableName, options={}) {
        var data = API.getData(variableName);
        // we will initialise the form
        var dom=$(document.getElementById(divID));
        // Add the buttons ADD / REMOVE
        dom.find('[data-repeat]').prepend(`

        `);

        // Add the style
        dom.parent().prepend(
            ``
        );

        // need to replicate rows
        var counter=0;
        do {
            var elements=dom.find('[data-repeat]:not([data-index])');
            elements.each(function(index, row) {
                row=$(row);
                var jpath=getJpath(row);
                var variable=data.getChildSync(jpath);
                var table=row.closest('table');
                var length=0;
                var empty=false;
                if (!variable || variable.length===0 ) {
                    length=1;
                    empty=true;
                } else if (Array.isArray(variable)) {
                    length=variable.length;
                } else {
                    console.log('Wrong variable type', variable);
                }
                for (var i=0; i<length; i++) {
                    if (i==0) {
                        var currentRow=row;
                    } else {
                        var currentRow = row.clone();
                        table.append(currentRow);
                    }
                    currentRow.attr('data-index',i);
                    renameRow(currentRow, jpath, i, empty);
                }
                rename(table);
            });
        } while (elements.length>0)

        // we force the incorporation of the data in the form
        data.triggerChange();

        // rename the attributes 'name' or 'name-empty' of one specific row based on the jpath
        function renameRow(row, jpath, rowIndex, empty) {
            row=$(row);
            row.children('td:not(:has(table))').find('[data-field]').each(function(index, element) {
                element=$(element);
                var name=jpath.join('.');
                if (name) name+='.';
                name+=rowIndex;
                var attr=element.attr('data-field');
                if (attr) name+='.'+attr;
                if (empty) {
                    element.attr('name-empty', name);
                } else {
                    element.attr('name', name);
                }
            })
        }

        // get the jpath from one element based on the attributes 'data-repeat' and 'data-index'
        // the jpath is returned as an array
        function getJpath(element) {
            var jpath=[];
            while ($(element).length) {
                if ($(element).attr('data-index')) {
                    jpath.unshift($(element).attr('data-index'));
                }
                var repeatName=$(element).attr('data-repeat');
                if (repeatName) jpath.unshift(repeatName);
                element=$(element).parent().closest('[data-repeat]');
            }
            return jpath;
        }

        /*
            Rename the the full table
        */
        function rename(tbody) {
            var base=getBase(tbody).base;
            var search=new RegExp(base+'.[0-9]+');
            var rows=tbody.children('tr:has(td)');
            rows.each(function(rowIndex, row) {
                var replace=base+'.'+rowIndex;
                for (var attr of ['name','name-empty']) {
                    $(row).find('['+attr+']').each(
                        function(index, element) {
                            element=$(element);
                            var name=element.attr(attr);
                            name=name.replace(search,replace);
                            element.attr(attr, name);
                        }
                    );
                }
            });
        }

        function getBase(element) {
            var names=[];
            element.find('[name]').each(function(index,element) {
                names.push($(element).attr('name'));
            });
            if (names.length===0) {
                return '';
            }
            names.sort();
            return {
                base: names[0].replace(/(.*)\.([0-9]+).*/,'$1'),
                index: names[0].replace(/(.*)\.([0-9]+).*/,'$2'),
            };
        }

        if (options.debug) {
            document.getElementById(divID).addEventListener('mouseover', function(event) {
                var target=$(event.target);
                if (target.attr('name')) {
                    console.log('Name',target.attr('name'));
                }
            });
            document.getElementById(divID).addEventListener('mouseover', function(event) {
                var target=$(event.target);
                if (target.attr('name-empty')) {
                    console.log('Empty',target.attr('name'));
                }
            });
        }

        // when the value of a row change we should rename property if it was hidden
        document.getElementById(divID).addEventListener('change', function(event) {
            var target=$(event.target);
            if (target.attr('name-empty')) {
                var empties=target.closest('tr').children('td:not(:has(table))').find('[name-empty]');
                empties.each( (index,element) => {
                    $(element).attr('name',$(element).attr('name-empty'));
                    $(element).removeAttr('name-empty');
                });
            }
        });

        document.getElementById(divID).addEventListener('click', function(event) {
            var from=event.target;
            var table=$(from).closest('tbody');
            var tr=$(from).closest('tr');
            switch (from.className) {
                case 'addRow':
                    // if we try to add a row we should check if
                    // there is already an empty one
                    var empties=table.children('tr').children('td:not(:has(table))').find('[name-empty]');
                    if (empties.length>0) {
                        empties[0].focus();
                        return;
                    }
                    var clone = tr.clone();
                    clone.find('select, input, textarea').val('');
                    clone.find('tr:not(:first-child)').remove();
                    var fields=clone.find('[name]');
                    // rename attribute 'name' to 'name-empty'
                    fields.each( (index,element) => {
                        $(element).attr('name-empty',$(element).attr('name'));
                        $(element).removeAttr('name');
                    });
                    table.append(clone);
                    clone.find('[name-empty]')[0].focus();
                    rename(table);
                    break;
                case 'removeRow':
                    var base=getBase(tr);
                    if (base) {
                        var jpath=base.base.split('.');
                        var data = API.getData(variableName);
                        var variable=data.getChildSync(jpath)
                        variable.splice(base.index, 1);
                        data.triggerChange();
                    }
                    // need to throw an event to remove this entry
                    if(table.children('tr:has(td)').length>1) {
                        tr.remove();
                    } else {
                        tr.find('select, input, textarea').val('');
                        tr.find('tr:not(:first-child)').remove();
                        var fields=tr.find('[name]');
                        fields.each( (index,element) => {
                            $(element).attr('name-empty',$(element).attr('name'));
                            $(element).removeAttr('name');
                        });
                        tr.find('[name-empty]')[0].focus();
                    }
                    rename(table);
                    break;
                }
            })
        }
        return AdvancedForm;
    }
 );
<|endoftext|>


<commit_before>describe('Event save', function () {
	var eventId;
	var event;

	it('Stores an event', function () {
		server.call('login', {
			"user": {
				"username": "greg"
			},
			"password": {
				"digest": "0d2c690e7dd5f94780383e9dfa1f4def044319104ad16ab15e45eeb2a8dfc81b",
				"algorithm": "sha-256"
			}
		});

		var theFuture = new Date();
		theFuture.setHours(1000);

		var evenLater = new Date();
		evenLater.setHours(1002);

		event = {
			title: "Intentionally clever title for a generated test-event",
			description: "This space intentionally filled with bland verbiage. You are safe to ignore this. ",
			location: { name: "Undisclosed place where heavy testing takes place" },
			start: theFuture,
			end: evenLater,
			region: 'testId',
			internal: true,
		};

		eventId = server.call('saveEvent', '', event);

		assert.isString(eventId, "got an event ID");
	});

	it('Allows creator to update the event', function () {
		delete event.region;
		event.title = event.title + " No really";
		server.call('saveEvent', eventId, event);
	});
});
<commit_msg>fix test follwing venue rename<commit_after>describe('Event save', function () {
	var eventId;
	var event;

	it('Stores an event', function () {
		server.call('login', {
			"user": {
				"username": "greg"
			},
			"password": {
				"digest": "0d2c690e7dd5f94780383e9dfa1f4def044319104ad16ab15e45eeb2a8dfc81b",
				"algorithm": "sha-256"
			}
		});

		var theFuture = new Date();
		theFuture.setHours(1000);

		var evenLater = new Date();
		evenLater.setHours(1002);

		event = {
			title: "Intentionally clever title for a generated test-event",
			description: "This space intentionally filled with bland verbiage. You are safe to ignore this. ",
			venue: { name: "Undisclosed place where heavy testing takes place" },
			start: theFuture,
			end: evenLater,
			region: 'testId',
			internal: true,
		};

		eventId = server.call('saveEvent', '', event);

		assert.isString(eventId, "got an event ID");
	});

	it('Allows creator to update the event', function () {
		delete event.region;
		event.title = event.title + " No really";
		server.call('saveEvent', eventId, event);
	});
});
<|endoftext|>