Initial Release
This commit is contained in:
parent
0cc1bebf18
commit
5cc0ae8c18
24 changed files with 1753 additions and 1 deletions
127
src/App.svelte
Normal file
127
src/App.svelte
Normal file
|
@ -0,0 +1,127 @@
|
|||
<script>
|
||||
|
||||
import Visualization from './Visualization.svelte';
|
||||
import { config } from './stores.js';
|
||||
|
||||
import './vendor/normalize.css';
|
||||
import './vendor/milligram.min.css';
|
||||
|
||||
let isAnimated = false;
|
||||
let splitscreen = false;
|
||||
let controlsOpen = false;
|
||||
let left, right;
|
||||
|
||||
function handleKeyboard(event) {
|
||||
switch (event.code) {
|
||||
case 'ArrowLeft':
|
||||
//i = (i>0) ? i-1 : data.length-1;
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 'ArrowRight':
|
||||
//i = (i<data.length-1) ? i+1 : 0;
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 'ArrowUp':
|
||||
$config.speed += 10;
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 'ArrowDown':
|
||||
$config.speed -= 10;
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 'Space':
|
||||
isAnimated = !isAnimated;
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
//log current index on the lower right
|
||||
}
|
||||
|
||||
function reset() {
|
||||
left.reset();
|
||||
if (splitscreen) {
|
||||
right.reset();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeyboard} />
|
||||
|
||||
<main>
|
||||
<Visualization bind:this={left} {isAnimated}/>
|
||||
{#if splitscreen}<Visualization bind:this={right} {isAnimated}/>{/if}
|
||||
</main>
|
||||
<aside class:open={controlsOpen}>
|
||||
<label class="hideCheckbox toggle" title="Open Controls">⚙<input type="checkbox" bind:checked={controlsOpen}></label>
|
||||
<div id="controls">
|
||||
<h3>Controls</h3>
|
||||
<p><small>Press Space to pause and use arrow keys to skip or change speed</small></p>
|
||||
<label>Zoom:<br/><input type="number" id="zoom" step="1" value={$config.zoom} on:change={e => $config.zoom = e.target.value}></label>
|
||||
<label>Speed:<br/><input type="number" id="speed" step="1" value={$config.speed} on:change={e => $config.speed = e.target.value}></label>
|
||||
<label>Duration Multiplier:<br/><input type="number" id="duration_multiplier" step="0.1" value={$config.duration_multiplier} on:change={e => $config.duration_multiplier = e.target.value}></label>
|
||||
<label>Trace Length:<br/><input type="number" id="trace_length" step="1" value={$config.trace_length} on:change={e => $config.trace_length = e.target.value}></label>
|
||||
<label>Duration Function Exponent:<br/><input type="number" id="exp" step="0.1" value={$config.duration_exponent} on:change={e => $config.duration_exponent = e.target.value}></label>
|
||||
</div>
|
||||
<label id="run" class="hideCheckbox toggle" title="Run Animation">{#if !isAnimated}▶{:else}⏸{/if}<input type="checkbox" bind:checked={isAnimated}></label>
|
||||
<label id="split" class="hideCheckbox toggle" title="Make Splitscreen">{#if !splitscreen}🗖{:else}🗗{/if}<input type="checkbox" bind:checked={splitscreen}></label>
|
||||
<span id="reset" class="toggle" title="Reset Animation" on:click={reset}>↶</span>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
aside {
|
||||
position: absolute;
|
||||
left: -20vw;
|
||||
transition: left 0.5s;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 20vw;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.open {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: absolute;
|
||||
right: -30px;
|
||||
width: 20px;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: left 0.5s;
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.hideCheckbox > input {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
#run {
|
||||
bottom: 100px;
|
||||
}
|
||||
|
||||
#split {
|
||||
bottom: 60px;
|
||||
}
|
||||
|
||||
#reset {
|
||||
bottom: 20px;
|
||||
}
|
||||
</style>
|
52
src/FilePicker.svelte
Normal file
52
src/FilePicker.svelte
Normal file
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { readFileAsync } from './helpers';
|
||||
import Papa from './vendor/papaparse.min.js';
|
||||
|
||||
let datafiles, imagefiles, isValid, fixation_data, sequential_fixation_data;
|
||||
const image = new Image();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: isValid = datafiles && imagefiles && datafiles[0] && imagefiles[0];
|
||||
|
||||
async function visualize() {
|
||||
|
||||
image.src = await readFileAsync(imagefiles[0]);
|
||||
|
||||
let parsing = new Promise( resolve => {
|
||||
Papa.parse(datafiles[0], {
|
||||
comments: '#',
|
||||
header: true,
|
||||
dynamicTyping: true,
|
||||
complete: results => {
|
||||
if (!results.data[0].x || !results.data[0].y || !results.data[0].dur || !results.data[0].word_id || !results.data[0].word) {
|
||||
throw Error('Data malformed. Please use csv style tab-separated format with headers x,y,dur,word_id, word as first line.');
|
||||
}
|
||||
fixation_data = results.data;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
parsing.then( () => {
|
||||
dispatch('load', {image, fixation_data})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div id="overlay">
|
||||
<h1>Choose Fixation Data and Image</h1>
|
||||
<label>Fixation Data<br/><input bind:files={datafiles} type="file" accept=".txt" /></label>
|
||||
<label>Image<br /><input bind:files={imagefiles} type="file" accept=".png, .jpg" /></label>
|
||||
<button id="run-button" class="button" on:click={visualize} disabled={!isValid}>Visualize!</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#overlay {
|
||||
width: 400px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
203
src/Visualization.svelte
Normal file
203
src/Visualization.svelte
Normal file
|
@ -0,0 +1,203 @@
|
|||
<script>
|
||||
export let isAnimated = false;
|
||||
|
||||
import FilePicker from './FilePicker.svelte';
|
||||
|
||||
import { onMount, onDestroy, tick as nextTick } from 'svelte';
|
||||
import { config } from './stores.js';
|
||||
import { aggregateWordFixations } from './helpers';
|
||||
|
||||
let observer, canvas, progress, ctx, show_sequential, fixation_data, sequential_fixation_data, fixation_backdrop, data, container;
|
||||
|
||||
$: {
|
||||
data = (show_sequential) ? sequential_fixation_data : fixation_data;
|
||||
reset();
|
||||
}
|
||||
|
||||
let i = 0, t = null, isInitialized = false;
|
||||
|
||||
onMount(() => {
|
||||
ctx = canvas.getContext('2d');
|
||||
observer = new ResizeObserver( entries => {
|
||||
canvas.height = entries[0].contentRect.height;
|
||||
canvas.width = entries[0].contentRect.width;
|
||||
});
|
||||
observer.observe(container);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
|
||||
async function onLoad(event) {
|
||||
fixation_data = event.detail.fixation_data;
|
||||
sequential_fixation_data = aggregateWordFixations(event.detail.fixation_data);
|
||||
fixation_backdrop = event.detail.image;
|
||||
isInitialized = true;
|
||||
await nextTick();
|
||||
//config.update(c => {c.$config.zoom = canvas.width/fixation_backdrop.width; return c;});
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function tick(time) {
|
||||
if (!data || !fixation_backdrop) {
|
||||
requestAnimationFrame(tick);
|
||||
return;
|
||||
}
|
||||
|
||||
let dt;
|
||||
|
||||
if (isAnimated){
|
||||
if (t == null) t = time;
|
||||
dt = (time - t)*$config.speed/100
|
||||
if (dt > data[i].dur) {
|
||||
i = (i<data.length-1)?i+1:0;
|
||||
t = time;
|
||||
progress.style.width = (100/data.length*i)+'%';
|
||||
}
|
||||
} else {
|
||||
dt = data[i].dur;
|
||||
progress.style.width = (100/data.length*i)+'%';
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, 5000, 5000);
|
||||
ctx.save();
|
||||
|
||||
drawBackdrop();
|
||||
drawFixation(data[i], dt);
|
||||
drawTrace(data.slice((i-$config.trace_length>0)?i-$config.trace_length:0,i+1))
|
||||
ctx.restore();
|
||||
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
i = 0;
|
||||
t = null;
|
||||
}
|
||||
|
||||
function transform({ x, y }) {
|
||||
// transforms coordinates from image space into drawing space
|
||||
return [$config.zoom/100 * x + $config.translatex, $config.zoom/100 * y + $config.translatey]
|
||||
}
|
||||
|
||||
function drawBackdrop() {
|
||||
ctx.drawImage(fixation_backdrop, $config.translatex, $config.translatey, $config.zoom/100*fixation_backdrop.width, $config.zoom/100*fixation_backdrop.height);
|
||||
}
|
||||
|
||||
function drawFixation({ x, y, dur }, dt) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
let radius = Math.pow(dt/dur, $config.duration_exponent)*dur*$config.duration_multiplier;
|
||||
let [xt, yt] = transform({x, y});
|
||||
let gradient;
|
||||
try {
|
||||
gradient = ctx.createRadialGradient(xt,yt,0, xt,yt,radius);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
gradient.addColorStop(0, 'rgb(200, 0, 0, 0.5)');
|
||||
gradient.addColorStop(1, 'rgb(200, 0, 0, 0)');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.arc(xt, yt, radius, 0, Math.PI*2, true);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawTrace(trace) {
|
||||
ctx.save()
|
||||
ctx.lineWidth = $config.trace_width;
|
||||
let alpha = 0.2 + 0.8/$config.trace_length;
|
||||
let i=0, xt, yt, xtn, ytn;
|
||||
for (i; i<trace.length-1; i++) {
|
||||
let { x, y } = trace[i];
|
||||
let { x:xn, y:yn } = trace[i+1];
|
||||
[xt, yt] = transform({x, y});
|
||||
[xtn, ytn] = transform({x:xn, y:yn});
|
||||
ctx.strokeStyle = `rgba(0,0,0,${alpha}`;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xt, yt);
|
||||
ctx.arc(xt, yt, $config.trace_radius, 0, Math.PI*2, true);
|
||||
ctx.lineTo(xtn, ytn);
|
||||
ctx.stroke();
|
||||
alpha += 0.8/$config.trace_length;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.arc(xt, yt, $config.trace_radius, 0, Math.PI*2, true);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function translate(e) {
|
||||
if (e.buttons == 1) {
|
||||
$config.translatex += e.movementX;
|
||||
$config.translatey += e.movementY;
|
||||
}
|
||||
}
|
||||
|
||||
function setZoom(e) {
|
||||
$config.zoom += (e.deltaY > 0) ? -1 : 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={container} id="container">
|
||||
{#if !isInitialized}
|
||||
<FilePicker on:load={onLoad} />
|
||||
{/if}
|
||||
<span id="loadFile" class:hide={!isInitialized} title="Close and load other files" on:click={() => isInitialized = false}>🞩</span>
|
||||
<div id="controls" class:hide={!isInitialized}>
|
||||
<label title="Aggregates multiple (re-)visits into one fixation per word. Averages the fixation position and plays back in word order. Use to compare to fixation prediction models."><input type="checkbox" bind:checked={show_sequential}> As sequential word fixations</label>
|
||||
</div>
|
||||
<canvas class:hide={!isInitialized} bind:this={canvas} on:mousemove={translate} on:wheel|preventDefault={setZoom} height="1080" width="1920">This browser does not support canvas animations, which are required to render the visualization.</canvas>
|
||||
<div id="progress" bind:this={progress}></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
canvas {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#loadFile {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 20px;
|
||||
background: #9b4dca;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
text-align: right;
|
||||
background: rgba(255,255,255,0.5);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
40
src/helpers.js
Normal file
40
src/helpers.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
export function readFileAsync(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
export function aggregateWordFixations(data) {
|
||||
//slicing off first fixation since it is the calibration in center
|
||||
let sorted = data.filter( item => item.word_id ).slice(1).sort((a,b) => a.word_id-b.word_id);
|
||||
//add first back in
|
||||
sorted.unshift(data[0]);
|
||||
//aggregate fixations for same words in bags
|
||||
let aggregate = sorted.reduce( (acc, val) => {
|
||||
if (acc[0].length == 0 || val.word_id == acc[acc.length-1][0].word_id) {
|
||||
acc[acc.length-1].push(val)
|
||||
} else {
|
||||
acc.push([val]);
|
||||
}
|
||||
return acc;
|
||||
}, [[]]);
|
||||
//average fixation coordinates and sum duration for each bag
|
||||
return aggregate.map( bag => {
|
||||
let word = bag[0].word;
|
||||
let word_id = bag[0].word_id;
|
||||
let x=0;
|
||||
let y=0;
|
||||
let dur=0;
|
||||
for (let fixation of bag) {
|
||||
x += fixation.x/bag.length;
|
||||
y += fixation.y/bag.length;
|
||||
dur += fixation.dur;
|
||||
}
|
||||
return {x, y, dur, word_id, word};
|
||||
});
|
||||
}
|
9
src/main.js
Normal file
9
src/main.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
41
src/stores.js
Normal file
41
src/stores.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
export const config = writable({
|
||||
zoom: 100,
|
||||
translatex: 0,
|
||||
translatey: 0,
|
||||
duration_multiplier: 0.1,
|
||||
duration_exponent: 0.2,
|
||||
speed: 100,
|
||||
show_sequential: false,
|
||||
trace_length: 3,
|
||||
trace_radius: 5,
|
||||
trace_width: 2,
|
||||
canvas_width: 1920,
|
||||
canvas_height: 1080
|
||||
});
|
||||
|
||||
|
||||
|
||||
// export const time = readable(new Date(), function start(set) {
|
||||
// const interval = setInterval(() => {
|
||||
// set(new Date());
|
||||
// }, 1000);
|
||||
|
||||
// return function stop() {
|
||||
// clearInterval(interval);
|
||||
// };
|
||||
// });
|
||||
|
||||
// function tickGenerator() {
|
||||
// const { subscribe, set, update } = writable(0);
|
||||
// let interval;
|
||||
|
||||
// return {
|
||||
// subscribe,
|
||||
// start: () => setInterval(),
|
||||
// stop: () => update(n => n - 1),
|
||||
// reset: () => set(0)
|
||||
// };
|
||||
// }
|
||||
//export const count = createCount();
|
11
src/vendor/milligram.min.css
vendored
Normal file
11
src/vendor/milligram.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
349
src/vendor/normalize.css
vendored
Normal file
349
src/vendor/normalize.css
vendored
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
7
src/vendor/papaparse.min.js
vendored
Normal file
7
src/vendor/papaparse.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue