<commit_before>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const AdmZip = require('adm-zip');
const inquirer = require('inquirer');
const fuzzy = require('fuzzy');
inquirer.registerPrompt('checkbox-autocomplete', require('inquirer-checkbox-autocomplete-prompt'));

const filesPath = path.join(__dirname, 'data', 'files');
const statsPath = path.join(__dirname, 'data', 'stats');

const stopwords = new Set(require(path.join(__dirname, 'stopwords.json')));

const numTopWords = 50;
const idRegEx = /FID-F_SBB_\d+_(\d+_\d+_\d+)_(\d+)_(\d+)-OCRMASTER-TECHMD/;

const parser = new xml2js.Parser();
function xml2obj(fileName) {
	return new Promise((resolve, reject) => {
		if (fileName.trim()[0] === '<') {
			parser.parseString(fileName, function(error, result) {
				if (error) reject(error);
				else resolve(result);
			});
		} else {
			fs.readFile(fileName, function(err, data) {
				parser.parseString(data, function(error, result) {
					if (error) reject(error);
					else resolve(result);
				});
			});
		}
	});
}

function parseMETS(fileName) {
	return xml2obj(fileName).then(result => {
		const rootXML = result['METS:mets'];
		const modsXML = rootXML['mets:dmdSec'][0]['mets:mdWrap'][0]['mets:xmlData'][0]['mods:mods'][0];
		const detailsXML = modsXML['mods:relatedItem'][0]['mods:part'][0]['mods:detail'];
		const pagesXML = rootXML['mets:amdSec'][0]['mets:techMD'];

		const pages = pagesXML.map(pageXML => {
			const id = pageXML['$'].ID;
			return {
				id,
				idParts: idRegEx.exec(id).slice(1),
				dpi:
					pageXML['mets:mdWrap'][0]['mets:xmlData'][0]['mix:mix'][0]['mix:ImageAssessmentMetadata'][0]['mix:SpatialMetrics'][0][
						'mix:xSamplingFrequency'
					][0]['mix:numerator'][0],
			};
		});

		const title = modsXML['mods:titleInfo'][0]['mods:title'][0];
		const dateIssued = modsXML['mods:originInfo'][0]['mods:dateIssued'][0]['_'];
		const details = detailsXML.map(detail => ({
			caption: detail['mods:caption'][0],
			number: detail['mods:number'][0],
		}));
		return { title, dateIssued, details, pages };
	});
}

function recursionReducer(recurseFkt, root, addSelf = false) {
	return (acc, tagName) => [
		...acc,
		...(root[tagName] || []).reduce((_acc, node) => [..._acc, ...recurseFkt(node)], []),
		...(addSelf ? root[tagName] || [] : []),
	];
}

/**
 * recurseToString will get all `String` tags from a root node.
 * `String` tags represent individual words.
 * @param {Object} root xml2js node
 * @return {Array} list of xml2js String nodes
 */
function recurseToString(root) {
	return [
		...['TextBlock', 'ComposedBlock', 'TextLine'].reduce(recursionReducer(recurseToString, root), []),
		...(root.String || []),
	];
}

function recurseToLine(root) {
	return [...['TextBlock', 'ComposedBlock'].reduce(recursionReducer(recurseToLine, root), []), ...(root.TextLine || [])];
}

function recurseToBlock(root) {
	return ['TextBlock', 'ComposedBlock'].reduce(recursionReducer(recurseToBlock, root, true), []);
}

function zeroPad(num, size) {
	const s = `000000000${num}`;
	return s.substr(s.length - size);
}

function getSize(o) {
	return {
		x: parseInt(o.HPOS, 10),
		y: parseInt(o.VPOS, 10),
		w: parseInt(o.WIDTH, 10),
		h: parseInt(o.HEIGHT, 10),
	};
}

function px2mm(l) {
	return (l * 25.4 / 300).toFixed(1);
}

function getTopWords(words) {
	const topWords = Object.entries(
		words.reduce((acc, w) => {
			const wn = w.$.CONTENT.replace(/\W/g, '');
			if (wn.length < 3) return acc;
			if (/\d+/.test(wn)) return acc;
			if (wn in acc) acc[wn] += 1;
			else acc[wn] = 1;
			return acc;
		}, {})
	).filter(w => !stopwords.has(w[0].toLocaleLowerCase()));
	topWords.sort((a, b) => (a[1] < b[1] ? 1 : -1));
	return topWords;
}

function parseAlto(fileName) {
	return xml2obj(fileName).then(result => {
		const printSpaceXML = result.alto.Layout[0].Page[0].PrintSpace[0];
		const words = recurseToString(printSpaceXML);
		// return { words: words.map(w => w.$.CONTENT).join(' ') };
		const lines = recurseToLine(printSpaceXML);
		const blocks = recurseToBlock(printSpaceXML);
		const minMax = words.reduce(
			(acc, w) => {
				const size = getSize(w.$);
				return {
					xMin: acc.xMin > size.x ? size.x : acc.xMin,
					xMax: acc.xMax < size.x + size.w ? size.x + size.w : acc.xMax,
					yMin: acc.yMin > size.y ? size.y : acc.yMin,
					yMax: acc.yMax < size.y + size.h ? size.y + size.h : acc.yMax,
				};
			},
			{ xMin: 999999999, yMin: 999999999, xMax: 0, yMax: 0 }
		);

		return {
			topWords: getTopWords(words)
				.map(w => w[0])
				.slice(0, numTopWords),
			Strings: words.length,
			TextLines: lines.length,
			arithmeticMeanStrinsPerLine: (lines.reduce((acc, l) => acc + (l.String || []).length, 0) / lines.length).toFixed(4),
			arithmeticMeanLineLengthInMm: px2mm(lines.reduce((acc, l) => acc + getSize(l.$).w, 0) / lines.length),
			blocks: blocks.length,
			arithmeticMeanLinesPerBlock: (blocks.reduce((acc, l) => acc + (l.TextLine || []).length, 0) / blocks.length).toFixed(4),
			sizeInPx: minMax,
			sizeInMm: {
				width: px2mm(minMax.xMax - minMax.xMin),
				height: px2mm(minMax.yMax - minMax.yMin),
			},
			arithmeticMeanWC: (words.reduce((acc, w) => acc + parseFloat(w.$.WC), 0) / words.length).toFixed(5),
		};
		console.log('words', words.length);
		// console.log('words', words.map(w => w.$.CONTENT).join(' '));
	});
}

function getZipContent(zipFilePath) {
	const { size } = fs.statSync(zipFilePath);
	console.log('size', size);
	console.log('load file', zipFilePath);
	const zip = new AdmZip(zipFilePath);
	const zipEntries = zip.getEntries(); // an array of ZipEntry records
	const altoFiles = zipEntries.filter(zipEntry => /\d\.xml$/.test(zipEntry.entryName)).map(zipEntry => ({
		name: zipEntry.entryName,
		content: zipEntry.getData().toString('utf8'),
	}));
	const metsFile = zipEntries.find(zipEntry => /METS.xml$/i.test(zipEntry.entryName));
	return {
		zipFileName: path.basename(zipFilePath, path.extname(zipFilePath)),
		altoFiles,
		metsFile: {
			name: metsFile.entryName,
			content: metsFile.getData().toString('utf8'),
		},
	};
}

// function ensurePath(...path) {
// }

async function parseZipContent(file, year) {
	const dataMets = await parseMETS(file.metsFile.content);
	const pageStats = await Promise.all(file.altoFiles.map(({ content }) => parseAlto(content))).then(altos =>
		altos.map((res, index) => ({ ...res, fileName: file.altoFiles[index].name }))
	);
	const fileStats = {
		...dataMets,
		metsFileName: file.metsFile.name,
		zipFileName: file.zipFileName,
		pages: pageStats,
	};
	if (!fs.existsSync(statsPath)) fs.mkdirSync(statsPath);
	const yearPath = path.join(statsPath, year);
	if (!fs.existsSync(yearPath)) fs.mkdirSync(yearPath);
	const statsFilePath = path.join(yearPath, `${file.zipFileName}.json`);
	console.log('statsFileName', statsFilePath);
	fs.writeFileSync(statsFilePath, JSON.stringify(fileStats, null, 2));
	return fileStats;
}

function copyFile(source, target) {
	const rd = fs.createReadStream(source);
	rd.pipe(fs.createWriteStream(target));
}

async function execSync(promises) {
	const out = [];
	for (const promise of promises) {
		try {
			out.push(await promise);
		} catch (error) {
			console.log('error: ', error);
		}
	}
	return out;
}

async function addToIndex() {
	const dataPath = path.join(__dirname, 'stats-index.json');
	// const filesPath = path.join('data', 'files');
	let oldData = {};
	try {
		oldData = require(dataPath);
	} catch (e) {
		process.stderr.write(`${dataPath} was empty, initialized with {}\n`);
	}

	// statsPath
	const files = fs.readdirSync(filesPath).reduce(
		(acc, year) => [
			...acc,
			...fs
				.readdirSync(path.join(filesPath, year))
				.filter(file => !(file in oldData))
				.map(file => [year, path.join(filesPath, year, file)]),
		],
		[]
	);
	const newData = Object.assign({}, oldData);
	for (const [year, filePath] of files) {
		try {
			newData[filePath] = await parseZipContent(getZipContent(filePath), year);
		} catch (error) {
			console.log('error: ', error);
		}
		console.log('done');
	}
	console.log('newData', newData);
	fs.writeFileSync(dataPath, JSON.stringify(newData, null, 2));
}

async function runCui() {
	const actions = [
		{
			name: 'Parse a file',
			async action() {
				const years = fs.readdirSync(`${filesPath}/`);
				// console.log('years', years);
				// const { year } = await inquirer.prompt([
				// 	{
				// 		type: 'checkbox-autocomplete',
				// 		name: 'year',
				// 		asyncSource: async (answers, input) => fuzzy.filter(input || '', years).map(el => el.original),
				// 		message: 'Please select a year:',
				// 		validate(answer) {
				// 			if (answer.length !== 1) return 'You must choose one year.';
				// 			return true;
				// 		},
				// 	},
				// ]);
				const files = years.reduce((allFiles, year) => {
					const filesInFolder = fs.readdirSync(`${filesPath}/${year}/`);
					return allFiles.concat(filesInFolder.map(file => path.join(year, file)));
				}, []);
				const { fileList } = await inquirer.prompt([
					{
						type: 'checkbox-autocomplete',
						name: 'fileList',
						asyncSource: async (answers, input) => fuzzy.filter(input || '', files).map(el => el.original),
						message: 'Please select one file:',
						validate(answer) {
							if (answer.length < 1) return 'You must choose at least one file.';
							return true;
						},
					},
				]);
				console.log('fileList', fileList);
				return Promise.all(
					fileList.map(file =>
						parseZipContent(getZipContent(path.join(filesPath, file)), file.split('/')[0])
							.then(res => {} /*console.log(JSON.stringify(res, null, 2))*/)
							.catch(e => console.warn(e))
					)
				);
			},
		},
		{
			name: 'Extend index with downloaded Files',
			action: () => addToIndex(),
		},
	];
	const { startAction } = await inquirer.prompt([
		{
			type: 'list',
			message: 'What do you want to do?',
			name: 'startAction',
			choices: actions,
		},
	]);
	const { action } = actions.find(({ name }) => name === startAction);
	if (action) action().then();
}

if (require.main === module) {
	runCui().then(() => console.log('The End'));
}

module.exports = {
	runCui,
	parseZipContent,
	getZipContent,
};
<commit_msg>add the absolute page size<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const AdmZip = require('adm-zip');
const inquirer = require('inquirer');
const fuzzy = require('fuzzy');
inquirer.registerPrompt('checkbox-autocomplete', require('inquirer-checkbox-autocomplete-prompt'));

const filesPath = path.join(__dirname, 'data', 'files');
const statsPath = path.join(__dirname, 'data', 'stats');

const stopwords = new Set(require(path.join(__dirname, 'stopwords.json')));

const numTopWords = 50;
const idRegEx = /FID-F_SBB_\d+_(\d+_\d+_\d+)_(\d+)_(\d+)-OCRMASTER-TECHMD/;

const parser = new xml2js.Parser();
function xml2obj(fileName) {
	return new Promise((resolve, reject) => {
		if (fileName.trim()[0] === '<') {
			parser.parseString(fileName, function(error, result) {
				if (error) reject(error);
				else resolve(result);
			});
		} else {
			fs.readFile(fileName, function(err, data) {
				parser.parseString(data, function(error, result) {
					if (error) reject(error);
					else resolve(result);
				});
			});
		}
	});
}

function parseMETS(fileName) {
	return xml2obj(fileName).then(result => {
		const rootXML = result['METS:mets'];
		const modsXML = rootXML['mets:dmdSec'][0]['mets:mdWrap'][0]['mets:xmlData'][0]['mods:mods'][0];
		const detailsXML = modsXML['mods:relatedItem'][0]['mods:part'][0]['mods:detail'];
		const pagesXML = rootXML['mets:amdSec'][0]['mets:techMD'];

		const pages = pagesXML.map(pageXML => {
			const id = pageXML['$'].ID;
			return {
				id,
				idParts: idRegEx.exec(id).slice(1),
				dpi:
					pageXML['mets:mdWrap'][0]['mets:xmlData'][0]['mix:mix'][0]['mix:ImageAssessmentMetadata'][0]['mix:SpatialMetrics'][0][
						'mix:xSamplingFrequency'
					][0]['mix:numerator'][0],
			};
		});

		const title = modsXML['mods:titleInfo'][0]['mods:title'][0];
		const dateIssued = modsXML['mods:originInfo'][0]['mods:dateIssued'][0]['_'];
		const details = detailsXML.map(detail => ({
			caption: detail['mods:caption'][0],
			number: detail['mods:number'][0],
		}));
		return { title, dateIssued, details, pages };
	});
}

function recursionReducer(recurseFkt, root, addSelf = false) {
	return (acc, tagName) => [
		...acc,
		...(root[tagName] || []).reduce((_acc, node) => [..._acc, ...recurseFkt(node)], []),
		...(addSelf ? root[tagName] || [] : []),
	];
}

/**
 * recurseToString will get all `String` tags from a root node.
 * `String` tags represent individual words.
 * @param {Object} root xml2js node
 * @return {Array} list of xml2js String nodes
 */
function recurseToString(root) {
	return [
		...['TextBlock', 'ComposedBlock', 'TextLine'].reduce(recursionReducer(recurseToString, root), []),
		...(root.String || []),
	];
}

function recurseToLine(root) {
	return [...['TextBlock', 'ComposedBlock'].reduce(recursionReducer(recurseToLine, root), []), ...(root.TextLine || [])];
}

function recurseToBlock(root) {
	return ['TextBlock', 'ComposedBlock'].reduce(recursionReducer(recurseToBlock, root, true), []);
}

function zeroPad(num, size) {
	const s = `000000000${num}`;
	return s.substr(s.length - size);
}

function getSize(o) {
	return {
		x: parseInt(o.HPOS, 10),
		y: parseInt(o.VPOS, 10),
		w: parseInt(o.WIDTH, 10),
		h: parseInt(o.HEIGHT, 10),
	};
}

function px2mm(l) {
	return (l * 25.4 / 300).toFixed(1);
}

function getTopWords(words) {
	const topWords = Object.entries(
		words.reduce((acc, w) => {
			const wn = w.$.CONTENT.replace(/\W/g, '');
			if (wn.length < 3) return acc;
			if (/\d+/.test(wn)) return acc;
			if (wn in acc) acc[wn] += 1;
			else acc[wn] = 1;
			return acc;
		}, {})
	).filter(w => !stopwords.has(w[0].toLocaleLowerCase()));
	topWords.sort((a, b) => (a[1] < b[1] ? 1 : -1));
	return topWords;
}

function parseAlto(fileName) {
	return xml2obj(fileName).then(result => {
		const printSpaceXML = result.alto.Layout[0].Page[0].PrintSpace[0];
		const words = recurseToString(printSpaceXML);
		// return { words: words.map(w => w.$.CONTENT).join(' ') };
		const lines = recurseToLine(printSpaceXML);
		const blocks = recurseToBlock(printSpaceXML);
		const minMax = words.reduce(
			(acc, w) => {
				const size = getSize(w.$);
				return {
					xMin: acc.xMin > size.x ? size.x : acc.xMin,
					xMax: acc.xMax < size.x + size.w ? size.x + size.w : acc.xMax,
					yMin: acc.yMin > size.y ? size.y : acc.yMin,
					yMax: acc.yMax < size.y + size.h ? size.y + size.h : acc.yMax,
				};
			},
			{ xMin: 999999999, yMin: 999999999, xMax: 0, yMax: 0 }
		);

		return {
			topWords: getTopWords(words)
				.map(w => w[0])
				.slice(0, numTopWords),
			Strings: words.length,
			TextLines: lines.length,
			arithmeticMeanStrinsPerLine: (lines.reduce((acc, l) => acc + (l.String || []).length, 0) / lines.length).toFixed(4),
			arithmeticMeanLineLengthInMm: px2mm(lines.reduce((acc, l) => acc + getSize(l.$).w, 0) / lines.length),
			blocks: blocks.length,
			arithmeticMeanLinesPerBlock: (blocks.reduce((acc, l) => acc + (l.TextLine || []).length, 0) / blocks.length).toFixed(4),
			sizeInPx: minMax,
			sizeInMm: {
				width: px2mm(minMax.xMax - minMax.xMin),
				height: px2mm(minMax.yMax - minMax.yMin),
			},
			absoluteSizeInPx: {
				width: minMax.xMax - minMax.xMin,
				heigth: minMax.yMax - minMax.yMin,
			},
			arithmeticMeanWC: (words.reduce((acc, w) => acc + parseFloat(w.$.WC), 0) / words.length).toFixed(5),
		};
		console.log('words', words.length);
		// console.log('words', words.map(w => w.$.CONTENT).join(' '));
	});
}

function getZipContent(zipFilePath) {
	const { size } = fs.statSync(zipFilePath);
	console.log('size', size);
	console.log('load file', zipFilePath);
	const zip = new AdmZip(zipFilePath);
	const zipEntries = zip.getEntries(); // an array of ZipEntry records
	const altoFiles = zipEntries.filter(zipEntry => /\d\.xml$/.test(zipEntry.entryName)).map(zipEntry => ({
		name: zipEntry.entryName,
		content: zipEntry.getData().toString('utf8'),
	}));
	const metsFile = zipEntries.find(zipEntry => /METS.xml$/i.test(zipEntry.entryName));
	return {
		zipFileName: path.basename(zipFilePath, path.extname(zipFilePath)),
		altoFiles,
		metsFile: {
			name: metsFile.entryName,
			content: metsFile.getData().toString('utf8'),
		},
	};
}

// function ensurePath(...path) {
// }

async function parseZipContent(file, year) {
	const dataMets = await parseMETS(file.metsFile.content);
	const pageStats = await Promise.all(file.altoFiles.map(({ content }) => parseAlto(content))).then(altos =>
		altos.map((res, index) => ({ ...res, fileName: file.altoFiles[index].name }))
	);
	const fileStats = {
		...dataMets,
		metsFileName: file.metsFile.name,
		zipFileName: file.zipFileName,
		pages: pageStats,
	};
	if (!fs.existsSync(statsPath)) fs.mkdirSync(statsPath);
	const yearPath = path.join(statsPath, year);
	if (!fs.existsSync(yearPath)) fs.mkdirSync(yearPath);
	const statsFilePath = path.join(yearPath, `${file.zipFileName}.json`);
	console.log('statsFileName', statsFilePath);
	fs.writeFileSync(statsFilePath, JSON.stringify(fileStats, null, 2));
	return fileStats;
}

function copyFile(source, target) {
	const rd = fs.createReadStream(source);
	rd.pipe(fs.createWriteStream(target));
}

async function execSync(promises) {
	const out = [];
	for (const promise of promises) {
		try {
			out.push(await promise);
		} catch (error) {
			console.log('error: ', error);
		}
	}
	return out;
}

async function addToIndex() {
	const dataPath = path.join(__dirname, 'stats-index.json');
	// const filesPath = path.join('data', 'files');
	let oldData = {};
	try {
		oldData = require(dataPath);
	} catch (e) {
		process.stderr.write(`${dataPath} was empty, initialized with {}\n`);
	}

	// statsPath
	const files = fs.readdirSync(filesPath).reduce(
		(acc, year) => [
			...acc,
			...fs
				.readdirSync(path.join(filesPath, year))
				.filter(file => !(file in oldData))
				.map(file => [year, path.join(filesPath, year, file)]),
		],
		[]
	);
	const newData = Object.assign({}, oldData);
	for (const [year, filePath] of files) {
		try {
			newData[filePath] = await parseZipContent(getZipContent(filePath), year);
		} catch (error) {
			console.log('error: ', error);
		}
		console.log('done');
	}
	console.log('newData', newData);
	fs.writeFileSync(dataPath, JSON.stringify(newData, null, 2));
}

async function runCui() {
	const actions = [
		{
			name: 'Parse a file',
			async action() {
				const years = fs.readdirSync(`${filesPath}/`);
				// console.log('years', years);
				// const { year } = await inquirer.prompt([
				// 	{
				// 		type: 'checkbox-autocomplete',
				// 		name: 'year',
				// 		asyncSource: async (answers, input) => fuzzy.filter(input || '', years).map(el => el.original),
				// 		message: 'Please select a year:',
				// 		validate(answer) {
				// 			if (answer.length !== 1) return 'You must choose one year.';
				// 			return true;
				// 		},
				// 	},
				// ]);
				const files = years.reduce((allFiles, year) => {
					const filesInFolder = fs.readdirSync(`${filesPath}/${year}/`);
					return allFiles.concat(filesInFolder.map(file => path.join(year, file)));
				}, []);
				const { fileList } = await inquirer.prompt([
					{
						type: 'checkbox-autocomplete',
						name: 'fileList',
						asyncSource: async (answers, input) => fuzzy.filter(input || '', files).map(el => el.original),
						message: 'Please select one file:',
						validate(answer) {
							if (answer.length < 1) return 'You must choose at least one file.';
							return true;
						},
					},
				]);
				console.log('fileList', fileList);
				return Promise.all(
					fileList.map(file =>
						parseZipContent(getZipContent(path.join(filesPath, file)), file.split('/')[0])
							.then(res => {} /*console.log(JSON.stringify(res, null, 2))*/)
							.catch(e => console.warn(e))
					)
				);
			},
		},
		{
			name: 'Extend index with downloaded Files',
			action: () => addToIndex(),
		},
	];
	const { startAction } = await inquirer.prompt([
		{
			type: 'list',
			message: 'What do you want to do?',
			name: 'startAction',
			choices: actions,
		},
	]);
	const { action } = actions.find(({ name }) => name === startAction);
	if (action) action().then();
}

if (require.main === module) {
	runCui().then(() => console.log('The End'));
}

module.exports = {
	runCui,
	parseZipContent,
	getZipContent,
};
<|endoftext|>


<commit_before>// Google Maps specific variable declaration
var geocoder;
var autocomplete;

// Variables for 'autocomplete' on the start and end location text boxes
var startLocation;
var destinationLocation;

// Map object storage variable
var map;

// Store the travel type
var travelType = "driving";

/* FOR POPUP PAGE */
// Variables of the current location set by asynchronous method
var currentLocationName;
var currentLocationLat;
var currentLocationLng;
var addedAttractionsArray = new Array();

function initialize() {
	//Map Specifications
	var mapProp = {
		center:new google.maps.LatLng(-24.994167,134.866944),
		zoom:5,
		mapTypeId:google.maps.MapTypeId.ROADMAP
	};

	//Add Map to Div
	map=new google.maps.Map(document.getElementById("map-canvas"),mapProp);

	//Initialise Geocoder object
	geocoder = new google.maps.Geocoder();

	// Initialising the autocomplete objects, restricting the search
	// to geographical location types.
	startLocationAutocomplete = new google.maps.places.Autocomplete((document.getElementById('start-location')),{ types: ['geocode'] });
	destinationLocationAutocomplete = new google.maps.places.Autocomplete((document.getElementById('end-location')),{ types: ['geocode'] });

	// When the user selects an address from the dropdown,
	// populate the address fields in the form.
	// start location autocomplete event
	google.maps.event.addListener(startLocationAutocomplete, 'place_changed', function() {
		geocodeAddress(document.getElementById('start-location').value, 1);
	});

	// destination location autocomplete event
	google.maps.event.addListener(destinationLocationAutocomplete, 'place_changed', function() {
		geocodeAddress(document.getElementById('end-location').value, 2);
	});

	//------------------------------------------------------------------------------------------------------------------------
	//POPUPWINDOW
	//------------------------------------------------------------------------------------------------------------------------

	//POPUPWINDOW: Initialise date picker for departure time
	if (!Modernizr.inputtypes.datetimelocal || navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
		$("#departure-time").datetimepicker();
	} else {
		// Remove the button and place the input field in the parent
		// CSS breaks if the button is removed without reordering DOM
		$("#departure-time>span").remove();
		$("#departure-time>input").attr("type", "datetime-local");
		$("#departure-time>input").appendTo($("#departure-time").parent());
	}

	//POPUPWINDOW: Autocomplete search filtering conditions set to only establishments within australia
	var options = {
		types: ['establishment'],
		componentRestrictions: {country: "aus"}
	};

	//POPUPWINDOW: Textfield input
	var input = document.getElementById('attraction-location');

	// Initialising the autocomplete objects, restricting the search
	// to geographical location types.
	startLocationAutocomplete = new google.maps.places.Autocomplete(input,options);

	// When the user selects an address from the dropdown,
	// populate the address fields in the form.
	//start location autocomplete event
	google.maps.event.addListener(startLocationAutocomplete, 'place_changed', function() {

		currentLocationName = document.getElementById('attraction-location').value;
		geocodeAddress(document.getElementById('attraction-location').value, 3);
	});

}

//Takes the entered address and set the start and end location latitude and longitude
//The assign variable being a 1 or a 2 determines whether the locations is the start or destination
function geocodeAddress(location, assign) {
	geocoder.geocode( { 'address': location}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {
			//global variable assignment
			if(assign == 1){
				startLocation = results[0].geometry.location;
			} else if(assign == 2){
				destinationLocation = results[0].geometry.location;
			} else if(assign == 3){
			currentLocationLat = results[0].geometry.location.lat();
			currentLocationLng = results[0].geometry.location.lng();

			}

		} else {
			alert('Geocode was not successful for the following reason: ' + status);
		}
	});
}

function addAttraction()
{
	var location = {name:currentLocationName, lat:currentLocationLat, lng:currentLocationLng};
	addedAttractionsArray.push(location);

	//Clears the attractions auto complete box
	document.getElementById("attraction-location").value = '';

	generateTable();
}

function generateTable()
{
	var table = document.getElementById("myTable");

	//DELETE ENTIRE TABLE - Very crude deletion method
	table.innerHTML = "";

	for (i = 0; i < addedAttractionsArray.length; i++)
	{
		var row = table.insertRow(-1);

		var locationNameCell = row.insertCell(0);
		var cell2 = row.insertCell(1);
		var cell3 = row.insertCell(2);

		locationNameCell.innerHTML = addedAttractionsArray[i].name;
		cell2.innerHTML = "[Time/ Distance this destination adds to the trip]";
		cell3.innerHTML = "<input type='checkbox'>";
	}
}

//(!BUG!) WITHIN METHOD NOT ALOT SELECTED ATTRACTIONS ARE DELETED
function deleteAttraction()
{
	var table=document.getElementById("myTable");
	var rowCount=table.rows.length;

	for(var i=0;i<rowCount;i++)
	{
		var chkbox = document.getElementById("myTable").rows[i].cells[2].childNodes[0];

		if(null!=chkbox&&true==chkbox.checked)
		{
			//Deletes an array entry - (Splice Method Supported by all browsers???)
			addedAttractionsArray.splice(i, 1)
		}

	}
	generateTable();
}

function calculateRoute(){
	//alert("Start Location: " + startLocation + "\nEnd Location: " + destinationLocation);

	console.log("Is " + travelType);

	if(travelType == "driving")
	{

	}
	else if(strUser == "walking")
	{

	}
	else if(strUser == "cycling")
	{

	}
	else if(strUser == "transit")
	{

	}
}

function setTravelType(originElement, newTravelType) {
	travelType = newTravelType;
	$("#travel-mode>.btn-group").each(function() {
		$(this).children().first().attr("class", "btn btn-default");
	});
	$(originElement).attr("class", "btn btn-success");
}

$(document).on('click touchend', '#select-attractions-button', function(){
	$('#overlay').fadeIn(500, function() {
		$('#popup').slideDown();
	});
});
$(document).on('click touchend', '#close-popup', function(){
	$('#popup').slideUp(500, function() {
		$('#overlay').fadeOut();
	});
});<commit_msg>fucking modernizr documentation<commit_after>// Google Maps specific variable declaration
var geocoder;
var autocomplete;

// Variables for 'autocomplete' on the start and end location text boxes
var startLocation;
var destinationLocation;

// Map object storage variable
var map;

// Store the travel type
var travelType = "driving";

/* FOR POPUP PAGE */
// Variables of the current location set by asynchronous method
var currentLocationName;
var currentLocationLat;
var currentLocationLng;
var addedAttractionsArray = new Array();

function initialize() {
	//Map Specifications
	var mapProp = {
		center:new google.maps.LatLng(-24.994167,134.866944),
		zoom:5,
		mapTypeId:google.maps.MapTypeId.ROADMAP
	};

	//Add Map to Div
	map=new google.maps.Map(document.getElementById("map-canvas"),mapProp);

	//Initialise Geocoder object
	geocoder = new google.maps.Geocoder();

	// Initialising the autocomplete objects, restricting the search
	// to geographical location types.
	startLocationAutocomplete = new google.maps.places.Autocomplete((document.getElementById('start-location')),{ types: ['geocode'] });
	destinationLocationAutocomplete = new google.maps.places.Autocomplete((document.getElementById('end-location')),{ types: ['geocode'] });

	// When the user selects an address from the dropdown,
	// populate the address fields in the form.
	// start location autocomplete event
	google.maps.event.addListener(startLocationAutocomplete, 'place_changed', function() {
		geocodeAddress(document.getElementById('start-location').value, 1);
	});

	// destination location autocomplete event
	google.maps.event.addListener(destinationLocationAutocomplete, 'place_changed', function() {
		geocodeAddress(document.getElementById('end-location').value, 2);
	});

	//------------------------------------------------------------------------------------------------------------------------
	//POPUPWINDOW
	//------------------------------------------------------------------------------------------------------------------------

	//POPUPWINDOW: Initialise date picker for departure time
	if (!Modernizr.inputtypes['datetime-local']  || navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
		$("#departure-time").datetimepicker();
	} else {
		// Remove the button and place the input field in the parent
		// CSS breaks if the button is removed without reordering DOM
		$("#departure-time>span").remove();
		$("#departure-time>input").attr("type", "datetime-local");
		$("#departure-time>input").appendTo($("#departure-time").parent());
	}

	//POPUPWINDOW: Autocomplete search filtering conditions set to only establishments within australia
	var options = {
		types: ['establishment'],
		componentRestrictions: {country: "aus"}
	};

	//POPUPWINDOW: Textfield input
	var input = document.getElementById('attraction-location');

	// Initialising the autocomplete objects, restricting the search
	// to geographical location types.
	startLocationAutocomplete = new google.maps.places.Autocomplete(input,options);

	// When the user selects an address from the dropdown,
	// populate the address fields in the form.
	//start location autocomplete event
	google.maps.event.addListener(startLocationAutocomplete, 'place_changed', function() {

		currentLocationName = document.getElementById('attraction-location').value;
		geocodeAddress(document.getElementById('attraction-location').value, 3);
	});

}

//Takes the entered address and set the start and end location latitude and longitude
//The assign variable being a 1 or a 2 determines whether the locations is the start or destination
function geocodeAddress(location, assign) {
	geocoder.geocode( { 'address': location}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {
			//global variable assignment
			if(assign == 1){
				startLocation = results[0].geometry.location;
			} else if(assign == 2){
				destinationLocation = results[0].geometry.location;
			} else if(assign == 3){
			currentLocationLat = results[0].geometry.location.lat();
			currentLocationLng = results[0].geometry.location.lng();

			}

		} else {
			alert('Geocode was not successful for the following reason: ' + status);
		}
	});
}

function addAttraction()
{
	var location = {name:currentLocationName, lat:currentLocationLat, lng:currentLocationLng};
	addedAttractionsArray.push(location);

	//Clears the attractions auto complete box
	document.getElementById("attraction-location").value = '';

	generateTable();
}

function generateTable()
{
	var table = document.getElementById("myTable");

	//DELETE ENTIRE TABLE - Very crude deletion method
	table.innerHTML = "";

	for (i = 0; i < addedAttractionsArray.length; i++)
	{
		var row = table.insertRow(-1);

		var locationNameCell = row.insertCell(0);
		var cell2 = row.insertCell(1);
		var cell3 = row.insertCell(2);

		locationNameCell.innerHTML = addedAttractionsArray[i].name;
		cell2.innerHTML = "[Time/ Distance this destination adds to the trip]";
		cell3.innerHTML = "<input type='checkbox'>";
	}
}

//(!BUG!) WITHIN METHOD NOT ALOT SELECTED ATTRACTIONS ARE DELETED
function deleteAttraction()
{
	var table=document.getElementById("myTable");
	var rowCount=table.rows.length;

	for(var i=0;i<rowCount;i++)
	{
		var chkbox = document.getElementById("myTable").rows[i].cells[2].childNodes[0];

		if(null!=chkbox&&true==chkbox.checked)
		{
			//Deletes an array entry - (Splice Method Supported by all browsers???)
			addedAttractionsArray.splice(i, 1)
		}

	}
	generateTable();
}

function calculateRoute(){
	//alert("Start Location: " + startLocation + "\nEnd Location: " + destinationLocation);

	console.log("Is " + travelType);

	if(travelType == "driving")
	{

	}
	else if(strUser == "walking")
	{

	}
	else if(strUser == "cycling")
	{

	}
	else if(strUser == "transit")
	{

	}
}

function setTravelType(originElement, newTravelType) {
	travelType = newTravelType;
	$("#travel-mode>.btn-group").each(function() {
		$(this).children().first().attr("class", "btn btn-default");
	});
	$(originElement).attr("class", "btn btn-success");
}

$(document).on('click touchend', '#select-attractions-button', function(){
	$('#overlay').fadeIn(500, function() {
		$('#popup').slideDown();
	});
});
$(document).on('click touchend', '#close-popup', function(){
	$('#popup').slideUp(500, function() {
		$('#overlay').fadeOut();
	});
});<|endoftext|>


<commit_before>/* global window, document, chrome, location, history, top */

let collapsers, jsonObject, jsonSelector, jsonPath, selectedListItem, hoveredListItem, originalBody;
chrome.runtime.onMessage.addListener(message => {
	if (message.copyText) {
		copyText(message.value);
	}
});
if (document.body && (document.body.childNodes[0] && document.body.childNodes[0].tagName == "PRE" || document.body.children.length == 0)) {
	const child = document.body.children.length ? document.body.childNodes[0] : document.body;
	const data = extractData(child.innerText);
	if (data) {
		originalBody = document.body.cloneNode(true);
		chrome.runtime.sendMessage({ init: true }, options => processData(data, options));
	}
}

function extractData(rawText) {
	const text = rawText.trim();
	let tokens;
	if (test(text)) {
		return {
			text: rawText,
			offset: 0
		};
	} else {
		tokens = text.match(/^([^\s(]*)\s*\(([\s\S]*)\)\s*;?$/);
		if (tokens && tokens[1] && tokens[2]) {
			if (test(tokens[2].trim())) {
				return {
					fnName: tokens[1],
					text: tokens[2],
					offset: rawText.indexOf(tokens[2])
				};
			}
		}
	}

	function test(text) {
		return ((text.charAt(0) == "[" && text.charAt(text.length - 1) == "]") || (text.charAt(0) == "{" && text.charAt(text.length - 1) == "}"));
	}
}

function processData(data, options) {
	if ((window == top || options.injectInFrame) && data && data.text) {
		const json = data.text;
		chrome.runtime.sendMessage({
			jsonToHTML: true,
			json,
			fnName: data.fnName,
			offset: data.offset
		}, result => {
			if (result.html) {
				displayUI(result.theme, result.html);
			}
			if (result.error) {
				displayError(result.error, result.loc, result.offset);
			}
		});
		try {
			jsonObject = JSON.parse(json);
		} catch (error) {
			// ignored
		}
	}
}

function displayError(error, loc, offset) {
	const link = document.createElement("link");
	const pre = document.body.firstChild.firstChild;
	const text = pre.textContent.substring(offset);
	const range = document.createRange();
	const imgError = document.createElement("img");
	const content = document.createElement("div");
	const errorPosition = document.createElement("span");
	const container = document.createElement("div");
	const closeButton = document.createElement("div");
	const ranges = [];
	let startRange = 0, indexRange = 0, endRange;
	link.rel = "stylesheet";
	link.type = "text/css";
	link.href = chrome.runtime.getURL("css/content-error.css");
	document.head.appendChild(link);
	while (indexRange != -1) {
		indexRange = text.indexOf("\n", startRange);
		ranges.push(startRange);
		startRange = indexRange + 1;
	}
	startRange = ranges[loc.first_line - 1] + loc.first_column + offset;
	endRange = ranges[loc.last_line - 1] + loc.last_column + offset;
	range.setStart(pre, startRange);
	if (startRange == endRange - 1) {
		range.setEnd(pre, startRange);
	} else {
		range.setEnd(pre, endRange);
	}
	errorPosition.className = "error-position";
	errorPosition.id = "error-position";
	range.surroundContents(errorPosition);
	imgError.src = chrome.runtime.getURL("resources/error-icon.gif");
	errorPosition.insertBefore(imgError, errorPosition.firstChild);
	content.className = "content";
	closeButton.className = "close-error";
	closeButton.onclick = () => content.parentElement.removeChild(content);
	content.textContent = error;
	content.appendChild(closeButton);
	container.className = "container";
	container.appendChild(content);
	errorPosition.parentNode.insertBefore(container, errorPosition.nextSibling);
	location.hash = "error-position";
	history.replaceState({}, "", "#");
}

function displayUI(theme, html) {
	const baseStyleElement = document.createElement("link");
	baseStyleElement.rel = "stylesheet";
	baseStyleElement.type = "text/css";
	baseStyleElement.href = chrome.runtime.getURL("css/jsonvue-core.css");
	document.head.appendChild(baseStyleElement);
	const userStyleElement = document.createElement("style");
	userStyleElement.appendChild(document.createTextNode(theme));
	document.head.appendChild(userStyleElement);
	document.body.innerHTML = html;
	collapsers = document.querySelectorAll("#json .collapsible .collapsible");
	const statusElement = document.createElement("div");
	statusElement.className = "status";
	const copyPathElement = document.createElement("div");
	copyPathElement.className = "copy-path";
	statusElement.appendChild(copyPathElement);
	document.body.appendChild(statusElement);
	const toolboxElement = document.createElement("div");
	toolboxElement.className = "toolbox";
	const expandElement = document.createElement("span");
	expandElement.title = "expand all";
	expandElement.innerText = "+";
	const viewSourceElement = document.createElement("a");
	viewSourceElement.title = "view source";
	viewSourceElement.innerText = "view source";
	const reduceElement = document.createElement("span");
	reduceElement.title = "reduce all";
	reduceElement.innerText = "-";
	toolboxElement.appendChild(expandElement);
	toolboxElement.appendChild(viewSourceElement);
	toolboxElement.appendChild(reduceElement);
	document.body.appendChild(toolboxElement);
	document.body.addEventListener("click", onToggle, false);
	document.body.addEventListener("mouseover", onMouseMove, false);
	document.body.addEventListener("click", onMouseClick, false);
	document.body.addEventListener("contextmenu", onContextMenu, false);
	expandElement.addEventListener("click", onExpandAll, false);
	viewSourceElement.addEventListener("click", onViewSource, false);
	reduceElement.addEventListener("click", onCollapseAll, false);
	copyPathElement.addEventListener("click", event => {
		if (event.isTrusted) {
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: statusElement.innerText
			});
		}
	}, false);
}

function onToggle(event) {
	const target = event.target;
	if (event.target.className == "collapser") {
		const collapsed = target.parentNode.getElementsByClassName("collapsible")[0];
		if (collapsed.parentNode.classList.contains("collapsed")) {
			collapsed.parentNode.classList.remove("collapsed");
		} else {
			collapsed.parentNode.classList.add("collapsed");
		}
	}
}

function onExpandAll() {
	collapserElements.forEach(collapsed => collapsed.parentNode.classList.remove("collapsed"));
}

function onCollapseAll() {
	collapserElements.forEach(collapsed => collapsed.parentNode.classList.add("collapsed"));
}

function onViewSource() {
	document.body.replaceWith(originalBody);
}

function onMouseMove(event) {
	if (event.isTrusted) {
		const statusElement = document.querySelector(".status");
		jsonPath = "";
		let element = getParentListItem(event.target);
		if (element) {
			jsonSelector = [];
			if (hoveredListItem) {
				hoveredListItem.firstChild.classList.remove("hovered");
			}
			hoveredListItem = element;
			element.firstChild.classList.add("hovered");
			do {
				if (element.parentNode.classList.contains("array")) {
					const index = [].indexOf.call(element.parentNode.children, element);
					jsonPath = "[" + index + "]" + jsonPath;
					jsonSelector.unshift(index);
				}
				if (element.parentNode.classList.contains("obj")) {
					const key = element.firstChild.firstChild.innerText;
					jsonPath = "." + key + jsonPath;
					jsonSelector.unshift(key);
				}
				element = element.parentNode.parentNode.parentNode;
			} while (element.tagName == "LI");
			if (jsonPath.charAt(0) == ".") {
				jsonPath = jsonPath.substring(1);
			}
			statusElement.innerText = jsonPath;
		} else if (hoveredListItem) {
			hoveredListItem.firstChild.classList.remove("hovered");
			hoveredListItem = null;
			statusElement.innerText = "";
			jsonSelector = [];
		}
	}
}

function onMouseClick(event) {
	if (selectedListItem) {
		selectedListItem.firstChild.classList.remove("selected");
	}
	selectedListItem = getParentListItem(event.target);
	if (selectedListItem) {
		selectedListItem.firstChild.classList.add("selected");
	}
}

function onContextMenu(event) {
	if (event.isTrusted) {
		const currentListItem = getParentListItem(event.target);
		if (currentListItem) {
			let value = jsonObject;
			jsonSelector.forEach(propertyName => value = value[propertyName]);
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: jsonPath,
				value: typeof value == "object" ? JSON.stringify(value) : value
			});
		} else {
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: "",
				value: typeof jsonObject == "object" ? JSON.stringify(jsonObject) : jsonObject
			});
		}
	}
}

function getParentListItem(element) {
	if (element.tagName != "LI") {
		while (element && element.tagName != "LI") {
			element = element.parentNode;
		}
	}
	if (element && element.tagName == "LI") {
		return element;
	}
}

function copyText(value) {
	const command = "copy";
	document.addEventListener(command, listener);
	document.execCommand(command);
	document.removeEventListener(command, listener);

	function listener(event) {
		event.clipboardData.setData("text/plain", value);
		event.preventDefault();
	}
}<commit_msg>move const declarations<commit_after>/* global window, document, chrome, location, history, top */

let collapserElements, jsonObject, jsonSelector, jsonPath, selectedListItem, hoveredListItem, originalBody;
chrome.runtime.onMessage.addListener(message => {
	if (message.copyText) {
		copyText(message.value);
	}
});
if (document.body && (document.body.childNodes[0] && document.body.childNodes[0].tagName == "PRE" || document.body.children.length == 0)) {
	const child = document.body.children.length ? document.body.childNodes[0] : document.body;
	const data = extractData(child.innerText);
	if (data) {
		originalBody = document.body.cloneNode(true);
		chrome.runtime.sendMessage({ init: true }, options => processData(data, options));
	}
}

function extractData(rawText) {
	const text = rawText.trim();
	let tokens;
	if (test(text)) {
		return {
			text: rawText,
			offset: 0
		};
	} else {
		tokens = text.match(/^([^\s(]*)\s*\(([\s\S]*)\)\s*;?$/);
		if (tokens && tokens[1] && tokens[2]) {
			if (test(tokens[2].trim())) {
				return {
					fnName: tokens[1],
					text: tokens[2],
					offset: rawText.indexOf(tokens[2])
				};
			}
		}
	}

	function test(text) {
		return ((text.charAt(0) == "[" && text.charAt(text.length - 1) == "]") || (text.charAt(0) == "{" && text.charAt(text.length - 1) == "}"));
	}
}

function processData(data, options) {
	if ((window == top || options.injectInFrame) && data && data.text) {
		const json = data.text;
		chrome.runtime.sendMessage({
			jsonToHTML: true,
			json,
			fnName: data.fnName,
			offset: data.offset
		}, result => {
			if (result.html) {
				displayUI(result.theme, result.html);
			}
			if (result.error) {
				displayError(result.error, result.loc, result.offset);
			}
		});
		try {
			jsonObject = JSON.parse(json);
		} catch (error) {
			// ignored
		}
	}
}

function displayError(error, loc, offset) {
	const link = document.createElement("link");
	const pre = document.body.firstChild.firstChild;
	const text = pre.textContent.substring(offset);
	const range = document.createRange();
	const imgError = document.createElement("img");
	const content = document.createElement("div");
	const errorPosition = document.createElement("span");
	const container = document.createElement("div");
	const closeButton = document.createElement("div");
	const ranges = [];
	let startRange = 0, indexRange = 0;
	link.rel = "stylesheet";
	link.type = "text/css";
	link.href = chrome.runtime.getURL("css/content-error.css");
	document.head.appendChild(link);
	while (indexRange != -1) {
		indexRange = text.indexOf("\n", startRange);
		ranges.push(startRange);
		startRange = indexRange + 1;
	}
	startRange = ranges[loc.first_line - 1] + loc.first_column + offset;
	const endRange = ranges[loc.last_line - 1] + loc.last_column + offset;
	range.setStart(pre, startRange);
	if (startRange == endRange - 1) {
		range.setEnd(pre, startRange);
	} else {
		range.setEnd(pre, endRange);
	}
	errorPosition.className = "error-position";
	errorPosition.id = "error-position";
	range.surroundContents(errorPosition);
	imgError.src = chrome.runtime.getURL("resources/error-icon.gif");
	errorPosition.insertBefore(imgError, errorPosition.firstChild);
	content.className = "content";
	closeButton.className = "close-error";
	closeButton.onclick = () => content.parentElement.removeChild(content);
	content.textContent = error;
	content.appendChild(closeButton);
	container.className = "container";
	container.appendChild(content);
	errorPosition.parentNode.insertBefore(container, errorPosition.nextSibling);
	location.hash = "error-position";
	history.replaceState({}, "", "#");
}

function displayUI(theme, html) {
	const baseStyleElement = document.createElement("link");
	const userStyleElement = document.createElement("style");
	const statusElement = document.createElement("div");
	const copyPathElement = document.createElement("div");
	const toolboxElement = document.createElement("div");
	const expandElement = document.createElement("span");
	const viewSourceElement = document.createElement("a");
	const reduceElement = document.createElement("span");
	baseStyleElement.rel = "stylesheet";
	baseStyleElement.type = "text/css";
	baseStyleElement.href = chrome.runtime.getURL("css/jsonvue-core.css");
	document.head.appendChild(baseStyleElement);
	userStyleElement.appendChild(document.createTextNode(theme));
	document.head.appendChild(userStyleElement);
	document.body.innerHTML = html;
	collapserElements = document.querySelectorAll("#json .collapsible .collapsible");
	statusElement.className = "status";
	copyPathElement.className = "copy-path";
	statusElement.appendChild(copyPathElement);
	document.body.appendChild(statusElement);
	toolboxElement.className = "toolbox";
	expandElement.title = "expand all";
	expandElement.innerText = "+";
	viewSourceElement.title = "view source";
	viewSourceElement.innerText = "view source";
	reduceElement.title = "reduce all";
	reduceElement.innerText = "-";
	toolboxElement.appendChild(expandElement);
	toolboxElement.appendChild(viewSourceElement);
	toolboxElement.appendChild(reduceElement);
	document.body.appendChild(toolboxElement);
	document.body.addEventListener("click", onToggle, false);
	document.body.addEventListener("mouseover", onMouseMove, false);
	document.body.addEventListener("click", onMouseClick, false);
	document.body.addEventListener("contextmenu", onContextMenu, false);
	expandElement.addEventListener("click", onExpandAll, false);
	viewSourceElement.addEventListener("click", onViewSource, false);
	reduceElement.addEventListener("click", onCollapseAll, false);
	copyPathElement.addEventListener("click", event => {
		if (event.isTrusted) {
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: statusElement.innerText
			});
		}
	}, false);
}

function onToggle(event) {
	const target = event.target;
	if (event.target.className == "collapser") {
		const collapsed = target.parentNode.getElementsByClassName("collapsible")[0];
		if (collapsed.parentNode.classList.contains("collapsed")) {
			collapsed.parentNode.classList.remove("collapsed");
		} else {
			collapsed.parentNode.classList.add("collapsed");
		}
	}
}

function onExpandAll() {
	collapserElements.forEach(collapsed => collapsed.parentNode.classList.remove("collapsed"));
}

function onCollapseAll() {
	collapserElements.forEach(collapsed => collapsed.parentNode.classList.add("collapsed"));
}

function onViewSource() {
	document.body.replaceWith(originalBody);
}

function onMouseMove(event) {
	if (event.isTrusted) {
		const statusElement = document.querySelector(".status");
		jsonPath = "";
		let element = getParentListItem(event.target);
		if (element) {
			jsonSelector = [];
			if (hoveredListItem) {
				hoveredListItem.firstChild.classList.remove("hovered");
			}
			hoveredListItem = element;
			element.firstChild.classList.add("hovered");
			do {
				if (element.parentNode.classList.contains("array")) {
					const index = [].indexOf.call(element.parentNode.children, element);
					jsonPath = "[" + index + "]" + jsonPath;
					jsonSelector.unshift(index);
				}
				if (element.parentNode.classList.contains("obj")) {
					const key = element.firstChild.firstChild.innerText;
					jsonPath = "." + key + jsonPath;
					jsonSelector.unshift(key);
				}
				element = element.parentNode.parentNode.parentNode;
			} while (element.tagName == "LI");
			if (jsonPath.charAt(0) == ".") {
				jsonPath = jsonPath.substring(1);
			}
			statusElement.innerText = jsonPath;
		} else if (hoveredListItem) {
			hoveredListItem.firstChild.classList.remove("hovered");
			hoveredListItem = null;
			statusElement.innerText = "";
			jsonSelector = [];
		}
	}
}

function onMouseClick(event) {
	if (selectedListItem) {
		selectedListItem.firstChild.classList.remove("selected");
	}
	selectedListItem = getParentListItem(event.target);
	if (selectedListItem) {
		selectedListItem.firstChild.classList.add("selected");
	}
}

function onContextMenu(event) {
	if (event.isTrusted) {
		const currentListItem = getParentListItem(event.target);
		if (currentListItem) {
			let value = jsonObject;
			jsonSelector.forEach(propertyName => value = value[propertyName]);
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: jsonPath,
				value: typeof value == "object" ? JSON.stringify(value) : value
			});
		} else {
			chrome.runtime.sendMessage({
				copyPropertyPath: true,
				path: "",
				value: typeof jsonObject == "object" ? JSON.stringify(jsonObject) : jsonObject
			});
		}
	}
}

function getParentListItem(element) {
	if (element.tagName != "LI") {
		while (element && element.tagName != "LI") {
			element = element.parentNode;
		}
	}
	if (element && element.tagName == "LI") {
		return element;
	}
}

function copyText(value) {
	const command = "copy";
	document.addEventListener(command, listener);
	document.execCommand(command);
	document.removeEventListener(command, listener);

	function listener(event) {
		event.clipboardData.setData("text/plain", value);
		event.preventDefault();
	}
}<|endoftext|>


<commit_before>export default {
  BackgroundsPlus: require("./backgrounds-plus.jpg")
};
<commit_msg>Deploy to GitHub pages [ci skip]<commit_after>import React from "react";
import Image from "../../../components/Image"
export const BackgroundsPlus = p => <Image {...p} src={require("./backgrounds-plus.jpg")} /><|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado

/*
 * A bucket that can be used to store spherical objects.  Manages the addition
 * and removal of the spheres, and stacks them as the are added, and manages
 * the stack as spheres are removed.
 *
 * This expects the spheres to have certain properties, please inspect the
 * code to understand the "contract" between the bucket and the spheres.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Includes
  var Vector2 = require( 'DOT/Vector2' );
  var Inheritance = require( 'PHETCOMMON/util/Inheritance' );
  var Bucket = require( 'PHETCOMMON/model/Bucket' );

  function SphereBucket( options ) {
    Bucket.call( this, options );
    this.particleRadius = options.particleRadius || 10;
    this.yOffset = -this.particleRadius * 0.4; // Empirically determined, for positioning particles inside the bucket.
    this.particles = [];
  }

  // Inherit from base type.
  Inheritance.inheritPrototype( SphereBucket, Bucket );

  SphereBucket.prototype.addParticleFirstOpen = function( particle, animate ) {
    particle.destination = this.getFirstOpenLocation();
    if ( !animate ) {
      particle.position = particle.destination;
    }
    this.particles.push( particle );
    var thisBucket = this;
    particle.userControlledProperty.once( function( userControlled ) {
      if ( userControlled ) {
        thisBucket.removeParticle( particle );
      }
    } );
  };

  SphereBucket.prototype.addParticleNearestOpen = function( particle, animate ) {
    particle.destination = this.getNearestOpenLocation( particle.destination );
    if ( !animate ) {
      particle.position = particle.destination;
    }
    this.particles.push( particle );
    var thisBucket = this;
    particle.userControlledProperty.once( function( userControlled ) {
      if ( userControlled ) {
        thisBucket.removeParticle( particle );
      }
    } );
  };

  SphereBucket.prototype.removeParticle = function( particle ) {
    if ( this.particles.indexOf( particle ) === -1 ) {
      console.log( "Error: Attempt to remove particle not contained in bucket, ignoring." );
      return;
    }
    this.particles = _.without( this.particles, particle );
    this.relayoutBucketParticles();
  };

  SphereBucket.prototype.isPositionOpen = function( position ) {
    var positionOpen = true;
    for ( var i = 0; i < this.particles.length; i++ ) {
      var particle = this.particles[ i ];
      if ( particle.destination.equals( position ) ) {
        positionOpen = false;
        break;
      }
    }
    return positionOpen;
  };

  SphereBucket.prototype.getFirstOpenLocation = function() {
    var openLocation = Vector2.ZERO;
    var usableWidth = this.size.width - 2 * this.particleRadius;
    var offsetFromBucketEdge = this.particleRadius * 2;
    var numParticlesInLayer = Math.floor( usableWidth / ( this.particleRadius * 2 ) );
    var row = 0;
    var positionInLayer = 0;
    var found = false;
    while ( !found ) {
      var testLocation = new Vector2( this.position.x - this.size.width / 2 + offsetFromBucketEdge + positionInLayer * 2 * this.particleRadius,
        this.getYPositionForLayer( row ) );
      if ( this.isPositionOpen( testLocation ) ) {
        // We found a location that is open.
        openLocation = testLocation;
        found = true;
        continue;
      }
      else {
        positionInLayer++;
        if ( positionInLayer >= numParticlesInLayer ) {
          // Move to the next layer.
          row++;
          positionInLayer = 0;
          numParticlesInLayer--;
          offsetFromBucketEdge += this.particleRadius;
          if ( numParticlesInLayer === 0 ) {
            // This algorithm doesn't handle the situation where
            // more particles are added than can be stacked into
            // a pyramid of the needed size, but so far it hasn't
            // needed to.  If this requirement changes, the
            // algorithm will need to change too.
            numParticlesInLayer = 1;
            offsetFromBucketEdge -= this.particleRadius;
          }
        }
      }
    }
    return openLocation;
  };

  SphereBucket.prototype.getLayerForYPosition = function( yPosition ) {
    return Math.abs( Math.round( ( yPosition - ( this.position.y + this.yOffset ) ) / ( this.particleRadius * 2 * 0.866 ) ) );
  };

  /*
   * Get the nearest open location to the provided current location.  This
   * is used for particle stacking.
   *
   * @param location
   * @return
   */
  SphereBucket.prototype.getNearestOpenLocation = function( position ) {
    // Determine the highest occupied layer.  The bottom layer is 0.
    var highestOccupiedLayer = 0;
    var self = this;
    _.each( this.particles, function( particle ) {
      var layer = self.getLayerForYPosition( particle.destination.y );
      if ( layer > highestOccupiedLayer ) {
        highestOccupiedLayer = layer;
      }
    } );

    // Make a list of all open locations in the occupied layers.
    var openLocations = [];
    var placeableWidth = this.size.width - 2 * this.particleRadius;
    var offsetFromBucketEdge = ( this.size.width - placeableWidth ) / 2 + this.particleRadius;
    var numParticlesInLayer = Math.floor( placeableWidth / ( this.particleRadius * 2 ) );

    // Loop, searching for open positions in the particle stack.
    for ( var layer = 0; layer <= highestOccupiedLayer + 1; layer++ ) {

      // Add all open locations in the current layer.
      for ( var positionInLayer = 0; positionInLayer < numParticlesInLayer; positionInLayer++ ) {
        var testPosition = new Vector2( this.position.x - this.size.width / 2 + offsetFromBucketEdge + positionInLayer * 2 * this.particleRadius,
          this.getYPositionForLayer( layer ) );
        if ( this.isPositionOpen( testPosition ) ) {

          // We found a location that is unoccupied.
          if ( layer === 0 || this.countSupportingParticles( testPosition ) === 2 ) {
            // This is a valid open location.
            openLocations.push( testPosition );
          }
        }
      }

      // Adjust variables for the next layer.
      numParticlesInLayer--;
      offsetFromBucketEdge += this.particleRadius;
      if ( numParticlesInLayer === 0 ) {
        // If the stacking pyramid is full, meaning that there are
        // no locations that are open within it, this algorithm
        // classifies the locations directly above the top of the
        // pyramid as being open.  This would result in a stack
        // of particles with a pyramid base.  So far, this hasn't
        // been a problem, but this limitation may limit
        // reusability of this algorithm.
        numParticlesInLayer = 1;
        offsetFromBucketEdge -= this.particleRadius;
      }
    }

    // Find the closest open location to the provided current location.
    // Only the X-component is used for this determination, because if
    // the Y-component is used the particles often appear to fall sideways
    // when released above the bucket, which just looks weird.
    var closestOpenLocation = openLocations[0] || Vector2.ZERO;

    _.each( openLocations, function( location ) {
      if ( location.distance( position ) < closestOpenLocation.distance( position ) ) {
        // This location is closer.
        closestOpenLocation = location;
      }
    } );
    return closestOpenLocation;
  };

  SphereBucket.prototype.getYPositionForLayer = function( layer ) {
    return this.position.y + this.yOffset + layer * this.particleRadius * 2 * 0.866;
  };

  /*
   * Determine whether a particle is 'dangling', i.e. hanging above an open
   * space in the stack of particles.  Dangling particles should fall.
   */
  SphereBucket.prototype.isDangling = function( particle ) {
    var onBottomRow = particle.destination.y === this.position.y + this.yOffset;
    return !onBottomRow && this.countSupportingParticles( particle.destination ) < 2;
  };

  SphereBucket.prototype.countSupportingParticles = function( position ) {
    var count = 0;
    for ( var i = 0; i < this.particles.length; i++ ) {
      var p = this.particles[i];
      if ( p.destination.y < position.y && // Must be in a lower layer
           p.destination.distance( position ) < this.particleRadius * 3 ) {
        // Must be a supporting particle.
        count++;
      }
    }
    return count;
  };

  SphereBucket.prototype.relayoutBucketParticles = function() {
    var particleMoved;
    do {
      for ( var i = 0; i < this.particles.length; i++ ) {
        particleMoved = false;
        var particle = this.particles[i];
        if ( this.isDangling( particle ) ) {
          particle.destination = this.getNearestOpenLocation( particle.destination );
          particleMoved = true;
          break;
        }
      }
    } while ( particleMoved );
  };

  SphereBucket.prototype.containsParticle = function( particle ) {
    return this.particles.indexOf( particle ) !== -1;
  };

  return SphereBucket;
} );
<commit_msg>Started adding feature where user can get a particle by clicking on the bucket.<commit_after>// Copyright 2002-2013, University of Colorado

/*
 * A bucket that can be used to store spherical objects.  Manages the addition
 * and removal of the spheres, and stacks them as the are added, and manages
 * the stack as spheres are removed.
 *
 * This expects the spheres to have certain properties, please inspect the
 * code to understand the "contract" between the bucket and the spheres.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Includes
  var Vector2 = require( 'DOT/Vector2' );
  var Inheritance = require( 'PHETCOMMON/util/Inheritance' );
  var Bucket = require( 'PHETCOMMON/model/Bucket' );

  function SphereBucket( options ) {
    Bucket.call( this, options );
    this.sphereRadius = options.sphereRadius || 10;
    this.yOffset = -this.sphereRadius * 0.4; // Empirically determined, for positioning particles inside the bucket.
    this.particles = [];
  }

  // Inherit from base type.
  Inheritance.inheritPrototype( SphereBucket, Bucket );

  SphereBucket.prototype.addParticleFirstOpen = function( particle, animate ) {
    particle.destination = this._getFirstOpenLocation();
    if ( !animate ) {
      particle.position = particle.destination;
    }
    this.particles.push( particle );
    var thisBucket = this;
    particle.userControlledProperty.once( function( userControlled ) {
      if ( userControlled ) {
        thisBucket.removeParticle( particle );
      }
    } );
  };

  SphereBucket.prototype.addParticleNearestOpen = function( particle, animate ) {
    particle.destination = this.getNearestOpenLocation( particle.destination );
    if ( !animate ) {
      particle.position = particle.destination;
    }
    this.particles.push( particle );
    var thisBucket = this;
    particle.userControlledProperty.once( function( userControlled ) {
      if ( userControlled ) {
        thisBucket.removeParticle( particle );
      }
    } );
  };

  SphereBucket.prototype.removeParticle = function( particle ) {
    if ( this.particles.indexOf( particle ) === -1 ) {
      console.log( "Error: Attempt to remove particle not contained in bucket, ignoring." );
      return;
    }
    this.particles = _.without( this.particles, particle );
    this.relayoutBucketParticles();
  };

  SphereBucket.prototype.containsParticle = function( particle ) {
    return this.particles.indexOf( particle ) !== -1;
  };

  SphereBucket.prototype.extractClosestParticle = function( location ) {
    var closestParticle = null;
    this.particles.forEach( function( particle ){
      if ( closestParticle === null || closestParticle.position.distance( location ) > particle.position.distance( location )){
        closestParticle = particle;
      }
    });
    if ( closestParticle != null ){
      this.removeParticle( closestParticle );
    }
    return closestParticle;
  };

  SphereBucket.prototype._isPositionOpen = function( position ) {
    var positionOpen = true;
    for ( var i = 0; i < this.particles.length; i++ ) {
      var particle = this.particles[ i ];
      if ( particle.destination.equals( position ) ) {
        positionOpen = false;
        break;
      }
    }
    return positionOpen;
  };

  SphereBucket.prototype._getFirstOpenLocation = function() {
    var openLocation = Vector2.ZERO;
    var usableWidth = this.size.width - 2 * this.sphereRadius;
    var offsetFromBucketEdge = this.sphereRadius * 2;
    var numParticlesInLayer = Math.floor( usableWidth / ( this.sphereRadius * 2 ) );
    var row = 0;
    var positionInLayer = 0;
    var found = false;
    while ( !found ) {
      var testLocation = new Vector2( this.position.x - this.size.width / 2 + offsetFromBucketEdge + positionInLayer * 2 * this.sphereRadius,
        this._getYPositionForLayer( row ) );
      if ( this._isPositionOpen( testLocation ) ) {
        // We found a location that is open.
        openLocation = testLocation;
        found = true;
        continue;
      }
      else {
        positionInLayer++;
        if ( positionInLayer >= numParticlesInLayer ) {
          // Move to the next layer.
          row++;
          positionInLayer = 0;
          numParticlesInLayer--;
          offsetFromBucketEdge += this.sphereRadius;
          if ( numParticlesInLayer === 0 ) {
            // This algorithm doesn't handle the situation where
            // more particles are added than can be stacked into
            // a pyramid of the needed size, but so far it hasn't
            // needed to.  If this requirement changes, the
            // algorithm will need to change too.
            numParticlesInLayer = 1;
            offsetFromBucketEdge -= this.sphereRadius;
          }
        }
      }
    }
    return openLocation;
  };

  SphereBucket.prototype._getLayerForYPosition = function( yPosition ) {
    return Math.abs( Math.round( ( yPosition - ( this.position.y + this.yOffset ) ) / ( this.sphereRadius * 2 * 0.866 ) ) );
  };

  /*
   * Get the nearest open location to the provided current location.  This
   * is used for particle stacking.
   *
   * @param location
   * @return
   */
  SphereBucket.prototype.getNearestOpenLocation = function( position ) {
    // Determine the highest occupied layer.  The bottom layer is 0.
    var highestOccupiedLayer = 0;
    var self = this;
    _.each( this.particles, function( particle ) {
      var layer = self._getLayerForYPosition( particle.destination.y );
      if ( layer > highestOccupiedLayer ) {
        highestOccupiedLayer = layer;
      }
    } );

    // Make a list of all open locations in the occupied layers.
    var openLocations = [];
    var placeableWidth = this.size.width - 2 * this.sphereRadius;
    var offsetFromBucketEdge = ( this.size.width - placeableWidth ) / 2 + this.sphereRadius;
    var numParticlesInLayer = Math.floor( placeableWidth / ( this.sphereRadius * 2 ) );

    // Loop, searching for open positions in the particle stack.
    for ( var layer = 0; layer <= highestOccupiedLayer + 1; layer++ ) {

      // Add all open locations in the current layer.
      for ( var positionInLayer = 0; positionInLayer < numParticlesInLayer; positionInLayer++ ) {
        var testPosition = new Vector2( this.position.x - this.size.width / 2 + offsetFromBucketEdge + positionInLayer * 2 * this.sphereRadius,
          this._getYPositionForLayer( layer ) );
        if ( this._isPositionOpen( testPosition ) ) {

          // We found a location that is unoccupied.
          if ( layer === 0 || this.countSupportingParticles( testPosition ) === 2 ) {
            // This is a valid open location.
            openLocations.push( testPosition );
          }
        }
      }

      // Adjust variables for the next layer.
      numParticlesInLayer--;
      offsetFromBucketEdge += this.sphereRadius;
      if ( numParticlesInLayer === 0 ) {
        // If the stacking pyramid is full, meaning that there are
        // no locations that are open within it, this algorithm
        // classifies the locations directly above the top of the
        // pyramid as being open.  This would result in a stack
        // of particles with a pyramid base.  So far, this hasn't
        // been a problem, but this limitation may limit
        // reusability of this algorithm.
        numParticlesInLayer = 1;
        offsetFromBucketEdge -= this.sphereRadius;
      }
    }

    // Find the closest open location to the provided current location.
    // Only the X-component is used for this determination, because if
    // the Y-component is used the particles often appear to fall sideways
    // when released above the bucket, which just looks weird.
    var closestOpenLocation = openLocations[0] || Vector2.ZERO;

    _.each( openLocations, function( location ) {
      if ( location.distance( position ) < closestOpenLocation.distance( position ) ) {
        // This location is closer.
        closestOpenLocation = location;
      }
    } );
    return closestOpenLocation;
  };

  SphereBucket.prototype._getYPositionForLayer = function( layer ) {
    return this.position.y + this.yOffset + layer * this.sphereRadius * 2 * 0.866;
  };

  /*
   * Determine whether a particle is 'dangling', i.e. hanging above an open
   * space in the stack of particles.  Dangling particles should fall.
   */
  SphereBucket.prototype._isDangling = function( particle ) {
    var onBottomRow = particle.destination.y === this.position.y + this.yOffset;
    return !onBottomRow && this.countSupportingParticles( particle.destination ) < 2;
  };

  SphereBucket.prototype.countSupportingParticles = function( position ) {
    var count = 0;
    for ( var i = 0; i < this.particles.length; i++ ) {
      var p = this.particles[i];
      if ( p.destination.y < position.y && // Must be in a lower layer
           p.destination.distance( position ) < this.sphereRadius * 3 ) {
        // Must be a supporting particle.
        count++;
      }
    }
    return count;
  };

  SphereBucket.prototype.relayoutBucketParticles = function() {
    var particleMoved;
    do {
      for ( var i = 0; i < this.particles.length; i++ ) {
        particleMoved = false;
        var particle = this.particles[i];
        if ( this._isDangling( particle ) ) {
          particle.destination = this.getNearestOpenLocation( particle.destination );
          particleMoved = true;
          break;
        }
      }
    } while ( particleMoved );
  };

  return SphereBucket;
} );
<|endoftext|>


<commit_before>HandsomeTrello.helpers = {
  data: {
    lockCheckUpdateDOM: {}
  },

  getCookie: function (name) {
    var matches = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + '=([^;]*)'));

    return matches ? decodeURIComponent(matches[1]) : undefined;
  },

  initEvent: function (name, detail) {
    if (typeof detail === 'undefined') {
      detail = null;
    }

    document.dispatchEvent(
      new CustomEvent(name, {
        detail: detail
      })
    );
  },

  eventListener: function (name, callback) {
    document.addEventListener(name, callback);
  },

  goToLink: function (url) {
    var _link = document.createElement('a');

    _link.setAttribute('href', url);
    _link.classList.add('hide');

    document.body.appendChild(_link);

    _link.click();

    this.removeElement(_link);
  },

  generateDateString: function (date) {
    if (typeof date === 'string') {
      date = new Date(date);
    }

    return (
      date.toLocaleString('en-US', {
        month: 'short',
        day: 'numeric',
        year: 'numeric'
      }) +
      ' at ' +
      date.toLocaleString('en-US', {
        hour: 'numeric',
        minute: '2-digit'
      })
    );
  },

  generateGetParamsStringFromObject: function (object) {
    var stringData = '';

    for (var key in object) {
      if (stringData !== '') {
        stringData += '&';
      }
      stringData += key + '=' + encodeURIComponent(object[key]);
    }

    return stringData;
  },

  sortByProperty: function (array, property) {
    function getPropertyByPath(object) {
      return property.split('.').reduce(function (val, key) {
        return val[key];
      }, object);
    }

    array.sort(function (a, b) {
      return getPropertyByPath(a) - getPropertyByPath(b);
    });
  },

  isJSONString: function (string) {
    try {
      JSON.parse(string);
    } catch (e) {
      return false;
    }
    return true;
  },

  triggerResize: function () {
    window.dispatchEvent(new Event('resize'));
  },

  findParentByClass: function (_element, className) {
    if (typeof _element !== 'undefined') {
      while (typeof _element.parentElement !== 'undefined' && (_element = _element.parentElement) !== null) {
        if (_element.classList.contains(className)) {
          return _element;
        }
      }
    }

    return false;
  },

  getOffset: function (_element) {
    function getOffsetSum(_element) {
      var top = 0,
        left = 0;

      while (_element) {
        top = top + parseInt(_element.offsetTop);
        left = left + parseInt(_element.offsetLeft);

        _element = _element.offsetParent;
      }

      return {
        top: top,
        left: left
      };
    }

    function getOffsetRect(_element) {
      var box = _element.getBoundingClientRect(),
        body = document.body,
        docElem = document.documentElement,
        scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop,
        scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft,
        clientTop = docElem.clientTop || body.clientTop || 0,
        clientLeft = docElem.clientLeft || body.clientLeft || 0,
        top = box.top + scrollTop - clientTop,
        left = box.left + scrollLeft - clientLeft;

      return {
        top: Math.round(top),
        left: Math.round(left)
      };
    }

    if (_element.getBoundingClientRect) {
      return getOffsetRect(_element);
    } else {
      return getOffsetSum(_element);
    }
  },

  jsonToDOM: function (json, doc, nodes) {
    if (typeof doc === 'undefined') {
      doc = document;
    }

    if (typeof nodes === 'undefined') {
      nodes = {};
    }

    var namespaces = {
      html: 'http://www.w3.org/1999/xhtml',
      xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
    };
    var defaultNamespace = namespaces.html;

    function namespace(name) {
      var m = /^(?:(.*):)?(.*)$/.exec(name);
      return [namespaces[m[1]], m[2]];
    }

    function tag(name, attr) {
      if (Array.isArray(name)) {
        var frag = doc.createDocumentFragment();
        Array.prototype.forEach.call(arguments, function (arg) {
          if (!Array.isArray(arg[0]))
            frag.appendChild(tag.apply(null, arg));
          else
            arg.forEach(function (arg) {
              frag.appendChild(tag.apply(null, arg));
            });
        });
        return frag;
      }

      var args = Array.prototype.slice.call(arguments, 2);
      var vals = namespace(name);
      var elem = doc.createElementNS(vals[0] || defaultNamespace, vals[1]);

      for (var key in attr) {
        var val = attr[key];
        if (nodes && key == 'key')
          nodes[val] = elem;

        vals = namespace(key);
        if (typeof val == 'function')
          elem.addEventListener(key.replace(/^on/, ''), val, false);
        else
          elem.setAttributeNS(vals[0] || '', vals[1], val);
      }
      args.forEach(function (e) {
        try {
          elem.appendChild(
            Object.prototype.toString.call(e) == '[object Array]' ? tag.apply(null, e) :
              e instanceof doc.defaultView.Node ? e : doc.createTextNode(e)
          );
        } catch (ex) {
          elem.appendChild(doc.createTextNode(ex));
        }
      });

      return elem;
    }

    return tag.apply(null, json);
  },

  appendElementAfterAnother: function (_element, _beforeElement) {
    if (_beforeElement.nextSibling) {
      _beforeElement.parentNode.insertBefore(_element, _beforeElement.nextSibling);
    } else {
      _beforeElement.parentNode.appendChild(_element);
    }
  },

  appendElement: function (_element, _parentElement) {
    if (typeof _element === 'undefined') {
      return false;
    }

    _parentElement.appendChild(_element);
  },

  prependElement: function (_element, _parentElement) {
    if (typeof _element === 'undefined') {
      return false;
    }

    if (_parentElement.firstChild) {
      _parentElement.insertBefore(_element, _parentElement.firstChild);
    } else {
      _parentElement.appendChild(_element);
    }
  },

  removeElement: function (_element) {
    if (
      typeof _element !== 'undefined' &&
      typeof _element.parentElement !== 'undefined' &&
      typeof _element.parentElement.removeChild !== 'undefined'
    ) {
      this.lockDOM('removeElement', true);

      _element.parentElement.removeChild(_element);

      this.lockDOM('removeElement', false);
    }
  },

  getElementByProperty: function (object, property, value) {
    for (var key in object) {
      if (object[key][property] == value) {
        return object[key];
      }
    }

    return false;
  },

  checkLockedDOM: function () {
    return Object.keys(this.data.lockCheckUpdateDOM).length;
  },

  lockDOM: function (target, status) {
    var hasLockCheckUpdateDOM = typeof this.data.lockCheckUpdateDOM[target] !== 'undefined';

    if (status) {
      if (hasLockCheckUpdateDOM) {
        this.data.lockCheckUpdateDOM[target]++;
      } else {
        this.data.lockCheckUpdateDOM[target] = 1;
      }
    } else {
      if (hasLockCheckUpdateDOM) {
        this.data.lockCheckUpdateDOM[target]--;

        if (!this.data.lockCheckUpdateDOM[target]) {
          delete this.data.lockCheckUpdateDOM[target];
        }
      }
    }
  }
};
<commit_msg>Fixes by jshint x3<commit_after>HandsomeTrello.helpers = {
  data: {
    lockCheckUpdateDOM: {}
  },

  getCookie: function (name) {
    var matches = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + '=([^;]*)'));

    return matches ? decodeURIComponent(matches[1]) : undefined;
  },

  initEvent: function (name, detail) {
    if (typeof detail === 'undefined') {
      detail = null;
    }

    document.dispatchEvent(
      new CustomEvent(name, {
        detail: detail
      })
    );
  },

  eventListener: function (name, callback) {
    document.addEventListener(name, callback);
  },

  goToLink: function (url) {
    var _link = document.createElement('a');

    _link.setAttribute('href', url);
    _link.classList.add('hide');

    document.body.appendChild(_link);

    _link.click();

    this.removeElement(_link);
  },

  generateDateString: function (date) {
    if (typeof date === 'string') {
      date = new Date(date);
    }

    return (
      date.toLocaleString('en-US', {
        month: 'short',
        day: 'numeric',
        year: 'numeric'
      }) +
      ' at ' +
      date.toLocaleString('en-US', {
        hour: 'numeric',
        minute: '2-digit'
      })
    );
  },

  generateGetParamsStringFromObject: function (object) {
    var stringData = '';

    for (var key in object) {
      if (object.hasOwnProperty(key)) {
        if (stringData !== '') {
          stringData += '&';
        }

        stringData += key + '=' + encodeURIComponent(object[key]);
      }
    }

    return stringData;
  },

  sortByProperty: function (array, property) {
    function getPropertyByPath(object) {
      return property.split('.').reduce(function (val, key) {
        return val[key];
      }, object);
    }

    array.sort(function (a, b) {
      return getPropertyByPath(a) - getPropertyByPath(b);
    });
  },

  isJSONString: function (string) {
    try {
      JSON.parse(string);
    } catch (e) {
      return false;
    }
    return true;
  },

  triggerResize: function () {
    window.dispatchEvent(new Event('resize'));
  },

  findParentByClass: function (_element, className) {
    if (typeof _element !== 'undefined') {
      while (typeof _element.parentElement !== 'undefined' && (_element = _element.parentElement) !== null) {
        if (_element.classList.contains(className)) {
          return _element;
        }
      }
    }

    return false;
  },

  getOffset: function (_element) {
    function getOffsetSum(_element) {
      var top = 0,
        left = 0;

      while (_element) {
        top = top + parseInt(_element.offsetTop);
        left = left + parseInt(_element.offsetLeft);

        _element = _element.offsetParent;
      }

      return {
        top: top,
        left: left
      };
    }

    function getOffsetRect(_element) {
      var box = _element.getBoundingClientRect(),
        body = document.body,
        docElem = document.documentElement,
        scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop,
        scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft,
        clientTop = docElem.clientTop || body.clientTop || 0,
        clientLeft = docElem.clientLeft || body.clientLeft || 0,
        top = box.top + scrollTop - clientTop,
        left = box.left + scrollLeft - clientLeft;

      return {
        top: Math.round(top),
        left: Math.round(left)
      };
    }

    if (_element.getBoundingClientRect) {
      return getOffsetRect(_element);
    } else {
      return getOffsetSum(_element);
    }
  },

  jsonToDOM: function (json, doc, nodes) {
    if (typeof doc === 'undefined') {
      doc = document;
    }

    if (typeof nodes === 'undefined') {
      nodes = {};
    }

    var namespaces = {
      html: 'http://www.w3.org/1999/xhtml',
      xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
    };
    var defaultNamespace = namespaces.html;

    function namespace(name) {
      var m = /^(?:(.*):)?(.*)$/.exec(name);
      return [namespaces[m[1]], m[2]];
    }

    function tag(name, attr) {
      if (Array.isArray(name)) {
        var frag = doc.createDocumentFragment();
        Array.prototype.forEach.call(arguments, function (arg) {
          if (!Array.isArray(arg[0]))
            frag.appendChild(tag.apply(null, arg));
          else
            arg.forEach(function (arg) {
              frag.appendChild(tag.apply(null, arg));
            });
        });
        return frag;
      }

      var args = Array.prototype.slice.call(arguments, 2);
      var vals = namespace(name);
      var elem = doc.createElementNS(vals[0] || defaultNamespace, vals[1]);

      for (var key in attr) {
        if (attr.hasOwnProperty(key)) {
          var val = attr[key];

          if (nodes && key == 'key')
            nodes[val] = elem;

          vals = namespace(key);

          if (typeof val == 'function') {
            elem.addEventListener(key.replace(/^on/, ''), val, false);
          } else {
            elem.setAttributeNS(vals[0] || '', vals[1], val);
          }
        }
      }
      args.forEach(function (e) {
        try {
          elem.appendChild(
            Object.prototype.toString.call(e) == '[object Array]' ? tag.apply(null, e) :
              e instanceof doc.defaultView.Node ? e : doc.createTextNode(e)
          );
        } catch (ex) {
          elem.appendChild(doc.createTextNode(ex));
        }
      });

      return elem;
    }

    return tag.apply(null, json);
  },

  appendElementAfterAnother: function (_element, _beforeElement) {
    if (_beforeElement.nextSibling) {
      _beforeElement.parentNode.insertBefore(_element, _beforeElement.nextSibling);
    } else {
      _beforeElement.parentNode.appendChild(_element);
    }
  },

  appendElement: function (_element, _parentElement) {
    if (typeof _element === 'undefined') {
      return false;
    }

    _parentElement.appendChild(_element);
  },

  prependElement: function (_element, _parentElement) {
    if (typeof _element === 'undefined') {
      return false;
    }

    if (_parentElement.firstChild) {
      _parentElement.insertBefore(_element, _parentElement.firstChild);
    } else {
      _parentElement.appendChild(_element);
    }
  },

  removeElement: function (_element) {
    if (
      typeof _element !== 'undefined' &&
      typeof _element.parentElement !== 'undefined' &&
      typeof _element.parentElement.removeChild !== 'undefined'
    ) {
      this.lockDOM('removeElement', true);

      _element.parentElement.removeChild(_element);

      this.lockDOM('removeElement', false);
    }
  },

  getElementByProperty: function (object, property, value) {
    for (var key in object) {
      if (object.hasOwnProperty(key) && object[key][property] == value) {
        return object[key];
      }
    }

    return false;
  },

  checkLockedDOM: function () {
    return Object.keys(this.data.lockCheckUpdateDOM).length;
  },

  lockDOM: function (target, status) {
    var hasLockCheckUpdateDOM = typeof this.data.lockCheckUpdateDOM[target] !== 'undefined';

    if (status) {
      if (hasLockCheckUpdateDOM) {
        this.data.lockCheckUpdateDOM[target]++;
      } else {
        this.data.lockCheckUpdateDOM[target] = 1;
      }
    } else {
      if (hasLockCheckUpdateDOM) {
        this.data.lockCheckUpdateDOM[target]--;

        if (!this.data.lockCheckUpdateDOM[target]) {
          delete this.data.lockCheckUpdateDOM[target];
        }
      }
    }
  }
};
<|endoftext|>


<commit_before>import BaseFormlyFieldMixin from './base-formly-field.mixin'
import Wrapper from '../components/form/Wrapper.vue'

export default {
  mixins: [BaseFormlyFieldMixin],
  computed: {
    isWrapped () {
      return Boolean(this.getToValueOf('wrapper'))
    },
    wrapperProperties () {
      return this.getToValueOf('wrapper', {})
    }
  },
  components: {
    Wrapper
  }
}
<commit_msg>Clearify and complete the wrapped field mixin<commit_after>import BaseFormlyFieldMixin from './base-formly-field.mixin'

export default {
  mixins: [BaseFormlyFieldMixin],
  props: {
    // Should be overridden by using a child wrapper
    wrappedComponent: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    // Override created() method of BaseFormlyFieldMixin.
    // Prevent to the reinitialisation by a wrapped component.
    created () {},
  }
}
<|endoftext|>