deploy: b4d46e331515135fd852a09e78fd168614b541d7

Этот коммит содержится в:
DFRLab 2024-10-12 16:02:29 +00:00
родитель 5b45719a9d
Коммит 94d9c1cf69
121 изменённых файлов: 79 добавлений и 6168 удалений

21
.gitignore поставляемый
Просмотреть файл

@ -1,21 +0,0 @@
node_modules
# Output
.output
.vercel
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

0
.nojekyll Обычный файл
Просмотреть файл

1
.npmrc
Просмотреть файл

@ -1 +0,0 @@
engine-strict=true

Просмотреть файл

@ -1,4 +0,0 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock

Просмотреть файл

@ -1,8 +0,0 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

Просмотреть файл

@ -1,18 +0,0 @@
To run this app locally:
- clone this repository
- navigate into the folder of the repository on the command line
- run `npm install`
- run `npm run dev`
The command line log will give you a url to run the app locally in your browser.
## Building
To create a production version of your app:
```bash
npm run build
```
The files of the built app will be in the `build` folder. You can preview the production build with `npm run preview`.

1
_app/env.js Обычный файл
Просмотреть файл

@ -0,0 +1 @@
export const env={}

1
_app/immutable/assets/0.DbPGuKpG.css Обычный файл
Просмотреть файл

@ -0,0 +1 @@
:root{--bg: #F9F8F8;--transparentbg: #F9F8F8cb;--usa-blue: #3c3b6e;--usa-lightblue: #c9c7eb;--usa-red: #b22234;--usa-lightred: #c5888f;--text-darkgray: #5e4a4a;--font-01: Volkhov, serif;--font-02: Quicksand, sans-serif}body{background-color:var(--bg)}

1
_app/immutable/assets/2.TrGWaNyh.css Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
_app/immutable/assets/_layout.DbPGuKpG.css Обычный файл
Просмотреть файл

@ -0,0 +1 @@
:root{--bg: #F9F8F8;--transparentbg: #F9F8F8cb;--usa-blue: #3c3b6e;--usa-lightblue: #c9c7eb;--usa-red: #b22234;--usa-lightred: #c5888f;--text-darkgray: #5e4a4a;--font-01: Volkhov, serif;--font-02: Quicksand, sans-serif}body{background-color:var(--bg)}

1
_app/immutable/assets/_page.TrGWaNyh.css Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

3
_app/immutable/chunks/entry.Cu_RPa2W.js Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

4
_app/immutable/chunks/index.CuQfD_Qi.js Обычный файл
Просмотреть файл

@ -0,0 +1,4 @@
var J=Object.defineProperty;var K=(t,e,n)=>e in t?J(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var R=(t,e,n)=>K(t,typeof e!="symbol"?e+"":e,n);import{n as x,A as Q,B as U,f as F,C as v,D as N,E as I,F as V,G as k,H as z,b as q,I as T,J as W,K as X,L as Y,M as B,N as Z,O as tt,P as et,Q as nt,R as st}from"./scheduler.BeEXgHAC.js";const L=typeof window<"u";let it=L?()=>window.performance.now():()=>Date.now(),j=L?t=>requestAnimationFrame(t):x;const y=new Set;function G(t){y.forEach(e=>{e.c(t)||(y.delete(e),e.f())}),y.size!==0&&j(G)}function rt(t){let e;return y.size===0&&j(G),{promise:new Promise(n=>{y.add(e={c:t,f:n})}),abort(){y.delete(e)}}}const S=new Map;let O=0;function at(t){let e=5381,n=t.length;for(;n--;)e=(e<<5)-e^t.charCodeAt(n);return e>>>0}function ot(t,e){const n={stylesheet:U(e),rules:{}};return S.set(t,n),n}function D(t,e,n,s,c,f,l,i=0){const u=16.666/s;let r=`{
`;for(let d=0;d<=1;d+=u){const g=e+(n-e)*f(d);r+=d*100+`%{${l(g,1-g)}}
`}const $=r+`100% {${l(n,1-n)}}
}`,o=`__svelte_${at($)}_${i}`,m=Q(t),{stylesheet:p,rules:a}=S.get(m)||ot(m,t);a[o]||(a[o]=!0,p.insertRule(`@keyframes ${o} ${$}`,p.cssRules.length));const _=t.style.animation||"";return t.style.animation=`${_?`${_}, `:""}${o} ${s}ms linear ${c}ms 1 both`,O+=1,o}function ft(t,e){const n=(t.style.animation||"").split(", "),s=n.filter(e?f=>f.indexOf(e)<0:f=>f.indexOf("__svelte")===-1),c=n.length-s.length;c&&(t.style.animation=s.join(", "),O-=c,O||ut())}function ut(){j(()=>{O||(S.forEach(t=>{const{ownerNode:e}=t.stylesheet;e&&F(e)}),S.clear())})}let w;function lt(){return w||(w=Promise.resolve(),w.then(()=>{w=null})),w}function A(t,e,n){t.dispatchEvent(V(`${e?"intro":"outro"}${n}`))}const E=new Set;let h;function yt(){h={r:0,c:[],p:h}}function wt(){h.r||v(h.c),h=h.p}function ct(t,e){t&&t.i&&(E.delete(t),t.i(e))}function xt(t,e,n,s){if(t&&t.o){if(E.has(t))return;E.add(t),h.c.push(()=>{E.delete(t),s&&(n&&t.d(1),s())}),t.o(e)}else s&&s()}const dt={duration:0};function vt(t,e,n,s){let f=e(t,n,{direction:"both"}),l=s?0:1,i=null,u=null,r=null,$;function o(){r&&ft(t,r)}function m(a,_){const d=a.b-l;return _*=Math.abs(d),{a:l,b:a.b,d,duration:_,start:a.start,end:a.start+_,group:a.group}}function p(a){const{delay:_=0,duration:d=300,easing:g=k,tick:C=x,css:M}=f||dt,P={start:it()+_,b:a};a||(P.group=h,h.r+=1),"inert"in t&&(a?$!==void 0&&(t.inert=$):($=t.inert,t.inert=!0)),i||u?u=P:(M&&(o(),r=D(t,l,a,d,_,g,M)),a&&C(0,1),i=m(P,d),I(()=>A(t,a,"start")),rt(b=>{if(u&&b>u.start&&(i=m(u,d),u=null,A(t,i.b,"start"),M&&(o(),r=D(t,l,i.b,i.duration,0,g,f.css))),i){if(b>=i.end)C(l=i.b,1-l),A(t,i.b,"end"),u||(i.b?o():--i.group.r||v(i.group.c)),i=null;else if(b>=i.start){const H=b-i.start;l=i.a+i.d*g(H/i.duration),C(l,1-l)}}return!!(i||u)}))}return{run(a){N(f)?lt().then(()=>{f=f({direction:a?"in":"out"}),p(a)}):p(a)},end(){o(),i=u=null}}}function bt(t,e,n){const s=t.$$.props[e];s!==void 0&&(t.$$.bound[s]=n,n(t.$$.ctx[s]))}function Et(t){t&&t.c()}function St(t,e){t&&t.l(e)}function _t(t,e,n){const{fragment:s,after_update:c}=t.$$;s&&s.m(e,n),I(()=>{const f=t.$$.on_mount.map(Z).filter(N);t.$$.on_destroy?t.$$.on_destroy.push(...f):v(f),t.$$.on_mount=[]}),c.forEach(I)}function $t(t,e){const n=t.$$;n.fragment!==null&&(X(n.after_update),v(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function ht(t,e){t.$$.dirty[0]===-1&&(tt.push(t),et(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<<e%31}function Ot(t,e,n,s,c,f,l=null,i=[-1]){const u=Y;B(t);const r=t.$$={fragment:null,ctx:[],props:f,update:x,not_equal:c,bound:z(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(e.context||(u?u.$$.context:[])),callbacks:z(),dirty:i,skip_bound:!1,root:e.target||u.$$.root};l&&l(r.root);let $=!1;if(r.ctx=n?n(t,e.props||{},(o,m,...p)=>{const a=p.length?p[0]:m;return r.ctx&&c(r.ctx[o],r.ctx[o]=a)&&(!r.skip_bound&&r.bound[o]&&r.bound[o](a),$&&ht(t,o)),m}):[],r.update(),$=!0,v(r.before_update),r.fragment=s?s(r.ctx):!1,e.target){if(e.hydrate){nt();const o=q(e.target);r.fragment&&r.fragment.l(o),o.forEach(F)}else r.fragment&&r.fragment.c();e.intro&&ct(t.$$.fragment),_t(t,e.target,e.anchor),st(),T()}B(u)}class Ct{constructor(){R(this,"$$");R(this,"$$set")}$destroy(){$t(this,1),this.$destroy=x}$on(e,n){if(!N(n))return x;const s=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return s.push(n),()=>{const c=s.indexOf(n);c!==-1&&s.splice(c,1)}}$set(e){this.$$set&&!W(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}const mt="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(mt);export{Ct as S,xt as a,Et as b,wt as c,St as d,$t as e,vt as f,yt as g,bt as h,Ot as i,rt as l,_t as m,it as n,ct as t};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
_app/immutable/chunks/stores.Lq868Yjj.js Обычный файл
Просмотреть файл

@ -0,0 +1 @@
import{s as e}from"./entry.Cu_RPa2W.js";const r=()=>{const s=e;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},b={subscribe(s){return r().page.subscribe(s)}};export{b as p};

2
_app/immutable/entry/app.eRIxyuLO.js Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
_app/immutable/entry/start.Cp5tm-R9.js Обычный файл
Просмотреть файл

@ -0,0 +1 @@
import{a as t}from"../chunks/entry.Cu_RPa2W.js";export{t as start};

1
_app/immutable/nodes/0.caO7FUPE.js Обычный файл
Просмотреть файл

@ -0,0 +1 @@
import{s as l,l as r,u as i,m as u,o as _}from"../chunks/scheduler.BeEXgHAC.js";import{S as f,i as c,t as p,a as m}from"../chunks/index.CuQfD_Qi.js";const d=!0,S=Object.freeze(Object.defineProperty({__proto__:null,prerender:d},Symbol.toStringTag,{value:"Module"}));function $(n){let s;const a=n[1].default,e=r(a,n,n[0],null);return{c(){e&&e.c()},l(t){e&&e.l(t)},m(t,o){e&&e.m(t,o),s=!0},p(t,[o]){e&&e.p&&(!s||o&1)&&i(e,a,t,t[0],s?_(a,t[0],o,null):u(t[0]),null)},i(t){s||(p(e,t),s=!0)},o(t){m(e,t),s=!1},d(t){e&&e.d(t)}}}function g(n,s,a){let{$$slots:e={},$$scope:t}=s;return n.$$set=o=>{"$$scope"in o&&a(0,t=o.$$scope)},[t,e]}class v extends f{constructor(s){super(),c(this,s,g,$,l,{})}}export{v as component,S as universal};

1
_app/immutable/nodes/1.Crpz-BfZ.js Обычный файл
Просмотреть файл

@ -0,0 +1 @@
import{s as x,e as u,t as h,a as S,c as d,b as v,d as g,f as m,g as j,i as _,h as b,j as E,n as $,k}from"../chunks/scheduler.BeEXgHAC.js";import{S as q,i as y}from"../chunks/index.CuQfD_Qi.js";import{p as C}from"../chunks/stores.Lq868Yjj.js";function H(i){var f;let a,s=i[0].status+"",r,o,n,p=((f=i[0].error)==null?void 0:f.message)+"",c;return{c(){a=u("h1"),r=h(s),o=S(),n=u("p"),c=h(p)},l(e){a=d(e,"H1",{});var t=v(a);r=g(t,s),t.forEach(m),o=j(e),n=d(e,"P",{});var l=v(n);c=g(l,p),l.forEach(m)},m(e,t){_(e,a,t),b(a,r),_(e,o,t),_(e,n,t),b(n,c)},p(e,[t]){var l;t&1&&s!==(s=e[0].status+"")&&E(r,s),t&1&&p!==(p=((l=e[0].error)==null?void 0:l.message)+"")&&E(c,p)},i:$,o:$,d(e){e&&(m(a),m(o),m(n))}}}function P(i,a,s){let r;return k(i,C,o=>s(0,r=o)),[r]}class B extends q{constructor(a){super(),y(this,a,P,H,x,{})}}export{B as component};

5
_app/immutable/nodes/2.C2nigCiV.js Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
_app/version.json Обычный файл
Просмотреть файл

@ -0,0 +1 @@
{"version":"1728748945060"}

Просмотреть файл

@ -1,23 +0,0 @@
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
/** @type {import('eslint').Linter.Config[]} */
export default [
js.configs.recommended,
...svelte.configs['flat/recommended'],
prettier,
...svelte.configs['flat/prettier'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
ignores: ['build/', '.svelte-kit/', 'dist/']
}
];

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.5 KiB

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -1,7 +0,0 @@
export default [
{
//"id": "1tSZ6hKWKR0u3pIxvm3L_6SO3ab2yqjM8ww7GMBU3EwE",
"id": "1sDuhhouZ1IYsi355slAtY2SeF6781vO8AvhVL-EE_3A",
"filepath": "src/data/copy.json"
}
]

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 79 KiB

После

Ширина:  |  Высота:  |  Размер: 79 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 188 KiB

После

Ширина:  |  Высота:  |  Размер: 188 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 139 KiB

После

Ширина:  |  Высота:  |  Размер: 139 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 99 KiB

После

Ширина:  |  Высота:  |  Размер: 99 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 86 KiB

После

Ширина:  |  Высота:  |  Размер: 86 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 184 KiB

После

Ширина:  |  Высота:  |  Размер: 184 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 76 KiB

После

Ширина:  |  Высота:  |  Размер: 76 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 120 KiB

После

Ширина:  |  Высота:  |  Размер: 120 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 125 KiB

После

Ширина:  |  Высота:  |  Размер: 125 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 102 KiB

После

Ширина:  |  Высота:  |  Размер: 102 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 75 KiB

После

Ширина:  |  Высота:  |  Размер: 75 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 77 KiB

После

Ширина:  |  Высота:  |  Размер: 77 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 81 KiB

После

Ширина:  |  Высота:  |  Размер: 81 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 44 KiB

После

Ширина:  |  Высота:  |  Размер: 44 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 160 KiB

После

Ширина:  |  Высота:  |  Размер: 160 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 68 KiB

После

Ширина:  |  Высота:  |  Размер: 68 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 99 KiB

После

Ширина:  |  Высота:  |  Размер: 99 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 66 KiB

После

Ширина:  |  Высота:  |  Размер: 66 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 108 KiB

После

Ширина:  |  Высота:  |  Размер: 108 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 89 KiB

После

Ширина:  |  Высота:  |  Размер: 89 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 107 KiB

После

Ширина:  |  Высота:  |  Размер: 107 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 75 KiB

После

Ширина:  |  Высота:  |  Размер: 75 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 93 KiB

После

Ширина:  |  Высота:  |  Размер: 93 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 4.7 KiB

После

Ширина:  |  Высота:  |  Размер: 4.7 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 87 KiB

После

Ширина:  |  Высота:  |  Размер: 87 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 160 KiB

После

Ширина:  |  Высота:  |  Размер: 160 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 136 KiB

После

Ширина:  |  Высота:  |  Размер: 136 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 177 KiB

После

Ширина:  |  Высота:  |  Размер: 177 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 133 KiB

После

Ширина:  |  Высота:  |  Размер: 133 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 97 KiB

После

Ширина:  |  Высота:  |  Размер: 97 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 106 KiB

После

Ширина:  |  Высота:  |  Размер: 106 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 104 KiB

После

Ширина:  |  Высота:  |  Размер: 104 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 164 KiB

После

Ширина:  |  Высота:  |  Размер: 164 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 37 KiB

После

Ширина:  |  Высота:  |  Размер: 37 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 64 KiB

После

Ширина:  |  Высота:  |  Размер: 64 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 73 KiB

После

Ширина:  |  Высота:  |  Размер: 73 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 85 KiB

После

Ширина:  |  Высота:  |  Размер: 85 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 82 KiB

После

Ширина:  |  Высота:  |  Размер: 82 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 108 KiB

После

Ширина:  |  Высота:  |  Размер: 108 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 67 KiB

После

Ширина:  |  Высота:  |  Размер: 67 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 78 KiB

После

Ширина:  |  Высота:  |  Размер: 78 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 200 KiB

После

Ширина:  |  Высота:  |  Размер: 200 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 131 KiB

После

Ширина:  |  Высота:  |  Размер: 131 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 100 KiB

После

Ширина:  |  Высота:  |  Размер: 100 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 160 KiB

После

Ширина:  |  Высота:  |  Размер: 160 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 213 KiB

После

Ширина:  |  Высота:  |  Размер: 213 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 5.8 KiB

После

Ширина:  |  Высота:  |  Размер: 5.8 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 673 B

После

Ширина:  |  Высота:  |  Размер: 673 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.2 KiB

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 365 KiB

После

Ширина:  |  Высота:  |  Размер: 365 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 67 KiB

После

Ширина:  |  Высота:  |  Размер: 67 KiB

54
index.html Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 6.9 KiB

После

Ширина:  |  Высота:  |  Размер: 6.9 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 4.9 KiB

После

Ширина:  |  Высота:  |  Размер: 4.9 KiB

2882
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,36 +0,0 @@
{
"name": "interference2024",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"gdoc": "node tasks/fetch-google.js"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^9.6.0",
"archieml": "^0.5.0",
"d3-array": "^3.2.4",
"d3-fetch": "^3.0.1",
"d3-format": "^3.1.0",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
"d3-time-format": "^4.1.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.2.7",
"vite": "^5.0.3"
},
"type": "module"
}

Просмотреть файл

@ -1,51 +0,0 @@
export function slidable(node) {
let x;
let left;
function handleMousedown(event) {
x = event.clientX;
node.dispatchEvent(
new CustomEvent('slidestart', {
detail: { x },
})
);
window.addEventListener('mousemove', handleMousemove);
window.addEventListener('mouseup', handleMouseup);
}
function handleMousemove(event) {
const dx = event.clientX - x;
x = event.clientX;
node.dispatchEvent(
new CustomEvent('slide', {
detail: { x, dx },
})
);
}
function handleMouseup(event) {
x = event.clientX;
left = node.offsetLeft;
node.dispatchEvent(
new CustomEvent('slideend', {
detail: { x, left },
})
);
window.removeEventListener('mousemove', handleMousemove);
window.removeEventListener('mouseup', handleMouseup);
}
node.addEventListener('mousedown', handleMousedown);
return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
},
};
}

Просмотреть файл

@ -1,19 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/versions/bulma-no-dark-mode.css">
<link href="https://fonts.googleapis.com/css2?family=Volkhov:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

Просмотреть файл

@ -1 +0,0 @@
{"meta":{"title":"Interference 2024","subtitle":"Foreign Interference Attribution Tracker","og_site_name":"Interference Tracker 2024","og_description":"The DFRLab's Foreign Interference Attribution Tracker (FIAT) is an interactive, open-source database that captures allegations of foreign interference relevant to the 2024 election.","og_url":"https://interference2020.org/","og_image":""},"intro":[{"id":"intro","type":"text","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"},{"id":"overview","type":"concealed-text","title":"Overview","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"},{"id":"how-to-use","type":"concealed-text","title":"How To Use This Tool","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"}],"moreInfo":[{"id":"methodology","type":"concealed-text","title":"Methodology","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"},{"id":"case-selection","type":"concealed-text","title":"Case Selection","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"},{"id":"about","type":"text","label":"About This Project","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"},{"id":"about-dfrlab","type":"text","title":"About The DFRLab","text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."}]}

Просмотреть файл

@ -1,69 +0,0 @@
<script>
export let sidebarOpen
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const upperY = tweened(28, {
duration: 500,
easing: cubicOut
});
const lowerY = tweened(100, {
duration: 500,
easing: cubicOut
});
const strokeWidth = tweened(14, {
duration: 500,
easing: cubicOut
});
const circleRadius = tweened(0, {
duration: 500,
easing: cubicOut
});
$: if(sidebarOpen){
upperY.set(140)
lowerY.set(28)
strokeWidth.set(0)
circleRadius.set(0)
}
$: if(!sidebarOpen){
upperY.set(28)
lowerY.set(140)
strokeWidth.set(14)
circleRadius.set(14)
}
</script>
<div class="hamburger-container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 160">
<g id="home" class={"dark"}>
<line x1={8} y1={$upperY} x2={120} y2={28}/>
<circle cx={44} cy={28} r={$circleRadius} style:stroke-width={$strokeWidth}></circle>
<line x1={8} y1={80} x2={120} y2={80} style:stroke-width={$strokeWidth}/>
<circle cx={100} cy={80} r={$circleRadius} style:stroke-width={$strokeWidth}></circle>
<line x1={8} y1={$lowerY} x2={120} y2={140}/>
<circle cx={70} cy={140} r={$circleRadius} style:stroke-width={$strokeWidth}></circle>
</g>
</svg>
</div>
<style>
.hamburger-container {
width: 20px;
height: 20px;
float: right;
}
line {
transition: stroke 1s;
stroke-width: 14;
stroke-linecap: round;
}
circle {
stroke-width: 14;
fill: white;
}
.dark {
stroke: #000000;
}
</style>

Просмотреть файл

@ -1,41 +0,0 @@
<script>
export let cx;
export let cy;
export let r;
export let fill;
export let opacity;
export let stroke;
export let strokeWidth;
export let caseData;
export let hoveredCaseData;
export let tooltipX;
export let tooltipY;
export let showTooltip;
export let tooltipType;
function handleMouseOver(event) {
tooltipType = 'case'
showTooltip = true;
tooltipX = event.clientX;
tooltipY = event.clientY;
hoveredCaseData = caseData;
}
function handleMouseOut() {
//showTooltip = false;
}
</script>
<circle
{cx}
{cy}
{r}
{fill}
{opacity}
{stroke}
stroke-width={strokeWidth}
stroke-opacity={1}
on:mouseover={handleMouseOver}
on:focus={handleMouseOver}
on:mouseout={handleMouseOut}
on:blur={handleMouseOut}
/>

Просмотреть файл

@ -1,20 +0,0 @@
<script>
import CaseCard from "./CaseCard.svelte";
export let modalOpen
export let activeCaseData
function closeModal(){
modalOpen = false
}
</script>
<div id="card-modal" class={modalOpen ? "modal is-active" : "modal"}>
<div class="modal-background" on:click={closeModal}></div>
<div class="modal-content">
{#if activeCaseData}
<CaseCard cardData={activeCaseData} expanded={true} ></CaseCard>
{/if}
</div>
<div class="modal-close" on:click={closeModal}></div>
</div>

Просмотреть файл

@ -1,169 +0,0 @@
<script>
import { fade } from 'svelte/transition';
import { utcFormat } from 'd3-time-format';
import { platformFilter, actorNationFilter, sourceFilter } from '../../stores/filters';
import ScoreBar from '$lib/components/ScoreBar.svelte';
import ScoreQuestions from '$lib/components/ScoreQuestions.svelte';
import { slide } from 'svelte/transition';
export let cardData;
export let expanded;
export let modalOpen;
export let activeCaseData;
let openCase = function () {
modalOpen = true;
activeCaseData = cardData;
};
let scoreQuestionsExpanded = false;
</script>
<div class="card" transition:fade>
<div class="header">
<div class="card-header-title">
<h2 class="is-size-5">{cardData.short_title}</h2>
</div>
{#if expanded}
<div class="card-content">
<div class="score-bars">
<div class="score-bar-wrapper">
<ScoreBar value={cardData.credibility} maxValue={5} />
<p>Credibility</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.objectivity} maxValue={3} />
<p>Objectivity</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.evidence} maxValue={5} />
<p>Evidence</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.transparency} maxValue={5} />
<p>Transparency</p>
</div>
<span
class="score-info-icon disable-select"
on:click|self={() => (scoreQuestionsExpanded = !scoreQuestionsExpanded)}
>
{scoreQuestionsExpanded ? 'X' : '?'}
</span>
</div>
{#if scoreQuestionsExpanded}
<div class="score-questions-container" transition:slide|local>
<ScoreQuestions {cardData}></ScoreQuestions>
</div>
{/if}
</div>
{/if}
</div>
<div class="card-image">
<figure class="image">
<img src={`/images/${cardData.attribution_id}.jpg`} />
</figure>
{#if expanded}
<div class="image-credit">
Image: <a
href={cardData.image_credit_url == 'attribution_url'
? cardData.attribution_url_x
: cardData.image_credit_url}
target="_blank">{cardData.image_credit}</a
>
</div>
{/if}
</div>
<div class="card-content">
<div class="content">
{#if expanded}
<p>{utcFormat('%B %-d, %Y')(new Date(cardData.attribution_date))}</p>
<p><a href={cardData.attribution_url_x} target="_blank">{cardData.source}</a></p>
{/if}
<p>{cardData.short_description}</p>
{#if expanded}
<p>
<button class="button is-info is-small" on:click={sourceFilter.selectOne(cardData.source)}
>{cardData.source}</button
>
{#each cardData.actor_nation as nation}
<button class="button is-danger is-small" on:click={actorNationFilter.selectOne(nation)}
>{nation}</button
>
{/each}
{#each cardData.platform as platform}
<button class="button is-link is-small" on:click={platformFilter.selectOne(platform)}
>{platform}</button
>
{/each}
</p>
{/if}
</div>
</div>
{#if !expanded}
<footer class="card-footer">
<button on:click={openCase(cardData.attribution_id)} class="card-footer-item"
>Open case</button
>
</footer>
{/if}
</div>
<style>
/*div.card {
max-width: 780px;
}*/
.image-credit {
padding: 0px 24px;
font-size: 0.8rem;
position: relative;
height: 24px;
top: -24px;
color: white;
background-color: black;
opacity: 0.5;
line-height: 1.7;
}
.image-credit a {
color: white;
text-decoration: underline;
}
.score-bars {
display: flex;
align-items: flex-start;
justify-content: space-between;
width: 100%;
}
.score-bars span.score-info-icon {
width: 1.3rem;
height: 1.3rem;
margin: 0;
padding: 0 auto 0.1rem auto;
font-size: 0.8rem;
font-weight: bold;
text-align: center;
color: var(--usa-lightred);
border: 2px solid var(--text-darkgray);
border-radius: 2px;
background-color: var(--text-darkgray);
transition: all 400ms ease;
cursor: pointer;
}
.score-bars span.score-info-icon:hover {
color: var(--text-darkgray);
background-color: var(--usa-lightred);
}
.score-bar-wrapper {
flex: 1 1 0;
display: inline-block;
}
.score-bar-wrapper p {
font-size: 0.7rem;
}
button {
margin: 0.2rem;
}
</style>

Просмотреть файл

@ -1,53 +0,0 @@
<script>
import { utcFormat } from 'd3-time-format';
export let cases;
</script>
<div class="table-container">
<table class="table is-striped is-fullwidth">
<thead>
<tr>
<td>Title</td>
<td>Description</td>
<td>Attribution date</td>
<td>Source</td>
<td>Source category</td>
<td>Actor nation</td>
<td>Campaign</td>
</tr>
</thead>
<tbody>
{#each cases as attrCase}
{#if attrCase.show}
<tr id={'case-' + attrCase.attribution_id}>
<td>{attrCase.short_title}</td>
<td>{attrCase.short_description}</td>
<td>{utcFormat('%B %d, %Y')(new Date(attrCase.attribution_date))}</td>
<td>{attrCase.source}</td>
<td>{attrCase.source_category}</td>
<td
>{#each attrCase.actor_nation as nation, i}
{attrCase.actor_nation.length != i + 1 ? nation + ', ' : nation}
{/each}</td
>
<td>
{#each attrCase.campaign as camp, i}
{attrCase.campaign.length != i + 1 ? camp + ', ' : camp}
{/each}
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
<style>
thead {
font-weight: bold;
}
.table-container {
max-height: 800px;
overflow-y: scroll;
}
</style>

Просмотреть файл

@ -1,68 +0,0 @@
<script>
export let displayDataAs
export let selectedSorting
const sortOptions = [
{ id: 'attribution_date', label: 'Attribution Date', type: 'date' },
{ id: 'attribution_score', label: 'Attribution Score', type: 'number' },
{ id: 'actor_nation', label: 'Actor Nation', type: 'array' },
{ id: 'platform', label: 'Platform', type: 'array' },
{ id: 'source', label: 'Source', type: 'array' },
{ id: 'source_category', label: 'Source Category', type: 'string' }
];
</script>
<div class="container cases-controls">
<div class="cases-control">
<div class="buttons has-addons">
<button
class={displayDataAs == 'Table'
? 'button is-dark is-selected is-small'
: 'button is-small'}
on:click={() => {
displayDataAs = 'Table';
}}>Table</button
>
<button
class={displayDataAs == 'Cards'
? 'button is-dark is-selected is-small'
: 'button is-small'}
on:click={() => {
displayDataAs = 'Cards';
}}>Cards</button
>
</div>
</div>
<div class="cases-control">
<label for="sort-select" class="sort-label">Sort cases by </label>
<div class="select is-small">
<select bind:value={selectedSorting} id="sort-select">
{#each sortOptions as sortOpt}
<option value={sortOpt}>
{sortOpt.label}
</option>
{/each}
</select>
</div>
</div>
<div class="cases-control">
<a
href="https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv"
class="button is-small">Download the data</a
>
</div>
</div>
<style>
.cases-controls {
margin-bottom: 1rem;
}
.cases-control {
display: inline-block;
margin-right: 3rem;
}
.sort-label {
font-size: 0.9rem;
}
</style>

Просмотреть файл

@ -1,47 +0,0 @@
<script>
// custom checkbox
export let id;
export let checked = false;
</script>
<input type="checkbox"
id="checkbox-{id}"
class:checked
on:click|stopPropagation />
<label for="checkbox-{id}" class="choice-entry-name">
<slot></slot>
</label>
<style>
label {
display: inline-block;
width: 100%;
padding-left: 1.5rem;
cursor: pointer;
pointer-events: all;
}
input[type="checkbox"] {
display: none;
pointer-events: all;
}
input[type="checkbox"] + label::before {
display: block;
width: 15px;
height: 15px;
margin-top: 3px;
margin-right: 0.4rem;
border: 2px solid var(--usa-blue);
border-radius: 3px;
background-color: var(--bg);
content: "";
position: absolute;
left: 0.5rem;
}
input[type="checkbox"].checked + label::before {
box-shadow: inset 0px 0px 0px 3px var(--bg);
background-color: var(--usa-blue);
}
</style>

Просмотреть файл

@ -1,77 +0,0 @@
<script>
export let title
export let text
export let id
</script>
<input id={id} class="toggle" type="checkbox" />
<label for={id} class="lbl-toggle top">{title}</label>
<div class="collapsible-content">
<p>{@html text}</p>
</div>
<style>
input.toggle[type='checkbox'] {
display: none;
}
.lbl-toggle {
display: block;
margin-left: -0.3rem;
padding: 0.2rem 0;
font-size: 0.9rem;
font-weight: normal;
font-style: italic;
color: var(--text-black);
cursor: pointer;
transition: all 200ms ease;
}
.lbl-toggle.top {
font-size: 0.85rem;
font-weight: normal;
font-style: normal;
}
.lbl-toggle::before {
content: ' ';
display: inline-block;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid currentColor;
vertical-align: middle;
margin-top: 2px;
transform: translateX(-0.6rem) translateY(-2px);
transition: transform 200ms ease-out;
}
.collapsible-content {
max-height: 0px;
overflow: hidden;
transition: max-height 200ms ease-in-out;
}
.collapsible-content h4,
.collapsible-content h5 {
margin: 1.2rem 1rem 0 1rem;
}
.collapsible-content p {
padding: 0 1rem;
}
.toggle:checked + .lbl-toggle + .collapsible-content {
max-height: 10000px;
border: 1px solid var(--dfrlab-lightgray);
border-radius: 3px;
}
.toggle:checked + .lbl-toggle::before {
transform: rotate(90deg) translateY(0.6rem);
}
.toggle:checked + .lbl-toggle {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
</style>

Просмотреть файл

@ -1,164 +0,0 @@
<script>
import Dropdown from '$lib/components/Dropdown.svelte';
import Slider from '$lib/components/Slider.svelte';
import DateRangeSlider from '$lib/components/DateRangeSlider.svelte';
import SearchText from '$lib/components/SearchText.svelte';
import Share from '$lib/components/Share.svelte';
import { utcFormat } from 'd3-time-format';
//import { attributionScoreScale } from '../../stores/scales';
import {
platformFilter,
actorNationFilter,
sourceFilter,
sourceCategoryFilter,
methodFilter,
campaignFilter,
selectAllFilters,
attributionScoreFilter,
attributionScoreDef,
textSearchFilter,
timeRangeFilter,
fullTimeRange,
defaultTimeRange
} from '../../stores/filters';
const timeFormat = utcFormat('%m/%d/%y')
export let cases;
$: timeDummyRange = ($defaultTimeRange[0] - $fullTimeRange[0])/($fullTimeRange[1] - $fullTimeRange[0])
? [($defaultTimeRange[0] - $fullTimeRange[0])/($fullTimeRange[1] - $fullTimeRange[0])*10, 10]
: [0, 10]
function handleButtonClick() {
selectAllFilters();
timeRangeFilter.set($defaultTimeRange);
//timeDummyRange = [0, 10]
timeDummyRange = [($defaultTimeRange[0] - $fullTimeRange[0])/($fullTimeRange[1] - $fullTimeRange[0])*10, 10]
/*contextData.unselectAll();
$highlightPolarization = false;
$highlightCib = false;
if ($originalTimeDomain) {
$timeScale.domain($originalTimeDomain);
$timeScale = $timeScale;
$originalTimeDomain = null;
}*/
}
function addCount(filter, property, cases) {
return filter.map((d) => ({
...d,
count: cases
.map((d) => d[property])
.flat()
.filter((a) => a === d.id).length,
liveCount: cases
.filter((d) => d.show)
.map((d) => d[property])
.flat()
.filter((a) => a === d.id).length
}));
}
</script>
{#if cases}
<div class={"controls-wrapper"}>
<div class="grid is-col-min-8">
<SearchText
searchString={$textSearchFilter}
label="Search"
on:change={(e) => ($textSearchFilter = e.detail)}
on:reset={() => textSearchFilter.reset()}
/>
<Slider
value={$attributionScoreFilter}
label={`Attribution Score: ${$attributionScoreFilter[0]} - ${$attributionScoreFilter[1]}`}
min={attributionScoreDef[0]}
max={attributionScoreDef[1]}
showHandleLabels={false}
startColor={'#ffffff'}
stopColor={'#000000'}
on:changed={(e) => ($attributionScoreFilter = e.detail)}
/>
<Dropdown
items={addCount($actorNationFilter, 'actor_nation', cases)}
label="Actor Nation"
on:itemsAdded={(e) => actorNationFilter.select(e.detail)}
on:itemsRemoved={(e) => actorNationFilter.unselect(e.detail)}
></Dropdown>
<Dropdown
items={addCount($platformFilter, 'medium', cases)}
label="Platform"
on:itemsAdded={(e) => platformFilter.select(e.detail)}
on:itemsRemoved={(e) => platformFilter.unselect(e.detail)}
></Dropdown>
<Dropdown
items={addCount($sourceFilter, 'source', cases)}
label="Source"
on:itemsAdded={(e) => sourceFilter.select(e.detail)}
on:itemsRemoved={(e) => sourceFilter.unselect(e.detail)}
></Dropdown>
<Dropdown
items={addCount($sourceCategoryFilter, 'source_category', cases)}
label="Source Category"
on:itemsAdded={(e) => sourceCategoryFilter.select(e.detail)}
on:itemsRemoved={(e) => sourceCategoryFilter.unselect(e.detail)}
></Dropdown>
<Dropdown
items={addCount($methodFilter, 'methods', cases)}
label="Method"
on:itemsAdded={(e) => methodFilter.select(e.detail)}
on:itemsRemoved={(e) => methodFilter.unselect(e.detail)}
></Dropdown>
<Dropdown
items={addCount($campaignFilter, 'campaign', cases)}
label="Campaign"
on:itemsAdded={(e) => campaignFilter.select(e.detail)}
on:itemsRemoved={(e) => campaignFilter.unselect(e.detail)}
></Dropdown>
<DateRangeSlider
value={timeDummyRange}
label={`Date Range: ${timeFormat($timeRangeFilter[0])} - ${timeFormat($timeRangeFilter[1])}`}
min={0}
max={10}
showHandleLabels={false}
startColor={'#ffffff'}
stopColor={'#000000'}
/>
<button class="reset-filters" on:click={() => handleButtonClick()}> Reset </button>
<Share />
</div>
</div>
{/if}
<style>
button {
pointer-events: all;
}
button.reset-filters {
align-self: flex-end;
min-width: 100px;
height: 1.7rem;
max-height: 1.7rem;
margin: 0.3rem 0.3rem 0 0.3rem;
padding: 0.1rem 0.3rem;
font-family: var(--font-02);
font-size: 0.8rem;
font-weight: normal;
line-height: 1.3rem;
color: var(--usa-blue);
background-color: var(--bg);
border: 2px solid var(--usa-blue);
border-radius: 3px;
outline: none;
overflow: hidden;
transition: all 200ms ease;
}
button.reset-filters:hover {
color: var(--bg);
background-color: var(--usa-blue);
cursor: pointer;
}
</style>

Просмотреть файл

@ -1,162 +0,0 @@
<script>
// a custom slider for time range selection
import { createEventDispatcher } from 'svelte';
import { scaleLinear, scaleTime } from 'd3-scale';
import { slidable } from '../../actions/slidable';
import { timeRangeFilter, fullTimeRange, defaultTimeRange } from '../../stores/filters';
export let lockInMode = false;
export let label = '';
export let showLabel = true;
export let min = 0;
export let max = 10;
export let value;
export let showHandleLabels = true;
export let startColor = 'white';
export let middleColor = null;
export let stopColor = 'rgb(255, 0, 0)';
export let barOpacity = 1;
export let showBorder = true;
// Convert the numeric value numbers to dates
$: convertScale = scaleTime().domain($defaultTimeRange).range(value)
const dispatch = createEventDispatcher();
const handleWidth = 17;
const pos = {
left: 0,
right: 0
};
let sliderWidth = 0;
function handleSlide(e, side) {
const newPos = pos[side] + e.detail.dx;
if (newPos < 0 || newPos > sliderWidth) return;
if (side === 'left' && newPos > pos.right) return;
if (side === 'left' && newPos < scale.range()[0]) return;
if (side === 'right' && newPos < pos.left) return;
if (side === 'right' && newPos > scale.range()[1]) return;
pos[side] = newPos;
}
function handleSlideEnd(e, side) {
if (lockInMode) {
dispatch('changed', [Math.round(scale.invert(pos.left), 0),
Math.round(scale.invert(pos.right), 0)]);
} else {
dispatch('changed', [scale.invert(pos.left), scale.invert(pos.right)]);
$timeRangeFilter = [convertScale.invert(scale.invert(pos.left)), convertScale.invert(scale.invert(pos.right))]
}
}
$: scale = scaleLinear()
.domain([min, max])
.range([handleWidth / 2, sliderWidth - 1.7 * handleWidth]);
$: pos.left = scale(value[0]) || 0;
$: pos.right = scale(value[1]) || 0;
$: background = `linear-gradient(90deg, ${startColor}, ${middleColor ? middleColor + ', ' : ''}${stopColor})`;
//$: background = `linear-gradient(90deg, #ffffff, #000000)`;
</script>
<div class="slider"
bind:clientWidth={sliderWidth}
style="--handle-width: {handleWidth}px;">
{#if (showLabel)}
<div class="label">
{label}
</div>
{/if}
<div class="slider-body" class:border={showBorder}>
<div class="slider-selected-range"
style="width: {sliderWidth - 2 * handleWidth}px;
margin-left: {1 * handleWidth}px;
opacity: {barOpacity};
background: {background};"></div>
<div class="slider-handle"
class:no-label={!showHandleLabels}
style="left: {(Math.abs(value[0] - value[1]) < 0.1) ? pos.left - 5 : pos.left}px;"
use:slidable
on:slide={(e) => handleSlide(e, 'left')}
on:slideend={(e) => handleSlideEnd(e, 'left')}>
<span class="disable-select">{showHandleLabels ? Math.round(scale.invert(pos.left), 0) : ''}</span>
</div>
<div class="slider-handle"
class:no-label={!showHandleLabels}
style="left: {(Math.abs(value[0] - value[1]) < 0.1) ? pos.right + 5 : pos.right}px;"
use:slidable
on:slide={(e) => handleSlide(e, 'right')}
on:slideend={(e) => handleSlideEnd(e, 'right')}>
<span class="disable-select">{showHandleLabels ? Math.round(scale.invert(pos.right), 0) : ''}</span>
</div>
</div>
</div>
<style>
.slider {
display: flex;
flex-direction: column;
font-family: var(--font-02);
width: 200px;
max-width: 200px;
margin: 0.3rem 0.3rem 0 0.3rem;
position: relative;
pointer-events: all;
}
.label {
margin: 0 0 0.1rem 0;
font-size: 0.7rem;
color: var(--usa-blue);
}
.slider-body {
display: flex;
align-items: center;
width: 100%;
height: 1.7rem;
padding: 0.1rem 0;
font-size: 0.7rem;
background-color: var(--bg);
border: none;
position: relative;
}
.border {
border: 2px solid var(--usa-blue);
border-radius: 3px;
}
.slider-selected-range {
height: 8px;
border: none;
border-radius: 2px;
position: absolute;
z-index: 100;
}
.slider-handle {
width: var(--handle-width);
height: var(--handle-width);
border: 2px solid var(--usa-blue);
border-radius: 50%;
background-color: var(--bg);
cursor: pointer;
position: absolute;
z-index: 10000;
}
.slider-handle > span {
width: 100%;
height: 100%;
font-size: 0.7rem;
text-align: center;
color: var(--usa-blue);
position: absolute;
}
</style>

Просмотреть файл

@ -1,241 +0,0 @@
<script>
// a custom dropdown
import { createEventDispatcher } from 'svelte';
import { slide } from 'svelte/transition';
import { sortConsistently } from '$lib/utils/misc';
import Checkbox from '$lib/components/Checkbox.svelte';
export let items = [];
export let label = '';
export let nameField = 'id';
export let hideOneHitWonders = false;
export let superior = false;
const dispatch = createEventDispatcher();
let elem;
let expanded = false;
function handleBodyClick() {
expanded = false
}
function toggleExpanded() {
expanded = !expanded;
}
function selectAll() {
dispatch('itemsAdded', items.map((d) => d.id));
}
function unselectAll() {
dispatch('itemsRemoved', items.map((d) => d.id));
}
function handleDropdownClick() {
toggleExpanded();
}
function handleChoiceClick(id) {
if (!items.filter((d) => d.selected).map((d) => d.id).includes(id)) {
dispatch('itemsAdded', id);
} else {
dispatch('itemsRemoved', id);
}
}
</script>
<svelte:body on:click={(e) => handleBodyClick(e)}></svelte:body>
<div class="dropdown" bind:this={elem}>
<div class="label">
{label}
</div>
<div class="selected-items" on:click|stopPropagation={handleDropdownClick}>
<span class="selected-items-icon"></span>
<span class="selected-items-text">
{items.filter((d) => d.selected).length === 0
? 'none'
: (items.every((d) => d.selected && items.length > 1)
? 'all'
: items.filter((d) => d.selected).map((d) => d[nameField]).join(', '))}
</span>
<button class="selected-items-arrow">
<svg class:expanded width="15" height="10">
<path d="M0 0L15 0L7.5 10Z"></path>
</svg>
</button>
</div>
<div class="choice-wrapper">
{#if (expanded)}
<div class="choice" transition:slide class:superior>
<div class="choice-controls">
<button class="choice-controls-selectall" on:click|stopPropagation={selectAll}>Select all</button>
<button class="choice-controls-unselectall" on:click|stopPropagation={unselectAll}>Unselect all</button>
</div>
<ul class="choice-list">
{#each items.sort((a, b) => -sortConsistently(a, b, 'id', 'id')) as item, i (item.id)}
{#if (!(hideOneHitWonders && item.count === 1))}
<li on:click|stopPropagation>
<Checkbox id="{label}-{i}"
checked={item.selected}
on:click={() => handleChoiceClick(item.id)}>
<span class="choice-entry-name">{item[nameField]}</span>
{#if (item.liveCount)}
<span class="choice-entry-count">({item.liveCount})</span>
{:else if (item.source)}
<span class="choice-entry-source">({item.source})</span>
{/if}
</Checkbox>
</li>
{/if}
{/each}
</ul>
{#if (hideOneHitWonders)}
<p class="info">{label}s with only one result in the dataset are hidden.</p>
{/if}
</div>
{/if}
</div>
</div>
<style>
.dropdown {
align-self: flex-end;
display: flex;
flex-direction: column;
font-family: var(--font-02);
width: 200px;
max-width: 200px;
min-width: 200px;
margin: 0.3rem 0.3rem 0 0.3rem;
position: relative;
pointer-events: all;
}
.label {
margin: 0 0 0.1rem 0;
font-size: 0.7rem;
color: var(--usa-blue);
}
.selected-items {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 1.7rem;
padding: 0.1rem 0.3rem;
font-size: 0.8rem;
background-color: var(--bg);
border: 2px solid var(--usa-blue);
border-radius: 3px;
cursor: pointer;
}
.selected-items-text {
width: 100%;
color: var(--text-black);
overflow: hidden;
white-space: nowrap;
}
.selected-items-arrow {
background: none;
border: none;
outline: none;
cursor: pointer;
}
.selected-items-arrow svg {
margin-top: 0.2rem;
stroke: none;
fill: var(--usa-blue);
transition: transform 400ms ease;
}
.selected-items-arrow svg.expanded{
transform: rotate(-540deg);
}
.choice-wrapper {
width: 100%;
height: 0;
position: relative;
}
.choice {
width: 100%;
border: 2px solid var(--usa-lightblue);
border-radius: 3px;
background-color: var(--bg);
position: absolute;
z-index: 12000;
top: 0;
}
.superior {
z-index: 100000 !important;
}
.choice-controls {
display: flex;
align-items: center;
width: 100%;
}
.choice-controls button {
margin: 0.5rem;
padding: 0.2rem 0.5rem;
color: var(--bg);
border: none;
border-radius: 5px;
outline: none;
cursor: pointer;
}
button.choice-controls-selectall {
background-color: var(--usa-lightblue);
font-size: 0.8rem;
}
button.choice-controls-unselectall {
background-color: var(--usa-lightred);
font-size: 0.8rem;
}
ul.choice-list {
width: 100%;
max-height: 600px;
overflow-y: scroll;
list-style-type: none;
font-size: 0.8rem;
position: relative;
}
ul.choice-list li {
padding: 0.4rem 0.5rem;
cursor: pointer;
background-color: var(--bg);
transition: background-color 200ms ease;
}
ul.choice-list li:hover {
background-color: var(--usa-lightblue);
}
.choice-entry-count, .choice-entry-source {
font-size: 0.8em;
}
.choice-entry-source {
display: block;
}
p.info {
padding: 0.4rem 0.5rem;
font-size: 0.7rem;
color: var(--dfrlab-gray);
}
</style>

Просмотреть файл

@ -1,53 +0,0 @@
<script>
import { fade } from 'svelte/transition';
import { utcFormat } from 'd3-time-format';
export let tooltipX;
export let tooltipY;
export let hoveredEventData;
export let width;
</script>
<div
transition:fade="{{ duration: 250 }}"
class="event-tooltip"
style="
top:{tooltipY}px;
left:{tooltipX < width - 300 ? tooltipX + 10 : tooltipX - 300 - 10}px;
"
>
<p class="date">{utcFormat('%B %d, %Y')(hoveredEventData.date)}</p>
<h2>{hoveredEventData.Title}</h2>
<p class="description">{hoveredEventData.Description}</p>
</div>
<style>
.event-tooltip {
position: fixed;
/*border: 1px solid #ccc;*/
background: rgba(255, 255, 255, 0.95);
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
padding: 5px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
transform: translate(0%, -50%);
z-index: 1000;
max-width: 300px;
max-height: 400px;
overflow-y: scroll;
}
.date {
font-size: 0.7rem;
}
h2 {
margin: 0.2rem 0;
font-size: 0.9rem;
font-weight: bold;
}
.description {
font-size: 0.8rem;
line-height: 1.5;
}
</style>

Просмотреть файл

@ -1,64 +0,0 @@
<script>
import copy from '../../data/copy.json';
</script>
<section class="content title section" style:background-image={"url('images/fiat_2024_banner_background.jpg')"}>
<div class="container has-text-centered">
<div class="logos">
<a href="https://www.atlanticcouncil.org"
><img src="logos/ac.svg" alt="Atlantic Council Logo" /></a
>
<a
class="smaller"
href="https://www.atlanticcouncil.org/programs/digital-forensic-research-lab/"
><img src="logos/dfrlab.svg" alt="Digital Forensic Research Lab Logo" /></a
>
</div>
<h1 class="title is-size-1">{copy.meta.title}</h1>
<h2 class="subtitle is-size-4 has-text-weight-bold">{copy.meta.subtitle}</h2>
</div>
</section>
<style>
section.content.title {
padding-top: 1rem;
position: relative;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
max-width: 800px;
margin: auto;
}
section.title .logos {
display: flex;
align-items: center;
justify-content: center;
height: 2.2rem;
}
section.title .logos a {
height: 100%;
}
section.title .logos a:not(:last-child) {
margin-right: 1rem;
}
section.title .logos a img {
height: 100%;
}
section.title .logos a.smaller {
height: 80%;
}
.title {
font-family: var(--font-01);
color: var(--usa-blue);
}
.subtitle {
font-family: var(--font-02);
color: var(--usa-blue);
}
</style>

Просмотреть файл

@ -1,70 +0,0 @@
<script>
export let width
export let margins
export let radiusScale
export let opacityScale
$: minOpacity = opacityScale.domain()[0]
$: maxOpacity = opacityScale.domain()[1]
$: opacityRange = maxOpacity - minOpacity
$: opacities = [
minOpacity,
minOpacity + opacityRange/5,
minOpacity + opacityRange*2/5,
minOpacity + opacityRange*3/5,
minOpacity + opacityRange*4/5,
maxOpacity]
let height = 60
</script>
<svg {width} height={height}>
<g transform={`translate(${margins.left},${0})`}>
<text
x={72}
y={12}
text-anchor={'middle'}
>Breakout scale</text>
{#each radiusScale.domain() as rad,i}
<circle
cx={12 - radiusScale(rad) + i*30}
cy={32}
r={radiusScale(rad)}
fill={'#555555'}
></circle>
{/each}
<text
x={(width - margins.left - margins.right)/2}
y={12}
text-anchor={'middle'}
>Offline mobilization</text>
<circle
cx={(width - margins.left - margins.right)/2}
cy={32}
r={12}
fill={'none'}
stroke={'#000000'}
stroke-width={1.5}
></circle>
<circle
cx={(width - margins.left - margins.right)/2}
cy={32}
r={9}
fill={'#555555'}
></circle>
<text
x={width - margins.left - margins.right - 72}
y={12}
text-anchor={'middle'}
>Attribution score</text>
{#each opacities as op,i}
<circle
cx={width - margins.left - margins.right - 150 + i*30}
cy={32}
r={9}
fill={'#555555'}
opacity={opacityScale(op)}
></circle>
{/each}
</g>
</svg>

Просмотреть файл

@ -1,31 +0,0 @@
<script>
// a score bar used on case tooltips
export let value = 0;
export let minValue = 0;
export let maxValue = 1;
$: relState = (value - minValue) / (maxValue - minValue);
</script>
<div class="score-bar">
<span class="inner-score-bar"
style="width: {relState * 100}%;"></span>
</div>
<style>
.score-bar {
width: 100%;
max-width: 70px;
min-height: 10px;
border: 1px solid var(--text-darkgray);
border-radius: 3px;
position: relative;
}
.inner-score-bar {
height: 100%;
background-color: var(--text-darkgray);
border: none;
position: absolute;
}
</style>

Просмотреть файл

@ -1,86 +0,0 @@
<script>
import { questions } from '$lib/inputs/scores';
export let cardData;
</script>
<div class="score-questions">
{#each questions as quest}
<h4>{quest.label}: {cardData[quest.category]}/{quest.questions.length}</h4>
<ul>
{#each quest.questions as q}
<li>
<input type="checkbox" checked={cardData[q.column] == '1'} />
<span class="checkmark"></span>
<p>{q.label}</p>
</li>
{/each}
</ul>
{/each}
</div>
<style>
ul {
margin: 0.3rem 0;
list-style-type: none;
}
h4 {
margin: 1rem 0 0 0;
font-size: 0.8rem;
font-weight: bold;
color: var(--text-black);
}
li {
display: block;
margin: 0.3rem 0;
padding-left: 20px;
position: relative;
}
li p {
font-family: var(--font-02);
font-size: 0.8rem;
line-height: 1.5;
color: var(--text-black);
}
li input {
position: absolute;
opacity: 0;
height: 0;
width: 0;
}
.checkmark {
position: absolute;
top: 5px;
left: 0;
height: 15px;
width: 15px;
border: none;
border-radius: 2px;
background-color: var(--bg);
}
.checkmark:after {
content: '';
display: none;
position: absolute;
}
li input:checked ~ .checkmark:after {
display: block;
}
li .checkmark:after {
left: 5px;
top: 2px;
width: 3px;
height: 7px;
border: solid var(--usa-blue);
border-width: 0 3px 3px 0;
transform: rotate(45deg);
}
</style>

Просмотреть файл

@ -1,138 +0,0 @@
<script>
// free text search bar
import { createEventDispatcher } from 'svelte';
export let searchString = '';
export let label = '';
const dispatch = createEventDispatcher();
function reset() {
searchString = '';
search();
}
function handleKeyUp(e) {
if (e && e.keyCode === 13) search();
}
function handleGoClick() {
search();
}
function search() {
dispatch('change', searchString);
}
</script>
<div class="search-text">
<div class="label">
<p>{label}</p>
<p>|</p>
<span on:click={() => reset()}>Reset</span>
</div>
<div class="search">
<input id="table-search-field"
type="text"
placeholder="Type and press enter"
bind:value={searchString}
on:keyup={(e) => handleKeyUp(e)} />
{#if (searchString !== '')}
<span class="button-fields">
<span class="reset"
class:active={searchString}
on:click={() => searchString = ''}>
x
</span>
<span class="go"
class:active={searchString}
on:click={() => handleGoClick()}>
Go
</span>
</span>
{/if}
</div>
</div>
<style>
.search-text {
align-self: flex-end;
display: flex;
flex-direction: column;
font-family: var(--font-02);
width: 200px;
max-width: 200px;
min-width: 200px;
margin: 0.3rem 0.3rem 0 0.3rem;
position: relative;
pointer-events: all;
}
.label {
display: flex;
margin: 0 0 0.1rem 0;
font-size: 0.7rem;
color: var(--usa-blue);
}
.label > * {
padding-right: 0.2rem;
}
.label span {
margin: 0 -0.1rem;
padding: 0 0.1rem;
color: var(--usa-blue);
border: none;
border-radius: 3px;
cursor: pointer;
transition: all 200ms ease;
}
.label span:hover {
color: var(--bg);
background-color: var(--usa-blue);
}
.search {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 1.7rem;
font-size: 0.8rem;
background-color: var(--bg);
border: 2px solid var(--usa-blue);
border-radius: 3px;
position: relative;
}
input {
width: 100%;
height: 100%;
padding: 0.1rem 2.3rem 0.1rem 0.3rem;
font-size: 0.8rem;
color: var(--text-black);
background-color: var(--bg);
border: none;
}
.button-fields {
display: block;
position: absolute;
bottom: 0.2rem;
right: 0.3rem;
z-index: 10001;
}
.reset {
display: none;
margin-right: 0.3rem;
cursor: pointer;
}
.go {
cursor: pointer;
}
</style>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше