initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/libs
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
commonjs: true,
|
||||||
|
es2021: true,
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
Vue: 'readonly',
|
||||||
|
LZString: 'readonly',
|
||||||
|
},
|
||||||
|
extends: [ 'eslint:recommended' ],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'indent': [ 'error', 4 ],
|
||||||
|
'linebreak-style': [ 'error', 'unix' ],
|
||||||
|
'quotes': [ 'error', 'single' ],
|
||||||
|
'semi': [ 'error', 'always' ],
|
||||||
|
'curly': [ 'error', 'all' ],
|
||||||
|
'brace-style': [ 'error', '1tbs' ],
|
||||||
|
'jest/no-done-callback': 'off',
|
||||||
|
'jest/expect-expect': 'off',
|
||||||
|
'comma-dangle': [ 'error', 'always-multiline' ],
|
||||||
|
'complexity': 'error',
|
||||||
|
'consistent-return': 'error',
|
||||||
|
'dot-location': [ 'error', 'property' ],
|
||||||
|
'eqeqeq': [ 'error', 'always', { null: 'ignore' } ],
|
||||||
|
'no-empty-function': 'error',
|
||||||
|
'no-floating-decimal': 'error',
|
||||||
|
'no-multi-spaces': 'error',
|
||||||
|
'camelcase': [ 'error', { properties: 'never' } ],
|
||||||
|
'comma-spacing': [ 'error', { before: false, after: true } ],
|
||||||
|
'array-bracket-newline': [ 'error', { multiline: true } ],
|
||||||
|
'array-element-newline': [ 'error', { multiline: true, minItems: 6 } ],
|
||||||
|
'array-bracket-spacing': [ 'error', 'always' ],
|
||||||
|
'object-curly-spacing': [ 'error', 'always' ],
|
||||||
|
'comma-style': 'error',
|
||||||
|
'computed-property-spacing': 'error',
|
||||||
|
'eol-last': 'error',
|
||||||
|
'func-call-spacing': 'error',
|
||||||
|
'key-spacing': 'error',
|
||||||
|
'keyword-spacing': 'error',
|
||||||
|
'multiline-comment-style': 'error',
|
||||||
|
'newline-per-chained-call': 'error',
|
||||||
|
'no-lonely-if': 'error',
|
||||||
|
'no-multiple-empty-lines': 'error',
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'no-unneeded-ternary': 'error',
|
||||||
|
'no-whitespace-before-property': 'error',
|
||||||
|
'operator-assignment': 'error',
|
||||||
|
'quote-props': [ 'error', 'consistent-as-needed' ],
|
||||||
|
'space-before-blocks': 'error',
|
||||||
|
'space-infix-ops': 'error',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Life Calendar</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script type="text/javascript" src="libs/vue.global.js"></script>
|
||||||
|
<script type="text/javascript" src="libs/lz-string.min.js"></script>
|
||||||
|
<script type="text/javascript" src="main.js"></script>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<!-- card related -->
|
||||||
|
<!--
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta property="og:title" content="">
|
||||||
|
<meta property="twitter:title" content="">
|
||||||
|
<meta property="og:description" content="">
|
||||||
|
<meta property="twitter:description" content="">
|
||||||
|
<meta property="og:image" content="https://.../preview_640x320.jpg">
|
||||||
|
<meta property="twitter:image" content="">
|
||||||
|
<meta property="org:url" content="https://...">
|
||||||
|
-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="app" style="display:none;">
|
||||||
|
<h1>Life Calendar in weeks</h1>
|
||||||
|
View: <select v-model="view">
|
||||||
|
<option value="">Events</option>
|
||||||
|
<option v-for="view in views" v-bind:value="view.name">{{view.name}}</option>
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<table class="life">
|
||||||
|
<tr v-for="(year, row) in life">
|
||||||
|
<td v-for="(week, col) in year" class="week tooltip" v-bind:style="getStyle(row, col)"><span class="tooltiptext" v-html="getTooltip(row, col)" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<h2>Config</h2>
|
||||||
|
<table class="config">
|
||||||
|
<tr>
|
||||||
|
<td>Birthdate:</td>
|
||||||
|
<td><input type="date" v-model="birth"></td>
|
||||||
|
<td><button v-on:click="reset">Reset all data</button></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h3>Events:</h3>
|
||||||
|
<table class="config">
|
||||||
|
<tr v-for="(event, i) in events">
|
||||||
|
<td><input type="checkbox" v-model="event.display"></td>
|
||||||
|
<td><input type="date" v-model="event.date"></td>
|
||||||
|
<td><input v-model="event.text"></td>
|
||||||
|
<td><select-color v-model="event.color"></select-color></td>
|
||||||
|
<td><button v-on:click="deleteEvent(i)">Delete</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" checked disabled></td>
|
||||||
|
<td><input type="date" v-model="newEvent.date"></td>
|
||||||
|
<td><input v-model="newEvent.text"></td>
|
||||||
|
<td><select-color v-model="newEvent.color"></select-color></td>
|
||||||
|
<td><button v-on:click="addEvent">Add</button></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h3>Views:</h3>
|
||||||
|
<table class="config">
|
||||||
|
<tbody v-for="(view, viewIndex) in views">
|
||||||
|
<tr>
|
||||||
|
<th colspan="5"><input style="width:100%" v-model="view.name"></th>
|
||||||
|
<th><button v-on:click="deleteView(viewIndex)">Delete</button></th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="(period, periodIndex) in view.periods">
|
||||||
|
<td>•</td>
|
||||||
|
<td><input type="date" v-model="period.startDate"></td>
|
||||||
|
<td><input type="date" v-model="period.endDate"></td>
|
||||||
|
<td><input v-model="period.text"></td>
|
||||||
|
<td><select-color v-model="period.color"></select-color></td>
|
||||||
|
<td><button v-on:click="deletePeriod(viewIndex, periodIndex)">Delete</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>•</td>
|
||||||
|
<td><input type="date" v-model="view.newPeriod.startDate"></td>
|
||||||
|
<td><input type="date" v-model="view.newPeriod.endDate"></td>
|
||||||
|
<td><input v-model="view.newPeriod.text"></td>
|
||||||
|
<td><select-color v-model="view.newPeriod.color"></select-color></td>
|
||||||
|
<td><button v-on:click="addPeriod(viewIndex)">Add</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th colspan="6"><button style="width:100%" v-on:click="addView">Add</button></th>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
<small><a href="https://twitter.com/_klemek" target="_blank">@Klemek</a> - <a href="" target="_blank">Github Repository</a> - 2022</small>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString);
|
||||||
+15704
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,310 @@
|
|||||||
|
/* exported app */
|
||||||
|
|
||||||
|
const cookies = {
|
||||||
|
/**
|
||||||
|
* Save a value in a cookie
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} value
|
||||||
|
* @param {number | undefined} days
|
||||||
|
*/
|
||||||
|
set: function (name, value, days = undefined) {
|
||||||
|
const maxAge = !days ? undefined : days * 864e2;
|
||||||
|
document.cookie = `${name}=${encodeURIComponent(value)}${maxAge ? `;max-age=${maxAge};` : ''}`;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get a value from a cookie
|
||||||
|
* @param {string} name
|
||||||
|
* @return {string} value from cookie or empty if not found
|
||||||
|
*/
|
||||||
|
get: function (name) {
|
||||||
|
return document.cookie.split('; ').reduce(function (r, v) {
|
||||||
|
const parts = v.split('=');
|
||||||
|
return parts[0] === name ? decodeURIComponent(parts[1]) : r;
|
||||||
|
}, '');
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Delete a cookie
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
delete: function (name) {
|
||||||
|
this.set(name, '', -1);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Clear all cookies
|
||||||
|
*/
|
||||||
|
clear: function () {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i];
|
||||||
|
const eqPos = cookie.indexOf('=');
|
||||||
|
const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;
|
||||||
|
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Date.prototype.addDays = function(days) {
|
||||||
|
const date = new Date(this.getTime());
|
||||||
|
date.setDate(this.getDate() + parseInt(days));
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
Date.prototype.getWeek = function() {
|
||||||
|
const oneJan = new Date(this.getFullYear(), 0, 1);
|
||||||
|
const numberOfDays = Math.floor((this - oneJan) / (24 * 60 * 60 * 1000));
|
||||||
|
return Math.ceil(( this.getDay() + 1 + numberOfDays) / 7);
|
||||||
|
};
|
||||||
|
|
||||||
|
Date.prototype.formatSimple = function() {
|
||||||
|
return `${this.getFullYear()}-${('00' + (this.getMonth() + 1)).substr(-2)}-${('00' + this.getDate()).substr(-2)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLOR_PALETTE = [
|
||||||
|
'#F44336',
|
||||||
|
'#E91E63',
|
||||||
|
'#9C27B0',
|
||||||
|
'#673AB7',
|
||||||
|
'#3F51B5',
|
||||||
|
'#2196F3',
|
||||||
|
'#03A9F4',
|
||||||
|
'#00BCD4',
|
||||||
|
'#009688',
|
||||||
|
'#4CAF50',
|
||||||
|
'#8BC34A',
|
||||||
|
'#CDDC39',
|
||||||
|
'#FDD835',
|
||||||
|
'#FFC107',
|
||||||
|
'#FF9800',
|
||||||
|
'#FF5722',
|
||||||
|
'#795548',
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectColor = {
|
||||||
|
props: [ 'modelValue' ],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
colorPalette: COLOR_PALETTE,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
emits: [ 'update:modelValue' ],
|
||||||
|
template: `
|
||||||
|
<select class="select-color" v-bind:value="modelValue" v-on:input="$emit('update:modelValue', $event.target.value)" v-bind:style="{'background-color':colorPalette[modelValue]}">
|
||||||
|
<option v-for="(color, i) in colorPalette" v-bind:value="i" v-bind:style="{'background-color':color}">Color {{i + 1}}</option>
|
||||||
|
</select>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const NEW_PERIOD = {
|
||||||
|
color: '',
|
||||||
|
startDate: '',
|
||||||
|
endDate: '',
|
||||||
|
text: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const serialize = function(birtdate, view, events, views) {
|
||||||
|
return LZString.compressToBase64(JSON.stringify([ birtdate, view, events.map(event => [ event.color, event.date, event.text, event.display ? 1 : 0 ]), views.map(view => [ view.name, view.periods.map(period => [ period.color, period.startDate, period.endDate, period.text ]) ]) ]));
|
||||||
|
};
|
||||||
|
|
||||||
|
const deserialize = function(rawData) {
|
||||||
|
const data = JSON.parse(LZString.decompressFromBase64(rawData));
|
||||||
|
return {
|
||||||
|
birthdate: data[0],
|
||||||
|
view: data[1],
|
||||||
|
events: data[2].map(subData => {
|
||||||
|
return {
|
||||||
|
color: subData[0],
|
||||||
|
date: subData[1],
|
||||||
|
text: subData[2],
|
||||||
|
display: subData[3] === 1,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
views: data[3].map(subData1 => {
|
||||||
|
return {
|
||||||
|
name: subData1[0],
|
||||||
|
periods: subData1[1].map(subData2 => {
|
||||||
|
return {
|
||||||
|
color: subData2[0],
|
||||||
|
startDate: subData2[1],
|
||||||
|
endDate: subData2[2],
|
||||||
|
text: subData2[3],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
newPeriod: NEW_PERIOD,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
life: [],
|
||||||
|
birth: '1996-07-25',
|
||||||
|
view: '',
|
||||||
|
newEvent: {
|
||||||
|
color: '',
|
||||||
|
date: '',
|
||||||
|
text: '',
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
events: [],
|
||||||
|
views: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
birthdate() {
|
||||||
|
return new Date(this.birth);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showApp() {
|
||||||
|
document.getElementById('app').setAttribute('style', '');
|
||||||
|
},
|
||||||
|
getDate(row, col) {
|
||||||
|
return new Date(this.birthdate.getFullYear() + row, this.birthdate.getMonth(), this.birthdate.getDate()).addDays(col * 7);
|
||||||
|
},
|
||||||
|
getEvents(row, col) {
|
||||||
|
const startDate = this.getDate(row, col);
|
||||||
|
const endDate = startDate.addDays(7);
|
||||||
|
const strStartDate = startDate.formatSimple();
|
||||||
|
const strEndDate = endDate.formatSimple();
|
||||||
|
return this.events.concat(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'special',
|
||||||
|
color: null,
|
||||||
|
date: this.birth,
|
||||||
|
text: 'Birth',
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'special',
|
||||||
|
color: null,
|
||||||
|
date: new Date().formatSimple(),
|
||||||
|
text: 'Today',
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
).filter(event => event.display && event.date >= strStartDate && event.date < strEndDate);
|
||||||
|
},
|
||||||
|
getViews(row, col) {
|
||||||
|
const strStartDate = this.getDate(row, col).formatSimple();
|
||||||
|
const strNow = (new Date()).formatSimple();
|
||||||
|
const out = {};
|
||||||
|
this.views.forEach(view => {
|
||||||
|
const extract = view.periods.filter(period => period.startDate <= strStartDate && (!period.endDate && strStartDate <= strNow || period.endDate > strStartDate));
|
||||||
|
if (extract.length) {
|
||||||
|
out[view.name] = extract[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
getTooltip(row, col) {
|
||||||
|
const date = this.getDate(row, col);
|
||||||
|
let text = `${date.getFullYear()} (Week ${date.getWeek()})<br>Age: ${row}`;
|
||||||
|
const views = this.getViews(row, col);
|
||||||
|
for (const view in views) {
|
||||||
|
text += `<br>${view}: ${views[view].text}`; //TODO percent
|
||||||
|
}
|
||||||
|
this.getEvents(row, col).forEach(event => {
|
||||||
|
text += `<br>- ${new Date(event.date).formatSimple()}: ${event.text}`;
|
||||||
|
});
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
getColor(row, col) {
|
||||||
|
const events = this.getEvents(row, col);
|
||||||
|
for (const i in events) {
|
||||||
|
if (events[i].type === 'special') {
|
||||||
|
return events[i].color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.view) {
|
||||||
|
if (events.length > 0) {
|
||||||
|
return events[0].color;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const views = this.getViews(row, col);
|
||||||
|
if (views[this.view]) {
|
||||||
|
return views[this.view].color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
getStyle(row, col) {
|
||||||
|
const color = this.getColor(row, col);
|
||||||
|
return {
|
||||||
|
'background-color': color ? COLOR_PALETTE[color] : (color === null ? '#212121' : null),
|
||||||
|
'border-color': color ? COLOR_PALETTE[color] : (color === null ? '#212121' : null),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
deleteEvent(eventIndex) {
|
||||||
|
this.events.pop(eventIndex);
|
||||||
|
},
|
||||||
|
addEvent() {
|
||||||
|
if (this.newEvent.color && this.newEvent.text && this.newEvent.date) {
|
||||||
|
this.events.push(this.newEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteView(viewIndex) {
|
||||||
|
this.views.pop(viewIndex);
|
||||||
|
},
|
||||||
|
addView() {
|
||||||
|
this.views.push({
|
||||||
|
name: 'New View',
|
||||||
|
periods: [],
|
||||||
|
newPeriod: NEW_PERIOD,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deletePeriod(viewIndex, periodIndex) {
|
||||||
|
this.views[viewIndex].periods.pop(periodIndex);
|
||||||
|
},
|
||||||
|
addPeriod(viewIndex) {
|
||||||
|
if (this.views[viewIndex].newPeriod.color && this.views[viewIndex].newPeriod.text && this.views[viewIndex].newPeriod.startDate) {
|
||||||
|
this.views[viewIndex].periods.push(this.views[viewIndex].newPeriod);
|
||||||
|
this.views[viewIndex].newPeriod.startDate = this.views[viewIndex].newPeriod.endDate;
|
||||||
|
this.views[viewIndex].newPeriod.endDate = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.view = '';
|
||||||
|
this.events = [];
|
||||||
|
this.views = [];
|
||||||
|
this.$force;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
this.life = Array(90).fill(Array(52));
|
||||||
|
const url = new URL(window.location);
|
||||||
|
if (url.searchParams.get('d')) {
|
||||||
|
const data = deserialize(url.searchParams.get('d'));
|
||||||
|
this.birtdate = data.birthdate;
|
||||||
|
this.view = data.view;
|
||||||
|
this.events = data.events;
|
||||||
|
this.views = data.views;
|
||||||
|
} else if (cookies.get('d')) {
|
||||||
|
const data = deserialize(cookies.get('d'));
|
||||||
|
this.birtdate = data.birthdate;
|
||||||
|
this.view = data.view;
|
||||||
|
this.events = data.events;
|
||||||
|
this.views = data.views;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
setTimeout(this.showApp);
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
const data = serialize(this.birtdate, this.view, this.events, this.views);
|
||||||
|
const url = new URL(window.location);
|
||||||
|
if (url.searchParams.get('d') !== data) {
|
||||||
|
url.searchParams.set('d', data);
|
||||||
|
window.history.pushState({}, '', url);
|
||||||
|
}
|
||||||
|
cookies.set('d', data);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
app = Vue.createApp(app);
|
||||||
|
app.component('select-color', selectColor);
|
||||||
|
app.mount('#app');
|
||||||
|
};
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: Verdana, serif;
|
||||||
|
color: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
max-width: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #EEEEEE;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.life {
|
||||||
|
margin:-1.5rem;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.week{
|
||||||
|
margin:3px;
|
||||||
|
width:12px;
|
||||||
|
height: calc(100vw/52 - 2px);
|
||||||
|
padding:0;
|
||||||
|
border: 1px solid #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.config{
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.config td{
|
||||||
|
padding:.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.config td button{
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
main {
|
||||||
|
max-width: 48rem;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
table.life{
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
td.week{
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-color {
|
||||||
|
width:100px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-color option {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
background-color: #424242;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: .5em;
|
||||||
|
border-radius: 0 6px 6px 6px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: -5px;
|
||||||
|
left: 105%;
|
||||||
|
top: 105%;
|
||||||
|
opacity: 0;
|
||||||
|
display: table;
|
||||||
|
width: fit-content;
|
||||||
|
transition: opacity .5s;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user