Compare commits

..

17 Commits

65 changed files with 10284 additions and 174 deletions

15
.gitignore vendored
View File

@ -1,3 +1,6 @@
.elixir_ls/
.DS_Store
# The directory Mix will write compiled artifacts to. # The directory Mix will write compiled artifacts to.
/_build/ /_build/
@ -30,4 +33,14 @@ frenzy-*.tar
# variables. # variables.
/config/*.secret.exs /config/*.secret.exs
.elixir_ls/ # If NPM crashes, it generates a log, let's ignore it too.
npm-debug.log
# The directory NPM downloads your dependencies sources to.
/assets/node_modules/
# Since we are building assets from assets/,
# we ignore priv/static. You may want to comment
# this depending on your deployment strategy.
/priv/static/

93
assets/css/app.scss Normal file
View File

@ -0,0 +1,93 @@
@import "../node_modules/nprogress/nprogress.css";
@import "./open-iconic-bootstrap.min.css";
@import "./frenzy.scss";
/* LiveView specific classes for your customizations */
.invalid-feedback {
color: #a94442;
display: block;
margin: -1rem 0 2rem;
}
.phx-no-feedback.invalid-feedback, .phx-no-feedback .invalid-feedback {
display: none;
}
.phx-click-loading {
opacity: 0.5;
transition: opacity 1s ease-out;
}
.phx-disconnected{
cursor: wait;
}
.phx-disconnected *{
pointer-events: none;
}
.phx-modal {
opacity: 1!important;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.phx-modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.phx-modal-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.phx-modal-close:hover,
.phx-modal-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
/* Alerts and form errors */
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.alert p {
margin-bottom: 0;
}
.alert:empty {
display: none;
}

View File

@ -1,5 +1,3 @@
/* This file is for your main application css. */
.item-read a { .item-read a {
color: #606c76; color: #606c76;
} }
@ -143,3 +141,6 @@ label.sidebar-toggle > .oi {
display: inline-block; display: inline-block;
} }
.post-content img {
max-width: 100%;
}

File diff suppressed because one or more lines are too long

35
assets/js/app.js Normal file
View File

@ -0,0 +1,35 @@
// We need to import the CSS so that webpack will load it.
// The MiniCssExtractPlugin is used to separate it out into
// its own CSS file.
import "../css/app.scss";
// webpack automatically bundles all modules in your
// entry points. Those entry points can be configured
// in "webpack.config.js".
//
// Import deps with the dep name or local files with a relative path, for example:
//
// import {Socket} from "phoenix"
// import socket from "./socket"
//
import "phoenix_html";
import {Socket} from "phoenix";
import NProgress from "nprogress";
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import {LiveSocket} from "phoenix_live_view"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});
// Show progress bar on live navigation and form submits
window.addEventListener("phx:page-loading-start", info => NProgress.start());
window.addEventListener("phx:page-loading-stop", info => NProgress.done());
// connect if there are any LiveViews on the page
liveSocket.connect();
// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000)
window.liveSocket = liveSocket;

8389
assets/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
assets/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"scripts": {
"deploy": "webpack --mode production",
"watch": "webpack --mode development --watch"
},
"dependencies": {
"bootstrap": "^4.5.0",
"jquery": "^3.5.1",
"nprogress": "^0.2.0",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view",
"popper.js": "^1.16.1"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"sass-loader": "^8.0.2",
"terser-webpack-plugin": "^2.3.2",
"url-loader": "^4.1.0",
"webpack": "4.41.5",
"webpack-cli": "^3.3.2"
}
}

BIN
assets/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,543 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2014-7-1: Created.
-->
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
By P.J. Onori
Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
</metadata>
<defs>
<font id="open-iconic" horiz-adv-x="800" >
<font-face
font-family="Icons"
font-weight="400"
font-stretch="normal"
units-per-em="800"
panose-1="2 0 5 3 0 0 0 0 0 0"
ascent="800"
descent="0"
bbox="-0.5 -101 802 800.126"
underline-thickness="50"
underline-position="-100"
unicode-range="U+E000-E0DE"
/>
<missing-glyph />
<glyph glyph-name="" unicode="&#xe000;"
d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z" />
<glyph glyph-name="1" unicode="&#xe001;"
d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z" />
<glyph glyph-name="2" unicode="&#xe002;"
d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z" />
<glyph glyph-name="3" unicode="&#xe003;"
d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z" />
<glyph glyph-name="4" unicode="&#xe004;"
d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
<glyph glyph-name="5" unicode="&#xe005;"
d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
<glyph glyph-name="6" unicode="&#xe006;"
d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
<glyph glyph-name="7" unicode="&#xe007;"
d="M400 700c75 0 146 -23 206 -59l-75 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 24 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -8 -68 -16 -100h-218zM216 253l112 -347c-128 23 -232 109 -287 222zM372 100
h372c-64 -109 -177 -185 -310 -197z" />
<glyph glyph-name="8" unicode="&#xe008;" horiz-adv-x="600"
d="M200 800h100v-500h200l-247 -300l-253 300h200v500z" />
<glyph glyph-name="9" unicode="&#xe009;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z" />
<glyph glyph-name="a" unicode="&#xe00a;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z" />
<glyph glyph-name="b" unicode="&#xe00b;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z" />
<glyph glyph-name="c" unicode="&#xe00c;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z" />
<glyph glyph-name="d" unicode="&#xe00d;"
d="M300 600v-200h500v-100h-500v-200l-300 247z" />
<glyph glyph-name="e" unicode="&#xe00e;"
d="M500 600l300 -247l-300 -253v200h-500v100h500v200z" />
<glyph glyph-name="f" unicode="&#xe00f;" horiz-adv-x="600"
d="M200 800h200v-500h200l-297 -300l-303 300h200v500z" />
<glyph glyph-name="10" unicode="&#xe010;"
d="M300 700v-200h500v-200h-500v-200l-300 297z" />
<glyph glyph-name="11" unicode="&#xe011;"
d="M500 700l300 -297l-300 -303v200h-500v200h500v200z" />
<glyph glyph-name="12" unicode="&#xe012;" horiz-adv-x="600"
d="M297 800l303 -300h-200v-500h-200v500h-200z" />
<glyph glyph-name="13" unicode="&#xe013;" horiz-adv-x="600"
d="M247 800l253 -300h-200v-500h-100v500h-200z" />
<glyph glyph-name="14" unicode="&#xe014;"
d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z" />
<glyph glyph-name="15" unicode="&#xe015;"
d="M116 600l72 -72c-54 -54 -88 -126 -88 -209s34 -159 88 -213l-72 -72c-72 72 -116 175 -116 285s44 209 116 281zM684 600c72 -72 116 -171 116 -281s-44 -213 -116 -285l-72 72c54 54 88 130 88 213s-34 155 -88 209zM259 460l69 -72c-18 -18 -28 -41 -28 -69
s10 -54 28 -72l-69 -72c-36 36 -59 89 -59 144s23 105 59 141zM541 459c36 -36 59 -85 59 -140s-23 -108 -59 -144l-69 72c18 18 28 44 28 72s-10 51 -28 69z" />
<glyph glyph-name="16" unicode="&#xe016;" horiz-adv-x="400"
d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s68 8 100 19v-319l-100 100l-100 -100v319z" />
<glyph glyph-name="17" unicode="&#xe017;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
c0 66 -21 126 -56 175z" />
<glyph glyph-name="18" unicode="&#xe018;"
d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z" />
<glyph glyph-name="19" unicode="&#xe019;"
d="M397 800c13 1 23 -4 34 -13c2 -2 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c9 11 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z" />
<glyph glyph-name="1a" unicode="&#xe01a;"
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z" />
<glyph glyph-name="1b" unicode="&#xe01b;"
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z" />
<glyph glyph-name="1c" unicode="&#xe01c;"
d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c16 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103h-3c-26 -25 -62 -41 -100 -41h-512c-39 0 -77 15 -103 41s-41 64 -41 103c0 46 18 80 34 106
c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-63 -123 -119 -228h450c-56 105 -119 228 -119 228l-6 10v162z" />
<glyph glyph-name="1d" unicode="&#xe01d;"
d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c41 -34 66 -82 66 -134h-800c0 52 25 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z" />
<glyph glyph-name="1e" unicode="&#xe01e;" horiz-adv-x="600"
d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z" />
<glyph glyph-name="1f" unicode="&#xe01f;"
d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
c83 0 150 67 150 150s-67 150 -150 150h-150z" />
<glyph glyph-name="20" unicode="&#xe020;" horiz-adv-x="600"
d="M300 800v-300h200l-300 -500v300h-200z" />
<glyph glyph-name="21" unicode="&#xe021;"
d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z" />
<glyph glyph-name="22" unicode="&#xe022;" horiz-adv-x="400"
d="M0 800h400v-800l-200 200l-200 -200v800z" />
<glyph glyph-name="23" unicode="&#xe023;"
d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z" />
<glyph glyph-name="24" unicode="&#xe024;"
d="M300 800h200c55 0 100 -45 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 55 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -6 32 -9 50 -9h700c18 0 34 3 50 9v-200c0 -6 -3 -9 -9 -9h-782
c-6 0 -9 3 -9 9v200z" />
<glyph glyph-name="25" unicode="&#xe025;" horiz-adv-x="600"
d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
c-8 50 -25 106 -25 175c0 58 16 114 50 156c34 43 88 69 150 69z" />
<glyph glyph-name="26" unicode="&#xe026;"
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
s-22 50 -50 50h-300zM100 400v-400h600v400h-600z" />
<glyph glyph-name="27" unicode="&#xe027;"
d="M744 797l6 -3l44 -44c4 -4 3 -8 0 -12l-266 -375l-15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -60 134 -134c0 -147 -119 -266 -266 -266c-48 0 -95 12 -134 34c80 46 134 133 134 232c0 74 58 134 132 134z" />
<glyph glyph-name="28" unicode="&#xe028;"
d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
c0 -23 -2 -45 -6 -66l78 -40c21 -5 37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11l-65 35c-24 -46 -62 -86 -103 -110c-35 19 -60 45 -60 72v135v4v5v6v5v5v87c0 28 -22 50 -50 50c-24 0 -45 -17 -50 -40c1 -3 1 -8 1 -11s0 -8 -1 -11v-82v-4v-5v-144
c0 -28 -24 -53 -59 -72c-41 25 -79 64 -103 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z" />
<glyph glyph-name="29" unicode="&#xe029;"
d="M600 700h91c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v600zM210 503l290 147v-500l-250 125v-3c-15 0 -25 -8 -28 -22l75 -178c11 -25 0 -58 -25 -69s-58 0 -69 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z" />
<glyph glyph-name="2a" unicode="&#xe02a;"
d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
<glyph glyph-name="2b" unicode="&#xe02b;"
d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
<glyph glyph-name="2c" unicode="&#xe02c;"
d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
<glyph glyph-name="2d" unicode="&#xe02d;"
d="M0 600h800l-400 -400z" />
<glyph glyph-name="2e" unicode="&#xe02e;" horiz-adv-x="400"
d="M400 800v-800l-400 400z" />
<glyph glyph-name="2f" unicode="&#xe02f;" horiz-adv-x="400"
d="M0 800l400 -400l-400 -400v800z" />
<glyph glyph-name="30" unicode="&#xe030;"
d="M400 600l400 -400h-800z" />
<glyph glyph-name="31" unicode="&#xe031;"
d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -12 15 -25l-81 -250c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
<glyph glyph-name="32" unicode="&#xe032;"
d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z" />
<glyph glyph-name="33" unicode="&#xe033;"
d="M641 700l143 -141l-493 -493c-71 76 -146 148 -219 222l-72 71l141 141c50 -51 101 -101 153 -150c116 117 234 231 347 350z" />
<glyph glyph-name="34" unicode="&#xe034;"
d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z" />
<glyph glyph-name="35" unicode="&#xe035;" horiz-adv-x="600"
d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z" />
<glyph glyph-name="36" unicode="&#xe036;" horiz-adv-x="600"
d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z" />
<glyph glyph-name="37" unicode="&#xe037;"
d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z" />
<glyph glyph-name="38" unicode="&#xe038;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z" />
<glyph glyph-name="39" unicode="&#xe039;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
<glyph glyph-name="3a" unicode="&#xe03a;"
d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z" />
<glyph glyph-name="3b" unicode="&#xe03b;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
v222z" />
<glyph glyph-name="3c" unicode="&#xe03c;"
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z" />
<glyph glyph-name="3d" unicode="&#xe03d;"
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z" />
<glyph glyph-name="3e" unicode="&#xe03e;"
d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z" />
<glyph glyph-name="3f" unicode="&#xe03f;"
d="M250 800c82 0 154 -40 200 -100c-143 0 -270 -85 -325 -209c-36 -10 -70 -25 -100 -47c-16 33 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
s90 200 200 200c23 114 129 200 250 200z" />
<glyph glyph-name="40" unicode="&#xe040;"
d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z" />
<glyph glyph-name="41" unicode="&#xe041;"
d="M350 800h100l50 -119l28 -12l119 50l72 -72l-50 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 119l-12 28l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z" />
<glyph glyph-name="42" unicode="&#xe042;"
d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z" />
<glyph glyph-name="43" unicode="&#xe043;"
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z" />
<glyph glyph-name="44" unicode="&#xe044;"
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z" />
<glyph glyph-name="45" unicode="&#xe045;"
d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z" />
<glyph glyph-name="46" unicode="&#xe046;"
d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
c-83 0 -150 67 -150 150s67 150 150 150zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h50v50c0 28 -22 50 -50 50zM550 600c-28 0 -50 -22 -50 -50v-50h50c28 0 50 22 50 50s-22 50 -50 50zM300 400v-100h100v100h-100zM150 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z" />
<glyph glyph-name="47" unicode="&#xe047;"
d="M0 791c0 5 4 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z" />
<glyph glyph-name="48" unicode="&#xe048;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="49" unicode="&#xe049;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z" />
<glyph glyph-name="4a" unicode="&#xe04a;"
d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
<glyph glyph-name="4b" unicode="&#xe04b;"
d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z" />
<glyph glyph-name="4c" unicode="&#xe04c;"
d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z" />
<glyph glyph-name="4d" unicode="&#xe04d;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
<glyph glyph-name="4e" unicode="&#xe04e;"
d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z" />
<glyph glyph-name="4f" unicode="&#xe04f;"
d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z" />
<glyph glyph-name="50" unicode="&#xe050;"
d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
<glyph glyph-name="51" unicode="&#xe051;"
d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c81 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 60 166 141 191z" />
<glyph glyph-name="52" unicode="&#xe052;"
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z" />
<glyph glyph-name="53" unicode="&#xe053;" horiz-adv-x="600"
d="M200 700h100v-100h75c30 0 58 -6 81 -22s44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-14 0 -25 -11 -25 -25v-50c0 -15 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -68 -57 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22s-44 44 -44 78
v100h100v-94c4 -3 13 -6 25 -6h250c14 0 25 11 25 25v50c0 15 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 68 57 125 125 125h75v100z" />
<glyph glyph-name="54" unicode="&#xe054;"
d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z" />
<glyph glyph-name="55" unicode="&#xe055;"
d="M300 700v-600h-300v300zM800 700v-600h-300v300z" />
<glyph glyph-name="56" unicode="&#xe056;"
d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z" />
<glyph glyph-name="57" unicode="&#xe057;"
d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z" />
<glyph glyph-name="58" unicode="&#xe058;" horiz-adv-x="600"
d="M300 800l34 -34c11 -11 266 -270 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 218 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z" />
<glyph glyph-name="59" unicode="&#xe059;"
d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z" />
<glyph glyph-name="5a" unicode="&#xe05a;" horiz-adv-x="600"
d="M300 800l300 -300h-600zM0 300h600l-300 -300z" />
<glyph glyph-name="5b" unicode="&#xe05b;"
d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z" />
<glyph glyph-name="5c" unicode="&#xe05c;"
d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z" />
<glyph glyph-name="5d" unicode="&#xe05d;"
d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z" />
<glyph glyph-name="5e" unicode="&#xe05e;"
d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z" />
<glyph glyph-name="5f" unicode="&#xe05f;"
d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z" />
<glyph glyph-name="60" unicode="&#xe060;"
d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z" />
<glyph glyph-name="61" unicode="&#xe061;"
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z" />
<glyph glyph-name="62" unicode="&#xe062;"
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z" />
<glyph glyph-name="63" unicode="&#xe063;"
d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z" />
<glyph glyph-name="64" unicode="&#xe064;"
d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z" />
<glyph glyph-name="65" unicode="&#xe065;"
d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
<glyph glyph-name="66" unicode="&#xe066;" horiz-adv-x="900"
d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 155 57 212 0c58 -58 58 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-381 -381h-269v272l375 381l-63 63c-9 8 -16 24 -16 36c0 20 16 42 35 48z
M447 481l-313 -315l128 -132l316 316z" />
<glyph glyph-name="67" unicode="&#xe067;"
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z" />
<glyph glyph-name="68" unicode="&#xe068;"
d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z" />
<glyph glyph-name="69" unicode="&#xe069;"
d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z" />
<glyph glyph-name="6a" unicode="&#xe06a;" horiz-adv-x="400"
d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z" />
<glyph glyph-name="6b" unicode="&#xe06b;"
d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z" />
<glyph glyph-name="6c" unicode="&#xe06c;"
d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z" />
<glyph glyph-name="6d" unicode="&#xe06d;"
d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z" />
<glyph glyph-name="6e" unicode="&#xe06e;"
d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z" />
<glyph glyph-name="6f" unicode="&#xe06f;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-30 -13 -77 10 -65 -41c13 -55 -27 -3 -47 -15c-42 -26 49 -152 31 -156l-59 34c-8 0 -13 -5 -16 -10
c1 -30 10 -57 19 -84c28 -11 77 -2 100 -25c47 -28 97 -115 75 -159c34 -13 68 -22 106 -22c101 0 193 48 247 125c3 24 -8 44 -50 44c-69 0 -156 13 -153 97c2 46 101 108 66 143c-30 30 12 39 12 66c0 37 -65 32 -69 50s20 36 41 56c-30 10 -60 19 -94 19zM631 591
c-38 -11 -94 -35 -87 -53c6 -15 52 -1 65 -13c11 -10 16 -59 44 -31l22 22v3c-11 26 -26 50 -44 72z" />
<glyph glyph-name="70" unicode="&#xe070;"
d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z" />
<glyph glyph-name="71" unicode="&#xe071;"
d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z" />
<glyph glyph-name="72" unicode="&#xe072;"
d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z" />
<glyph glyph-name="73" unicode="&#xe073;"
d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z" />
<glyph glyph-name="74" unicode="&#xe074;"
d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -6 32 -9 50 -9h600c18 0 34 3 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="75" unicode="&#xe075;"
d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
v400c0 28 -22 50 -50 50h-50v100z" />
<glyph glyph-name="76" unicode="&#xe076;"
d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
c0 165 135 300 300 300z" />
<glyph glyph-name="77" unicode="&#xe077;"
d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -23 -105 -59 -141l-341 -340l-341 340c-36 36 -59 86 -59 141z" />
<glyph glyph-name="78" unicode="&#xe078;"
d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z" />
<glyph glyph-name="79" unicode="&#xe079;"
d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z" />
<glyph glyph-name="7a" unicode="&#xe07a;"
d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z" />
<glyph glyph-name="7b" unicode="&#xe07b;"
d="M200 600c80 0 142 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -88 -47 -134 -100c46 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z" />
<glyph glyph-name="7c" unicode="&#xe07c;" horiz-adv-x="400"
d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
s-50 -22 -50 -50h-100c0 83 67 150 150 150z" />
<glyph glyph-name="7d" unicode="&#xe07d;"
d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z" />
<glyph glyph-name="7e" unicode="&#xe07e;"
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
<glyph glyph-name="7f" unicode="&#xe07f;"
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
<glyph glyph-name="80" unicode="&#xe080;"
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
<glyph glyph-name="81" unicode="&#xe081;"
d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -32 0 -47 3l-3 -3v-100h-200v-200h-300v200l303 303c-3 15 -3 31 -3 47c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
<glyph glyph-name="82" unicode="&#xe082;"
d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z" />
<glyph glyph-name="83" unicode="&#xe083;"
d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z" />
<glyph glyph-name="84" unicode="&#xe084;" horiz-adv-x="600"
d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
c0 21 16 44 37 49zM437 344c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-106 -56c24 -4 43 -26 43 -50c0 -28 -23 -51 -51 -51c-2 0 -6 1 -8 1h-200c-26 1 -48 24 -48 50c0 16 12 36 26 44zM151 -50c0 23 20 50 46 50h3h4h5h100c28 0 50 -22 50 -50
s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
<glyph glyph-name="85" unicode="&#xe085;"
d="M199 800h100v-200h-200v100h100v100zM586 797h1c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0l-44 -44
c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l43 44c32 33 72 53 128 56zM208 490c4 5 14 16 22 16h3c2 0 6 1 8 1c28 0 50 -22 50 -50c0 -11 -6 -27 -14 -35l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0l44 44c8 13 27 24 42 24
c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43l-43 -44c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z" />
<glyph glyph-name="86" unicode="&#xe086;"
d="M586 797c18 1 39 1 57 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-62 -62 -132 -81 -182 -78s-69 17 -84 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13s62 3 106 47l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0
c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43c32 33 72 53 128 56zM386 566c50 -2 64 -17 85 -22s37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11c0 0 -19 9 -47 10s-63 -4 -103 -44l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0
c8 13 27 24 42 24c28 0 50 -22 50 -50c0 -15 -10 -35 -23 -43c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281l150 150c60 60 128 78 178 76z" />
<glyph glyph-name="87" unicode="&#xe087;"
d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z" />
<glyph glyph-name="88" unicode="&#xe088;"
d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z" />
<glyph glyph-name="89" unicode="&#xe089;"
d="M800 800l-400 -800l-100 300l-300 100z" />
<glyph glyph-name="8a" unicode="&#xe08a;" horiz-adv-x="600"
d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z" />
<glyph glyph-name="8b" unicode="&#xe08b;" horiz-adv-x="600"
d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z" />
<glyph glyph-name="8c" unicode="&#xe08c;"
d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z" />
<glyph glyph-name="8d" unicode="&#xe08d;"
d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z" />
<glyph glyph-name="8e" unicode="&#xe08e;"
d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -55 -45 -100 -100 -100h-500v-100l-200 150z" />
<glyph glyph-name="8f" unicode="&#xe08f;" horiz-adv-x="900"
d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z" />
<glyph glyph-name="90" unicode="&#xe090;" horiz-adv-x="600"
d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z" />
<glyph glyph-name="91" unicode="&#xe091;" horiz-adv-x="900"
d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="92" unicode="&#xe092;" horiz-adv-x="600"
d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z" />
<glyph glyph-name="93" unicode="&#xe093;" horiz-adv-x="600"
d="M0 700l600 -300l-600 -300v600z" />
<glyph glyph-name="94" unicode="&#xe094;" horiz-adv-x="600"
d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z" />
<glyph glyph-name="95" unicode="&#xe095;"
d="M400 700v-600l-400 300zM400 400l400 300v-600z" />
<glyph glyph-name="96" unicode="&#xe096;"
d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z" />
<glyph glyph-name="97" unicode="&#xe097;"
d="M0 700h200v-600h-200v600zM200 400l500 300v-600z" />
<glyph glyph-name="98" unicode="&#xe098;"
d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z" />
<glyph glyph-name="99" unicode="&#xe099;" horiz-adv-x="600"
d="M0 700h600v-600h-600v600z" />
<glyph glyph-name="9a" unicode="&#xe09a;"
d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z" />
<glyph glyph-name="9b" unicode="&#xe09b;"
d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z" />
<glyph glyph-name="9c" unicode="&#xe09c;" horiz-adv-x="600"
d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z" />
<glyph glyph-name="9d" unicode="&#xe09d;"
d="M0 500h800v-200h-800v200z" />
<glyph glyph-name="9e" unicode="&#xe09e;"
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z" />
<glyph glyph-name="9f" unicode="&#xe09f;"
d="M272 700c-14 -40 -22 -83 -22 -128c0 -221 179 -400 400 -400c45 0 88 8 128 22c-53 -158 -202 -272 -378 -272c-221 0 -400 179 -400 400c0 176 114 325 272 378z" />
<glyph glyph-name="a0" unicode="&#xe0a0;"
d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z" />
<glyph glyph-name="a1" unicode="&#xe0a1;"
d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z" />
<glyph glyph-name="a2" unicode="&#xe0a2;" horiz-adv-x="700"
d="M499 700c51 0 102 -20 141 -59c78 -78 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175l96 97l69 -69l-90 -94l-7 -3c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c37 40 39 102 0 141s-104 40 -144 0l-278 -275c-66 -69 -68 -179 0 -247
c69 -69 181 -69 250 0l9 12l116 113l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c39 39 90 59 141 59z" />
<glyph glyph-name="a3" unicode="&#xe0a3;"
d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z" />
<glyph glyph-name="a4" unicode="&#xe0a4;"
d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -40 8 -59 19c6 26 9 52 9 81c0 84 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
c0 50 -17 96 -44 134c66 2 126 33 169 84zM75 284c45 -53 106 -84 175 -84s130 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z" />
<glyph glyph-name="a5" unicode="&#xe0a5;"
d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c106 -5 191 -92 191 -200v-100h-800v100c0 108 85 195 191 200z" />
<glyph glyph-name="a6" unicode="&#xe0a6;" horiz-adv-x="600"
d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="a7" unicode="&#xe0a7;"
d="M350 800c17 0 34 -1 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -25 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -161 31 -222 81l272 272v341zM91 562l237 -234l-212 -212c-70 55 -116 138 -116 234c0 84 35 158 91 212z" />
<glyph glyph-name="a8" unicode="&#xe0a8;"
d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
<glyph glyph-name="a9" unicode="&#xe0a9;"
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z" />
<glyph glyph-name="aa" unicode="&#xe0aa;"
d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z" />
<glyph glyph-name="ab" unicode="&#xe0ab;"
d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -164 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 111 53 211 134 275z
" />
<glyph glyph-name="ac" unicode="&#xe0ac;"
d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z" />
<glyph glyph-name="ad" unicode="&#xe0ad;"
d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z" />
<glyph glyph-name="ae" unicode="&#xe0ae;"
d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c30 -57 58 -114 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-38 129 -79 257 -119 385c-42 -133 -83 -267 -125 -400c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z" />
<glyph glyph-name="af" unicode="&#xe0af;"
d="M200 600c0 57 43 100 100 100s100 -43 100 -100c0 -28 -18 -48 -28 -72c-3 -6 -3 -16 -3 -28h231v-231c12 0 22 0 28 3c24 10 44 28 72 28c57 0 100 -43 100 -100s-43 -100 -100 -100c-28 0 -48 18 -72 28c-6 3 -16 3 -28 3v-231h-231c0 12 0 22 3 28c10 24 28 44 28 72
c0 57 -43 100 -100 100s-100 -43 -100 -100c0 -28 18 -48 28 -72c3 -6 3 -16 3 -28h-231v600h231c0 12 0 22 -3 28c-10 24 -28 44 -28 72z" />
<glyph glyph-name="b0" unicode="&#xe0b0;" horiz-adv-x="500"
d="M247 700c84 0 148 -20 191 -59s59 -93 59 -141c0 -117 -69 -181 -119 -225s-81 -67 -81 -150v-25h-100v25c0 117 65 181 115 225s85 67 85 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
v-100h-100v100z" />
<glyph glyph-name="b1" unicode="&#xe0b1;"
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -12 -97 -34c-33 36 -53 82 -53 134
c0 110 90 200 200 200c23 114 129 200 250 200zM334 300h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-200c0 -28 -22 -50 -50 -50s-50 22 -50 50v200v2c0 20 15 42 34 48zM134 200h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2
c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z" />
<glyph glyph-name="b2" unicode="&#xe0b2;"
d="M600 800l200 -150l-200 -150v100h-50l-153 -191l175 -206l6 -3h22v100l200 -150l-200 -150v100h-25c-35 0 -56 12 -78 38l-166 190l-153 -190c-22 -27 -43 -38 -78 -38h-100v100h100l166 206l-163 191l-3 3h-100v100h100c34 0 56 -12 78 -38l153 -178l141 178
c22 27 43 38 78 38h50v100z" />
<glyph glyph-name="b3" unicode="&#xe0b3;"
d="M400 800c110 0 209 -47 281 -119l119 119v-300h-300l109 109c-54 55 -126 91 -209 91c-166 0 -300 -134 -300 -300s134 -300 300 -300c83 0 158 34 212 88l72 -72c-72 -72 -174 -116 -284 -116c-220 0 -400 180 -400 400s180 400 400 400z" />
<glyph glyph-name="b4" unicode="&#xe0b4;"
d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z" />
<glyph glyph-name="b5" unicode="&#xe0b5;" horiz-adv-x="600"
d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z" />
<glyph glyph-name="b6" unicode="&#xe0b6;"
d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z" />
<glyph glyph-name="b7" unicode="&#xe0b7;"
d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c110 0 200 -90 200 -200h-200v200z" />
<glyph glyph-name="b8" unicode="&#xe0b8;"
d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
s-100 45 -100 100s45 100 100 100z" />
<glyph glyph-name="b9" unicode="&#xe0b9;"
d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z" />
<glyph glyph-name="ba" unicode="&#xe0ba;"
d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -41 -34 -75 -75 -75h-450c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z" />
<glyph glyph-name="bb" unicode="&#xe0bb;"
d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z" />
<glyph glyph-name="bc" unicode="&#xe0bc;"
d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
c35 -46 73 -86 106 -110s62 -31 66 -31z" />
<glyph glyph-name="bd" unicode="&#xe0bd;"
d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z" />
<glyph glyph-name="be" unicode="&#xe0be;"
d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z" />
<glyph glyph-name="bf" unicode="&#xe0bf;"
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z" />
<glyph glyph-name="c0" unicode="&#xe0c0;"
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z" />
<glyph glyph-name="c1" unicode="&#xe0c1;"
d="M75 700h650c41 0 75 -34 75 -75v-550c0 -41 -34 -75 -75 -75h-650c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
v-100h400v100h-400z" />
<glyph glyph-name="c2" unicode="&#xe0c2;"
d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z" />
<glyph glyph-name="c3" unicode="&#xe0c3;"
d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM50 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM750 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
<glyph glyph-name="c4" unicode="&#xe0c4;"
d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-38 0 -63 -42 -44 -75s69 -33 88 0s-6 75 -44 75z" />
<glyph glyph-name="c5" unicode="&#xe0c5;"
d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
<glyph glyph-name="c6" unicode="&#xe0c6;"
d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="c7" unicode="&#xe0c7;"
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z" />
<glyph glyph-name="c8" unicode="&#xe0c8;"
d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z" />
<glyph glyph-name="c9" unicode="&#xe0c9;"
d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z" />
<glyph glyph-name="ca" unicode="&#xe0ca;"
d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z" />
<glyph glyph-name="cb" unicode="&#xe0cb;"
d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184s-5 -55 -31 -63s-52 5 -66 31s-109 219 -128 238s-44 28 -72 28v400z" />
<glyph glyph-name="cc" unicode="&#xe0cc;"
d="M400 666c10 19 28 32 47 34l19 -3c26 -8 39 -37 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 9 72 28s114 212 128 238zM0 400h100v-400h-100v400z" />
<glyph glyph-name="cd" unicode="&#xe0cd;"
d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-34 -94c-28 11 -58 16 -88 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 60 -16 88l91 37c14 -38 25 -81 25 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
h-100v100zM700 584c0 0 -296 -348 -316 -368s-48 -20 -68 0s-20 48 0 68s384 300 384 300z" />
<glyph glyph-name="ce" unicode="&#xe0ce;"
d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z" />
<glyph glyph-name="cf" unicode="&#xe0cf;"
d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
c-11 0 -19 8 -19 19v481z" />
<glyph glyph-name="d0" unicode="&#xe0d0;"
d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z" />
<glyph glyph-name="d1" unicode="&#xe0d1;"
d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
c0 6 3 9 9 9zM0 100h800v-100h-800v100z" />
<glyph glyph-name="d2" unicode="&#xe0d2;"
d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 5 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z" />
<glyph glyph-name="d3" unicode="&#xe0d3;"
d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z" />
<glyph glyph-name="d4" unicode="&#xe0d4;"
d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z" />
<glyph glyph-name="d5" unicode="&#xe0d5;"
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -33 225 -150 225 -290s-95 -258 -225 -291h-3c-23 -6 -47 -9 -72 -9v100c17 0 34 2 50 6c86 22 150 100 150 194s-64 172 -150 194c-16 4 -33 6 -50 6zM500 500l25 -3
c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
<glyph glyph-name="d6" unicode="&#xe0d6;" horiz-adv-x="600"
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l25 -3c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
<glyph glyph-name="d7" unicode="&#xe0d7;" horiz-adv-x="400"
d="M334 800h66v-800h-66l-134 200h-200v400h200z" />
<glyph glyph-name="d8" unicode="&#xe0d8;"
d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z" />
<glyph glyph-name="d9" unicode="&#xe0d9;"
d="M375 800c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -32 -322 -91l-53 82c109 70 237 109 375 109zM375 500c78 0 154 -23 216 -62l-53 -85c-46 30 -104 47 -163 47c-60 0 -112 -17 -159 -47l-54 85c62 40 134 62 213 62zM375 200
c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
<glyph glyph-name="da" unicode="&#xe0da;" horiz-adv-x="900"
d="M551 800c16 0 32 0 47 -3l-97 -97v-200h200l97 97c3 -15 3 -31 3 -47c0 -138 -112 -250 -250 -250c-32 0 -62 8 -90 19l-288 -291c-20 -20 -46 -28 -72 -28s-52 8 -72 28c-39 39 -39 105 0 144l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 150
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
<glyph glyph-name="db" unicode="&#xe0db;"
d="M141 700c84 -84 169 -167 253 -250c82 83 167 165 247 250l143 -141l-253 -253c84 -82 167 -166 253 -247l-143 -143c-81 86 -165 169 -247 253l-253 -253l-141 143c85 80 167 164 250 247c-83 84 -166 169 -250 253z" />
<glyph glyph-name="dc" unicode="&#xe0dc;"
d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z" />
<glyph glyph-name="dd" unicode="&#xe0dd;" horiz-adv-x="900"
d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z" />
<glyph glyph-name="de" unicode="&#xe0de;" horiz-adv-x="900"
d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Binary file not shown.

5
assets/static/robots.txt Normal file
View File

@ -0,0 +1,5 @@
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
User-agent: *
Disallow: /

58
assets/webpack.config.js Normal file
View File

@ -0,0 +1,58 @@
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = (env, options) => {
const devMode = options.mode !== 'production';
return {
optimization: {
minimizer: [
new TerserPlugin({ cache: true, parallel: true, sourceMap: devMode }),
new OptimizeCSSAssetsPlugin({})
]
},
entry: {
'app': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, '../priv/static/js'),
publicPath: '/js/'
},
devtool: devMode ? 'source-map' : undefined,
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.[s]?css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
{
test: /\.(eot|otf|svg|ttf|woff)/,
use: {
loader: 'url-loader',
},
},
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
]
}
};

View File

@ -15,7 +15,10 @@ config :frenzy, FrenzyWeb.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
secret_key_base: "OHENOE79+KzItVyWbScSSn2yPhZToFpNriYZm3gj2sGYR0hbKEiSLvcVvTK1zxo8", secret_key_base: "OHENOE79+KzItVyWbScSSn2yPhZToFpNriYZm3gj2sGYR0hbKEiSLvcVvTK1zxo8",
render_errors: [view: FrenzyWeb.ErrorView, accepts: ~w(html json)], render_errors: [view: FrenzyWeb.ErrorView, accepts: ~w(html json)],
pubsub: [name: Frenzy.PubSub, adapter: Phoenix.PubSub.PG2] pubsub_server: Frenzy.PubSub,
live_view: [
signing_salt: "lgnxcl3mtR6kB7yBeW7l5wd/SbEx5vRY"
]
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, :console, config :logger, :console,

View File

@ -11,7 +11,15 @@ config :frenzy, FrenzyWeb.Endpoint,
debug_errors: true, debug_errors: true,
code_reloader: true, code_reloader: true,
check_origin: false, check_origin: false,
watchers: [] watchers: [
node: [
"node_modules/webpack/bin/webpack.js",
"--mode",
"development",
"--watch-stdin",
cd: Path.expand("../assets", __DIR__)
]
]
# ## SSL Support # ## SSL Support
# #

View File

@ -10,6 +10,8 @@ defmodule Frenzy.Application do
children = [ children = [
# Start the Ecto repository # Start the Ecto repository
Frenzy.Repo, Frenzy.Repo,
# Start the PubSub system
{Phoenix.PubSub, name: Frenzy.PubSub},
# Start the endpoint when the application starts # Start the endpoint when the application starts
FrenzyWeb.Endpoint, FrenzyWeb.Endpoint,
# Starts a worker by calling: Frenzy.Worker.start_link(arg) # Starts a worker by calling: Frenzy.Worker.start_link(arg)

39
lib/frenzy/json_schema.ex Normal file
View File

@ -0,0 +1,39 @@
defmodule Frenzy.JSONSchema do
@enforce_keys [:value, :schema]
defstruct [:value, :schema]
@type t() :: %__MODULE__{
value: map(),
schema: schema()
}
@type schema() ::
:string
| :boolean
| :integer
| {:list, schema()}
| %{required(String.t()) => schema()}
@spec coerce(schema :: schema(), value :: map() | list() | String.t()) :: map()
def coerce(:string, value) when is_binary(value), do: value
def coerce(:boolean, "true"), do: true
def coerce(:boolean, _), do: false
def coerce(:integer, value) when is_binary(value) do
{res, _} = Integer.parse(value)
res
end
def coerce({:list, list_type}, list_value) when is_list(list_value) do
Enum.map(list_value, &coerce(list_type, &1))
end
def coerce(schema, map_value) when is_map(schema) and is_map(map_value) do
Enum.reduce(schema, %{}, fn {key, value_schema}, acc ->
value = Map.fetch!(map_value, key)
Map.put(acc, key, coerce(value_schema, value))
end)
end
end

73
lib/frenzy/keypath.ex Normal file
View File

@ -0,0 +1,73 @@
defmodule Frenzy.Keypath do
@moduledoc """
Utilities for accessing or updating values using keypaths.
A keypath is a list of map keys or list indices representing the path through nested maps/lists.
"""
@type container() :: map() | list()
@type t() :: [Map.key() | non_neg_integer()]
@doc """
Gets the value in the given container at the given keypath. Raises on index out of bounds/missing map key.
## Examples
iex> Frenzy.Keypath.get(%{foo: %{bar: "baz"}}, [:foo, :bar])
"baz"
iex> Frenzy.Keypath.get([%{"foo" => "bar"}, %{"foo" => "baz"}], [1, "foo"])
"baz"
"""
@spec get(container(), t()) :: any()
def get(value, []), do: value
def get(map, [key | rest]) when is_map(map) do
get(Map.fetch!(map, key), rest)
end
def get(list, [index | rest]) when is_list(list) and is_integer(index) and index >= 0 do
if index >= length(list) do
raise KeyError, "Index #{index} out of bounds (>= #{length(list)})"
else
get(Enum.at(list, index), rest)
end
end
@doc """
Sets the vlaue in the given container at the given keypath. Raises on index out of bounds/missing map key.
## Examples
iex> Frenzy.Keypath.set(%{foo: %{bar: "baz"}}, [:foo, :bar], "blah")
%{foo: %{bar: "blah"}}
iex> Frenzy.Keypath.set([%{"foo" => "bar"}, %{"list" => ["a", "b"]}], [1, "list", 0], "c")
[%{"foo" => "bar"}, %{"list" => ["c", "b"]}]
"""
@spec set(container(), t(), any()) :: map()
def set(map, [key], value) when is_map(map) do
Map.put(map, key, value)
end
def set(list, [index], value) when is_list(list) and is_integer(index) and index >= 0 do
if index >= length(list) do
raise KeyError, "Index #{index} out of bounds (>= #{length(list)})"
else
List.replace_at(list, index, value)
end
end
def set(map, [key | rest], value) when is_map(map) do
Map.put(map, key, set(Map.fetch!(map, key), rest, value))
end
def set(list, [index | rest], value) when is_list(list) and is_integer(index) and index >= 0 do
if index >= length(list) do
raise KeyError, "Index #{index} out of bounds (>= #{length(list)})"
else
List.replace_at(list, index, set(Enum.at(list, index), rest, value))
end
end
end

View File

@ -37,8 +37,8 @@ defmodule Frenzy.Pipeline.ConditionalStage do
@impl Stage @impl Stage
def validate_opts(opts) do def validate_opts(opts) do
cond do cond do
not (Map.has_key?(opts, "stage") and is_binary(opts["stage"]) and not (Map.has_key?(opts, "stage") and
module_exists(opts["stage"])) -> ((is_binary(opts["stage"]) and module_exists(opts["stage"])) or opts["stage"] == "")) ->
{:error, "stage must be a string containg a module that exists"} {:error, "stage must be a string containg a module that exists"}
not (Map.has_key?(opts, "opts") and is_map(opts["opts"])) -> not (Map.has_key?(opts, "opts") and is_map(opts["opts"])) ->
@ -47,6 +47,9 @@ defmodule Frenzy.Pipeline.ConditionalStage do
not (Map.has_key?(opts, "condition") and is_map(opts["condition"])) -> not (Map.has_key?(opts, "condition") and is_map(opts["condition"])) ->
{:error, "condition must be a map"} {:error, "condition must be a map"}
opts["stage"] == "" ->
{:ok, opts}
true -> true ->
with {:ok, stage_opts} <- with {:ok, stage_opts} <-
apply(String.to_existing_atom("Elixir." <> opts["stage"]), :validate_opts, [ apply(String.to_existing_atom("Elixir." <> opts["stage"]), :validate_opts, [
@ -66,6 +69,9 @@ defmodule Frenzy.Pipeline.ConditionalStage do
end end
end end
@impl Stage
def default_opts(), do: %{"stage" => "", "opts" => %{}, "condition" => %{}}
defp module_exists(module_name) do defp module_exists(module_name) do
try do try do
String.to_existing_atom("Elixir." <> module_name) String.to_existing_atom("Elixir." <> module_name)

View File

@ -13,7 +13,7 @@ defmodule Frenzy.Pipeline.Extractor.OmMalik do
{ {
:ok, :ok,
# remove related posts list # remove related posts list
Floki.filter_out(content_elem, ".rpbt_shortcode") Floki.filter_out(content_elem, ".rpbt_shortcode, .newsletter-content")
} }
_ -> _ ->

View File

@ -17,4 +17,7 @@ defmodule Frenzy.Pipeline.FilterStage do
def validate_opts(opts) do def validate_opts(opts) do
FilterEngine.validate_filter(opts) FilterEngine.validate_filter(opts)
end end
@impl Stage
def default_opts(), do: %{"mode" => "reject", "score" => 1, "rules" => []}
end end

View File

@ -60,6 +60,9 @@ defmodule Frenzy.Pipeline.ScrapeStage do
@impl Stage @impl Stage
def validate_opts(_), do: {:error, "options must be a map"} def validate_opts(_), do: {:error, "options must be a map"}
@impl Stage
def default_opts(), do: %{}
@spec get_article_content(String.t(), map()) :: {:ok, String.t()} | {:error, String.t()} @spec get_article_content(String.t(), map()) :: {:ok, String.t()} | {:error, String.t()}
defp get_article_content(url, opts) when is_binary(url) and url != "" do defp get_article_content(url, opts) when is_binary(url) and url != "" do
Logger.debug("Getting article from #{url}") Logger.debug("Getting article from #{url}")

View File

@ -2,4 +2,6 @@ defmodule Frenzy.Pipeline.Stage do
@callback apply(Map.t(), Map.t()) :: {:ok, Map.t()} | :tombstone | {:error, String.t()} @callback apply(Map.t(), Map.t()) :: {:ok, Map.t()} | :tombstone | {:error, String.t()}
@callback validate_opts(Map.t()) :: {:ok, Map.t()} | {:error, String.t()} @callback validate_opts(Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
@callback default_opts() :: Map.t()
end end

View File

@ -38,9 +38,7 @@ defmodule Frenzy.UpdateFeeds do
end end
defp update_feeds() do defp update_feeds() do
Logger.info("Updating all feeds") {count, feeds} =
{_count, feeds} =
Feed Feed
|> where( |> where(
[f], [f],
@ -50,7 +48,20 @@ defmodule Frenzy.UpdateFeeds do
|> select([f], f) |> select([f], f)
|> Repo.update_all(set: [last_refreshed_at: DateTime.utc_now()]) |> Repo.update_all(set: [last_refreshed_at: DateTime.utc_now()])
Enum.map(feeds, &update_feed/1) Logger.info("Updating #{count} feeds")
Enum.each(feeds, &update_feed/1)
Enum.each(feeds, fn feed ->
try do
update_feed(feed)
rescue
error ->
Logger.warn(
"Encountered error updating feed #{feed.id} #{feed.feed_url}: #{inspect(error)}"
)
end
end)
prune_old_items() prune_old_items()
end end

View File

@ -24,6 +24,7 @@ defmodule FrenzyWeb do
import Plug.Conn import Plug.Conn
import FrenzyWeb.Gettext import FrenzyWeb.Gettext
alias FrenzyWeb.Router.Helpers, as: Routes alias FrenzyWeb.Router.Helpers, as: Routes
import Phoenix.LiveView.Controller
end end
end end
@ -36,12 +37,23 @@ defmodule FrenzyWeb do
# Import convenience functions from controllers # Import convenience functions from controllers
import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1] import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]
# Use all HTML functionality (forms, tags, etc) unquote(view_helpers())
use Phoenix.HTML end
end
import FrenzyWeb.ErrorHelpers def live_view do
import FrenzyWeb.Gettext quote do
alias FrenzyWeb.Router.Helpers, as: Routes use Phoenix.LiveView, layout: {FrenzyWeb.LayoutView, "live.html"}
unquote(view_helpers())
end
end
def live_component do
quote do
use Phoenix.LiveComponent
unquote(view_helpers())
end end
end end
@ -50,6 +62,7 @@ defmodule FrenzyWeb do
use Phoenix.Router use Phoenix.Router
import Plug.Conn import Plug.Conn
import Phoenix.Controller import Phoenix.Controller
import Phoenix.LiveView.Router
end end
end end
@ -60,6 +73,23 @@ defmodule FrenzyWeb do
end end
end end
defp view_helpers do
quote do
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML
# Import LiveView helpers (live_render, live_component, live_patch, etc)
import Phoenix.LiveView.Helpers
# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View
import FrenzyWeb.ErrorHelpers
import FrenzyWeb.Gettext
alias FrenzyWeb.Router.Helpers, as: Routes
end
end
@doc """ @doc """
When used, dispatch to the appropriate controller/view/etc. When used, dispatch to the appropriate controller/view/etc.
""" """

View File

@ -1,7 +1,6 @@
defmodule FrenzyWeb.AccountController do defmodule FrenzyWeb.AccountController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, User, FervorClient} alias Frenzy.{Repo, User, FervorClient}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
def show(conn, _params) do def show(conn, _params) do
@ -42,7 +41,7 @@ defmodule FrenzyWeb.AccountController do
^confirm -> ^confirm ->
changeset = User.change_password_changeset(user, %{password: new}) changeset = User.change_password_changeset(user, %{password: new})
{:ok, user} = Repo.update(changeset) {:ok, _user} = Repo.update(changeset)
conn conn
|> put_flash(:info, "Password changed.") |> put_flash(:info, "Password changed.")
@ -80,7 +79,7 @@ defmodule FrenzyWeb.AccountController do
fever_password: new fever_password: new
}) })
{:ok, user} = Repo.update(changeset) {:ok, _user} = Repo.update(changeset)
conn conn
|> put_flash(:info, "Fever password changed.") |> put_flash(:info, "Fever password changed.")

View File

@ -1,7 +1,6 @@
defmodule FrenzyWeb.FeedController do defmodule FrenzyWeb.FeedController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, Group, Feed, Item, Pipeline, Paginator} alias Frenzy.{Repo, Group, Feed, Item, Pipeline, Paginator}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
import Ecto.Query import Ecto.Query

View File

@ -1,6 +1,6 @@
defmodule FrenzyWeb.Fervor.ItemsController do defmodule FrenzyWeb.Fervor.ItemsController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, FervorClient, Group, Feed, Filter, Item} alias Frenzy.{Repo, Item}
import Ecto.Query import Ecto.Query
alias FrenzyWeb.Fervor.Paginator alias FrenzyWeb.Fervor.Paginator

View File

@ -1,7 +1,6 @@
defmodule FrenzyWeb.Fervor.OauthController do defmodule FrenzyWeb.Fervor.OauthController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, FervorClient, User, ApprovedClient} alias Frenzy.{Repo, FervorClient, User, ApprovedClient}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
def authorize_get(conn, params) do def authorize_get(conn, params) do

View File

@ -1,18 +1,15 @@
defmodule FrenzyWeb.Fervor.Paginator do defmodule FrenzyWeb.Fervor.Paginator do
import Ecto.Query import Ecto.Query
def paginate(query, %{"max_id" => max_id} = params) do def paginate(query, %{"max_id" => max_id}) do
limit = Map.get(params, "limit", 20)
from(o in query, where: o.id < ^max_id, order_by: [desc: :id]) from(o in query, where: o.id < ^max_id, order_by: [desc: :id])
end end
def paginate(query, %{"min_id" => min_id} = params) do def paginate(query, %{"min_id" => min_id}) do
limit = Map.get(params, "limit", 20)
from(o in query, where: o.id > ^min_id, order_by: [asc: :id]) from(o in query, where: o.id > ^min_id, order_by: [asc: :id])
end end
def paginate(query, %{"since_id" => since_id} = params) do def paginate(query, %{"since_id" => since_id}) do
limit = Map.get(params, "limit", 20)
from(o in query, where: o.id > ^since_id, order_by: [desc: :id]) from(o in query, where: o.id > ^since_id, order_by: [desc: :id])
end end

View File

@ -1,7 +1,6 @@
defmodule FrenzyWeb.GroupController do defmodule FrenzyWeb.GroupController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, Group, Feed, Item, Paginator} alias Frenzy.{Repo, Group, Feed, Item, Paginator}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
import Ecto.Query import Ecto.Query

View File

@ -1,7 +1,6 @@
defmodule FrenzyWeb.ItemController do defmodule FrenzyWeb.ItemController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, Feed, Item} alias Frenzy.{Repo, Item}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
plug :user_owns_item plug :user_owns_item

View File

@ -1,9 +1,7 @@
defmodule FrenzyWeb.LoginController do defmodule FrenzyWeb.LoginController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, User} alias Frenzy.{Repo, User}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
import Ecto.Query
def login(conn, params) do def login(conn, params) do
render(conn, "login.html", %{ render(conn, "login.html", %{
@ -29,7 +27,7 @@ defmodule FrenzyWeb.LoginController do
end end
end end
def logout(conn, params) do def logout(conn, _params) do
conn conn
|> put_flash(:info, "Logged out.") |> put_flash(:info, "Logged out.")
|> clear_session() |> clear_session()

View File

@ -1,9 +1,9 @@
defmodule FrenzyWeb.PipelineController do defmodule FrenzyWeb.PipelineController do
use FrenzyWeb, :controller use FrenzyWeb, :controller
alias Frenzy.{Repo, Pipeline} alias Frenzy.{Repo, Pipeline}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint alias FrenzyWeb.Endpoint
import Ecto.Query import Ecto.Query
import Phoenix.LiveView.Controller
plug :user_owns_pipeline plug :user_owns_pipeline
@ -78,7 +78,7 @@ defmodule FrenzyWeb.PipelineController do
redirect(conn, to: Routes.pipeline_path(Endpoint, :index)) redirect(conn, to: Routes.pipeline_path(Endpoint, :index))
end end
def edit(conn, _params) do def edit(conn, %{"json" => _}) do
pipeline = conn.assigns[:pipeline] pipeline = conn.assigns[:pipeline]
{:ok, stages_json} = Jason.encode(pipeline.stages, pretty: true) {:ok, stages_json} = Jason.encode(pipeline.stages, pretty: true)
@ -90,6 +90,11 @@ defmodule FrenzyWeb.PipelineController do
}) })
end end
def edit(conn, _params) do
pipeline = conn.assigns[:pipeline]
live_render(conn, FrenzyWeb.EditPipelineLive, session: %{"pipeline_id" => pipeline.id})
end
def update(conn, %{"pipeline" => %{"name" => name, "stages" => stages_json}}) do def update(conn, %{"pipeline" => %{"name" => name, "stages" => stages_json}}) do
pipeline = conn.assigns[:pipeline] pipeline = conn.assigns[:pipeline]

View File

@ -1,10 +1,18 @@
defmodule FrenzyWeb.Endpoint do defmodule FrenzyWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :frenzy use Phoenix.Endpoint, otp_app: :frenzy
@session_options [
store: :cookie,
key: "_frenzy_key",
signing_salt: "bL/djGKI"
]
socket "/socket", FrenzyWeb.UserSocket, socket "/socket", FrenzyWeb.UserSocket,
websocket: true, websocket: true,
longpoll: false longpoll: false
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
# Serve at "/" the static files from "priv/static" directory. # Serve at "/" the static files from "priv/static" directory.
# #
# You should set gzip to true if you are running phx.digest # You should set gzip to true if you are running phx.digest
@ -37,10 +45,7 @@ defmodule FrenzyWeb.Endpoint do
# The session will be stored in the cookie and signed, # The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with. # this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it. # Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session, plug Plug.Session, @session_options
store: :cookie,
key: "_frenzy_key",
signing_salt: "bL/djGKI"
plug FrenzyWeb.Router plug FrenzyWeb.Router
end end

View File

@ -0,0 +1,213 @@
defmodule FrenzyWeb.ConfigureStage.ConditionalStageLive do
use FrenzyWeb, :live_component
alias Frenzy.{Keypath, JSONSchema}
@stages FrenzyWeb.EditPipelineLive.stages()
@impl true
def mount(socket) do
{:ok, assign(socket, stages: @stages, confirm_convert_to_rule: false)}
end
@impl true
def update(assigns, socket) do
assigns = Map.put(assigns, :opts, Frenzy.Keypath.get(assigns.stage, assigns.keypath))
{:ok, assign(socket, assigns)}
end
@impl true
def handle_event("update_stage", %{"opts" => %{"stage" => ""}}, socket) do
{:noreply, socket}
end
@impl true
def handle_event("update_stage", %{"opts" => %{"stage" => module}}, socket) do
default_options = apply(String.to_existing_atom("Elixir." <> module), :default_opts, [])
new_opts =
socket.assigns.opts
|> Map.put("stage", module)
|> Map.put("opts", default_options)
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("update_filter", %{"mode" => mode, "score" => score}, socket) do
new_opts =
socket.assigns.opts
|> Keypath.set(["condition", "mode"], mode)
|> Keypath.set(["condition", "score"], JSONSchema.coerce(:integer, score))
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("add_rule", _params, socket) do
new_rules =
socket.assigns.opts["condition"]["rules"] ++
[%{"mode" => "text", "param" => "", "property" => "title", "weight" => 1}]
new_stage =
Frenzy.Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition", "rules"],
new_rules
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("delete_rule", %{"index" => index}, socket) do
index = String.to_integer(index)
new_rules =
socket.assigns.opts
|> Frenzy.Keypath.get(["condition", "rules"])
|> List.delete_at(index)
new_stage =
Frenzy.Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition", "rules"],
new_rules
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event(
"update_rule",
%{
"index" => "no_index",
"mode" => mode,
"param" => param,
"property" => property,
"weight" => weight
},
socket
) do
new_rule =
socket.assigns.opts["condition"]
|> Map.put("mode", mode)
|> Map.put("param", param)
|> Map.put("property", property)
|> Map.put("weight", JSONSchema.coerce(:integer, weight))
new_stage =
Frenzy.Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition"],
new_rule
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event(
"update_rule",
%{
"index" => index,
"mode" => mode,
"param" => param,
"property" => property,
"weight" => weight
},
socket
) do
index = String.to_integer(index)
new_rule =
Enum.at(socket.assigns.opts["condition"]["rules"], index)
|> Map.put("mode", mode)
|> Map.put("param", param)
|> Map.put("property", property)
|> Map.put("weight", JSONSchema.coerce(:integer, weight))
new_stage =
Frenzy.Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition", "rules", index],
new_rule
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("convert_to_filter", _value, socket) do
rule = socket.assigns.opts["condition"]
filter = %{"mode" => "accept", "score" => 1, "rules" => [rule]}
new_stage =
Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition"],
filter
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("convert_to_rule", _value, socket) do
rules = socket.assigns.opts["condition"]["rules"]
case length(rules) do
0 ->
new_stage =
Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition"],
%{"mode" => "text", "param" => "", "property" => "title", "weight" => 1}
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
1 ->
handle_event("confirm_convert_to_rule", nil, socket)
_count ->
{:noreply, assign(socket, confirm_convert_to_rule: true)}
end
end
@impl true
def handle_event("cancel_convert_to_rule", _value, socket) do
{:noreply, assign(socket, confirm_convert_to_rule: false)}
end
@impl true
def handle_event("confirm_convert_to_rule", _value, socket) do
rule = socket.assigns.opts["condition"]["rules"] |> List.first()
new_stage =
Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["condition"],
rule
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, assign(socket, confirm_convert_to_rule: false)}
end
def component_module(name) do
FrenzyWeb.EditPipelineLive.component_module(name)
end
end

View File

@ -0,0 +1,68 @@
<div id="<%= @id %>">
<%= if Mix.env == :dev do %>
<pre><%= Jason.encode!(@opts, pretty: true) %></pre>
<% end %>
<%= f = form_for @opts, "#", [as: :opts, phx_change: :update_stage, phx_target: @myself] %>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="<%= @id %>-stage">Module</label>
<div class="col-sm-10">
<%= select f, :stage, @stages, prompt: "Select a stage...", id: "#{@id}-stage", class: "custom-select" %>
</div>
</div>
</form>
<div class="card mb-2">
<div class="card-header">
<h4 class="m-0"><%= @opts["stage"] %></h4>
</div>
<div class="card-body">
<% component = component_module(@opts["stage"]) %>
<%= unless is_nil(component) do %>
<%= live_component(@socket, component, index: @index, id: "#{@id}-conditional", stage: @stage, keypath: @keypath ++ ["opts"]) %>
<% end %>
</div>
</div>
<%= if @opts["condition"]["mode"] in ["accept", "reject"] do %>
<div class="card">
<div class="card-header container-fluid">
<div class="row">
<div class="col">
<h4 class="m-0">Condition: Filter</h4>
</div>
<div class="col text-right">
<button phx-click="convert_to_rule" phx-target="#<%= @id %>" class="btn btn-primary btn-sm">Convert to Rule</button>
</div>
</div>
</div>
<div class="card-body">
<%= if @confirm_convert_to_rule do %>
<div class="alert alert-danger mb-2">
<p>This will modify the conditional stage to only run when the first rule is met. Are you sure you want to proceed?</p>
<button class="btn btn-danger btn-sm" phx-click="confirm_convert_to_rule" phx-target="#<%= @id %>">Convert to Rule</button>
<button class="btn btn-secondary btn-sm" phx-click="cancel_convert_to_rule" phx-target="#<%= @id %>">Cancel</button>
</div>
<% end %>
<%= live_component @socket, FrenzyWeb.FilterLive, id: "##{@id}-filter", parent_id: @id, filter: @opts["condition"] %>
</div>
</div>
<% else %>
<div class="card">
<div class="card-header container-fluid">
<div class="row">
<div class="col">
<h4 class="m-0">Condition: Rule</h4>
</div>
<div class="col text-right">
<button phx-click="convert_to_filter" phx-target="#<%= @id %>" class="btn btn-primary btn-sm">Convert to Filter</button>
</div>
</div>
</div>
<div class="card-body">
<%= live_component @socket, FrenzyWeb.FilterRuleLive, id: "##{@id}-rule", parent_id: @id, rule: @opts["condition"], index: :no_index %>
</div>
</div>
<% end %>
</div>

View File

@ -0,0 +1,79 @@
defmodule FrenzyWeb.ConfigureStage.FilterStageLive do
use FrenzyWeb, :live_component
alias Frenzy.JSONSchema
@impl true
def update(assigns, socket) do
assigns = Map.put(assigns, :opts, Frenzy.Keypath.get(assigns.stage, assigns.keypath))
{:ok, assign(socket, assigns)}
end
@impl true
def handle_event("update_filter", %{"mode" => mode, "score" => score}, socket) do
new_opts =
socket.assigns.opts
|> Map.put("mode", mode)
|> Map.put("score", JSONSchema.coerce(:integer, score))
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("add_rule", _params, socket) do
new_rules =
socket.assigns.opts["rules"] ++
[%{"mode" => "text", "param" => "", "property" => "title", "weight" => 1}]
new_opts = Map.put(socket.assigns.opts, "rules", new_rules)
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event("delete_rule", %{"index" => index}, socket) do
index = String.to_integer(index)
new_rules = List.delete_at(socket.assigns.opts["rules"], index)
new_opts = Map.put(socket.assigns.opts, "rules", new_rules)
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
@impl true
def handle_event(
"update_rule",
%{
"index" => index,
"mode" => mode,
"param" => param,
"property" => property,
"weight" => weight
},
socket
) do
index = String.to_integer(index)
new_rule =
Enum.at(socket.assigns.opts["rules"], index)
|> Map.put("mode", mode)
|> Map.put("param", param)
|> Map.put("property", property)
|> Map.put("weight", JSONSchema.coerce(:integer, weight))
new_stage =
Frenzy.Keypath.set(
socket.assigns.stage,
socket.assigns.keypath ++ ["rules", index],
new_rule
)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
end

View File

@ -0,0 +1,6 @@
<div id="<%= @id %>">
<%= if Mix.env == :dev do %>
<pre><%= Jason.encode!(@opts, pretty: true) %></pre>
<% end %>
<%= live_component @socket, FrenzyWeb.FilterLive, id: "#{@id}-filter", parent_id: @id, filter: @opts %>
</div>

View File

@ -0,0 +1,58 @@
defmodule FrenzyWeb.ConfigureStage.ScrapeStageLive do
use FrenzyWeb, :live_component
@extractors [
{"Builtin", "builtin"},
{"beckyhansmeyer.com", Frenzy.Pipeline.Extractor.BeckyHansmeyer},
{"daringfireball.net", Frenzy.Pipeline.Extractor.DaringFireball},
{"ericasadun.com", Frenzy.Pipeline.Extractor.EricaSadun},
{"finertech.com", Frenzy.Pipeline.Extractor.FinerTech},
{"macstories.net", Frenzy.Pipeline.Extractor.MacStories},
{"om.co", Frenzy.Pipeline.Extractor.OmMalik},
{"whatever.scalzi.com", Frenzy.Pipeline.Extractor.WhateverScalzi}
]
|> Enum.map(fn {pretty_name, module} ->
{
pretty_name,
case module do
"builtin" -> "builtin"
module -> module |> to_string() |> String.slice(7..-1)
end
}
end)
@schema %{
"convert_to_data_uris" => :boolean,
"extractor" => :string
}
@impl true
def mount(socket) do
{:ok, assign(socket, extractors: @extractors)}
end
@impl true
def update(assigns, socket) do
assigns = Map.put(assigns, :opts, Frenzy.Keypath.get(assigns.stage, assigns.keypath))
{:ok, assign(socket, assigns)}
end
@impl true
def handle_event(
"update_stage",
%{"opts" => %{"convert_to_data_uris" => convert_to_data_uris, "extractor" => extractor}},
socket
) do
convert_to_data_uris = String.downcase(convert_to_data_uris) == "true"
new_opts =
socket.assigns.opts
|> Map.put("convert_to_data_uris", convert_to_data_uris)
|> Map.put("extractor", extractor)
new_stage = Frenzy.Keypath.set(socket.assigns.stage, socket.assigns.keypath, new_opts)
send(self(), {:update_stage, socket.assigns.index, new_stage})
{:noreply, socket}
end
end

View File

@ -0,0 +1,17 @@
<div id="<%= @id %>">
<%= if Mix.env == :dev do %>
<pre><%= Jason.encode!(@opts, pretty: true) %></pre>
<% end %>
<%= f = form_for @opts, "#", [as: :opts, phx_change: :update_stage, phx_target: @myself] %>
<div class="form-group form-check">
<%= checkbox f, :convert_to_data_uris, id: "#{@id}-convert_to_data_uris", class: "form-check-input" %>
<label class="form-check-label" for="<%= @id %>-convert_to_data_uris">Convert Images to Embedded Data URIs</label>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="<%= @id %>-extractor">Extractor</label>
<div class="col-sm-10">
<%= select f, :extractor, @extractors, id: "#{@id}-extractor", class: "custom-select" %>
</div>
</div>
</form>
</div>

View File

@ -0,0 +1,117 @@
defmodule FrenzyWeb.EditPipelineLive do
use FrenzyWeb, :live_view
use Phoenix.HTML
alias Frenzy.{Repo, Pipeline}
@stages [
{"Filter Stage", "Frenzy.Pipeline.FilterStage"},
{"Scrape Stage", "Frenzy.Pipeline.ScrapeStage"},
{"Conditional Stage", "Frenzy.Pipeline.ConditionalStage"}
]
def stages, do: @stages
@impl true
def mount(_, %{"pipeline_id" => pipeline_id}, socket) do
pipeline = Repo.get(Pipeline, pipeline_id)
{:ok,
assign(socket,
pipeline: pipeline,
stages: @stages
)}
end
@impl true
def handle_event("add_stage", %{"stage" => %{"module_name" => module}}, socket) do
pipeline = socket.assigns[:pipeline]
default_options = apply(String.to_existing_atom("Elixir." <> module), :default_opts, [])
changeset =
Pipeline.changeset(pipeline, %{
stages:
pipeline.stages ++
[%{"module_name" => module, "options" => default_options}]
})
{:ok, pipeline} = Repo.update(changeset)
{:noreply, assign(socket, pipeline: pipeline)}
end
def handle_event("delete_stage", %{"index" => index}, socket) do
pipeline = socket.assigns.pipeline
index = String.to_integer(index)
changeset =
Pipeline.changeset(pipeline, %{
stages: List.delete_at(pipeline.stages, index)
})
{:ok, pipeline} = Repo.update(changeset)
{:noreply, assign(socket, pipeline: pipeline)}
end
def handle_event(event, %{"index" => index}, socket) when event in ["move_up", "move_down"] do
index = String.to_integer(index)
offset =
case event do
"move_up" -> -1
"move_down" -> 1
end
pipeline = socket.assigns.pipeline
stage = Enum.at(pipeline.stages, index)
changeset =
Pipeline.changeset(pipeline, %{
stages:
pipeline.stages
|> List.delete_at(index)
|> List.insert_at(index + offset, stage)
})
{:ok, pipeline} = Repo.update(changeset)
{:noreply, assign(socket, pipeline: pipeline)}
end
@impl true
def handle_info({:update_stage, index, new_stage}, socket) do
pipeline = socket.assigns.pipeline
new_stages = List.replace_at(pipeline.stages, index, new_stage)
changeset = Pipeline.changeset(pipeline, %{stages: new_stages})
{:ok, pipeline} = Repo.update(changeset)
{:noreply, assign(socket, pipeline: pipeline)}
end
def component_module(module_name) do
case module_name do
"Frenzy.Pipeline.ScrapeStage" ->
FrenzyWeb.ConfigureStage.ScrapeStageLive
"Frenzy.Pipeline.FilterStage" ->
FrenzyWeb.ConfigureStage.FilterStageLive
"Frenzy.Pipeline.ConditionalStage" ->
FrenzyWeb.ConfigureStage.ConditionalStageLive
_ ->
nil
end
end
def component_for(socket, %{"module_name" => module} = stage, index) do
case component_module(module) do
nil ->
nil
component ->
live_component(socket, component,
index: index,
id: "stage-#{index}",
stage: stage,
keypath: ["options"]
)
end
end
end

View File

@ -0,0 +1,35 @@
<h1>Edit <%= @pipeline.name %></h1>
<a href="<%= Routes.pipeline_path(FrenzyWeb.Endpoint, :edit, @pipeline.id, json: "") %>" class="btn btn-primary">Edit as JSON</a>
<%= for {stage, index} <- Enum.with_index(@pipeline.stages) do %>
<div class="card mt-4">
<div class="card-header container-fluid">
<div class="row">
<div class="col">
<h4 class="m-0"><%= stage["module_name"] %></h4>
</div>
<div class="col text-right">
<%= content_tag :button, "Move Up", [phx_click: :move_up, phx_value_index: index, disabled: index == 0, class: "btn btn-secondary btn-sm"] %>
<%= content_tag :button, "Move Down", [phx_click: :move_down, phx_value_index: index, disabled: index == length(@pipeline.stages) - 1, class: "btn btn-secondary btn-sm"] %>
<button phx-click="delete_stage" phx-value-index="<%= index %>" class="btn btn-danger btn-sm">Delete</button>
</div>
</div>
</div>
<div class="card-body">
<%= component_for(@socket, stage, index) %>
</div>
</div>
<% end %>
<%= f = form_for :stage, "#", [class: "mt-4 mb-4", phx_submit: :add_stage] %>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="module_name">Module</label>
<div class="col-sm-10">
<%= select f, :module_name, @stages, class: "custom-select" %>
</div>
</div>
<%= submit "Add Stage", class: "btn btn-primary" %>
</form>

View File

@ -0,0 +1,13 @@
defmodule FrenzyWeb.FilterLive do
use FrenzyWeb, :live_component
@filter_modes [
{"Accept", "accept"},
{"Reject", "reject"}
]
@impl true
def mount(socket) do
{:ok, assign(socket, modes: @filter_modes)}
end
end

View File

@ -0,0 +1,37 @@
<div id="<%= @id %>">
<%= f = form_for @filter, "#", [phx_change: :update_filter, phx_target: "##{@parent_id}"] %>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="<%= @id %>-mode">Mode</label>
<div class="col-sm-10">
<%= select f, :mode, @modes, id: "#{@id}-mode", class: "custom-select" %>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="<%= @id %>-score">Score</label>
<div class="col-sm-10">
<%= number_input f, :score, id: "#{@id}-score", class: "form-control" %>
</div>
</div>
</form>
<%= for {rule, index} <- Enum.with_index(@filter["rules"]) do %>
<div class="card mt-2">
<div class="card-header container-fluid">
<div class="row">
<div class="col">
<h4 class="m-0">Rule <%= index %></h4>
</div>
<div class="col text-right">
<button phx-click="delete_rule" phx-value-index="<%= index %>" phx-target="#<%= @id %>" class="btn btn-danger btn-sm">Delete</button>
</div>
</div>
</div>
<div class="card-body">
<%= live_component @socket, FrenzyWeb.FilterRuleLive, id: "#{@id}-rule-#{index}", parent_id: @parent_id, rule: rule, index: index %>
</div>
</div>
<% end %>
<%= f = form_for :rule, "#", class: "mt-2", phx_submit: :add_rule, phx_target: "##{@parent_id}" %>
<%= submit "Add Rule", class: "btn btn-primary" %>
</form>
</div>

View File

@ -0,0 +1,19 @@
defmodule FrenzyWeb.FilterRuleLive do
use FrenzyWeb, :live_component
@modes [
{"Contains Substring", "contains_string"},
{"Matches Regex", "matches_regex"}
]
@properties [
{"Title", "title"},
{"URL", "url"},
{"Author", "author"}
]
@impl true
def mount(socket) do
{:ok, assign(socket, modes: @modes, properties: @properties)}
end
end

View File

@ -0,0 +1,29 @@
<div id="<%= @id %>">
<%= f = form_for @rule, "#", [phx_change: :update_rule, phx_target: "##{@parent_id}"] %>
<%= hidden_input f, :index, value: @index %>
<div class="form-group row mb-2">
<label class="col-sm-2 col-form-label" for="<%= @id %>-property">Item Property</label>
<div class="col-sm-10">
<%= select f, :property, @properties, id: "#{@id}-property", class: "custom-select" %>
</div>
</div>
<div class="form-group row mb-2">
<label class="col-sm-2 col-form-label" for="<%= @id %>-mode">Mode</label>
<div class="col-sm-10">
<%= select f, :mode, @modes, id: "#{@id}-mode", class: "custom-select" %>
</div>
</div>
<div class="form-group row mb-2">
<label class="col-sm-2 col-form-label" for="<%= @id %>-param">Value</label>
<div class="col-sm-10">
<%= text_input f, :param, id: "#{@id}-param", placeholder: if(@rule["mode"] == "contains_string", do: "substring", else: "regex"), class: "form-control text-monospace" %>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-sm-2 col-form-label" for="<%= @id %>-weight">Rule Weight</label>
<div class="col-sm-10">
<%= number_input f, :weight, id: "#{@id}-weight", class: "form-control" %>
</div>
</div>
</form>
</div>

View File

@ -0,0 +1,47 @@
defimpl Phoenix.HTML.FormData, for: Map do
alias Phoenix.HTML.Form
def to_form(map, opts) do
{name, opts} = Keyword.pop(opts, :as)
{errors, opts} = Keyword.pop(opts, :errors)
%Form{
source: map,
impl: __MODULE__,
id: name,
name: name,
params: map,
data: %{},
errors: errors,
options: opts
}
end
def to_form(_data, _form, _field, _opts) do
raise "Unimplemented"
end
def input_value(_data, %Form{data: data, params: params}, field) do
key =
case field do
field when is_atom(field) -> Atom.to_string(field)
field -> field
end
case Map.fetch(params, key) do
{:ok, value} ->
value
:error ->
Map.get(data, key)
end
end
def input_validations(_data, _form, _field) do
raise "Unimplemented"
end
def input_type(_data, _form, _field) do
raise "Unimplemented"
end
end

View File

@ -12,7 +12,9 @@ defmodule FrenzyWeb.Plug.Authenticate do
case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 24 * 60 * 60) do case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 24 * 60 * 60) do
{:error, _reason} -> {:error, _reason} ->
conn conn
|> Phoenix.Controller.redirect(to: Routes.login_path(Endpoint, :login)) |> Phoenix.Controller.redirect(
to: Routes.login_path(Endpoint, :login, continue: Phoenix.Controller.current_path(conn))
)
|> halt() |> halt()
{:ok, user_id} -> {:ok, user_id} ->

View File

@ -4,9 +4,10 @@ defmodule FrenzyWeb.Router do
pipeline :browser do pipeline :browser do
plug :accepts, ["html"] plug :accepts, ["html"]
plug :fetch_session plug :fetch_session
plug :fetch_flash plug :fetch_live_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_root_layout, {FrenzyWeb.LayoutView, :root}
end end
pipeline :browser_authenticate do pipeline :browser_authenticate do

View File

@ -12,7 +12,7 @@
<% end %> <% end %>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<%= form_for @conn, Routes.account_path(@conn, :export), [method: :post, class: "mt-2"], fn f -> %> <%= form_for @conn, Routes.account_path(@conn, :export), [method: :post, class: "mt-2"], fn _f -> %>
<%= submit "Export OPML", class: "btn btn-primary" %> <%= submit "Export OPML", class: "btn btn-primary" %>
<% end %> <% end %>
</li> </li>

View File

@ -2,7 +2,9 @@
<%= if @feed.favicon do %> <%= if @feed.favicon do %>
<img src="<%= @feed.favicon %>" alt="<%= @feed.title %> favicon" class="favicon"> <img src="<%= @feed.favicon %>" alt="<%= @feed.title %> favicon" class="favicon">
<% end %> <% end %>
<a href="<%= feed_site_url(@feed) %>" target="_blank" rel="noreferrer noopener">
<%= @feed.title %> <%= @feed.title %>
</a>
</h1> </h1>
<%= form_tag Routes.feed_path(@conn, :refresh, @feed.id), method: :post, class: "d-inline" do %> <%= form_tag Routes.feed_path(@conn, :refresh, @feed.id), method: :post, class: "d-inline" do %>

View File

@ -1,23 +1,3 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<title>Frenzy</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/open-iconic-bootstrap.min.css") %>"/>
</head>
<body>
<header>
<main class="main mt-4" role="main">
<div class="container">
<label class="sidebar-toggle" for="show-sidebar">
<span class="oi oi-menu" title="Open Menu"></span>
</label>
<%= if get_flash(@conn, :info) do %> <%= if get_flash(@conn, :info) do %>
<p class="alert alert-primary" role="alert"><%= get_flash(@conn, :info) %></p> <p class="alert alert-primary" role="alert"><%= get_flash(@conn, :info) %></p>
<% end %> <% end %>
@ -25,76 +5,4 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<% end %> <% end %>
<%= render @view_module, @view_template, assigns %> <%= @inner_content %>
</div>
</main>
<input id="show-sidebar" type="checkbox" style="display: none;">
<div class="container-fluid sidebar-container">
<div class="row">
<div class="col-sm-6 col-md-3 py-2 sidebar">
<label for="show-sidebar" class="mt-2 sidebar-toggle">
<span class="oi oi-x" title="Close Menu"></span>
</label>
<div class="d-flex flex-column sidebar-nav">
<ul class="nav flex-column">
<li class="nav-item">
<h1 class="sidebar-title">
<a href="/" class="nav-link">Frenzy</a>
</h1>
</li>
<%= unless is_nil(@conn.assigns[:user]) do %>
<li class="nav-item">
<h3 class="sidebar-title">
<a href="<%= Routes.group_path(@conn, :index) %>" class="nav-link">Groups</a>
</h3>
</li>
<%= for group <- user_groups(@user) do %>
<li class="nav-item">
<details open="">
<summary>
<a href="<%= Routes.group_path(@conn, :show, group.id) %>"><%= group.title %></a>
</summary>
<ul class="nav flex-column">
<%= for feed <- group.feeds do %>
<li class="nav-item feed-nav-item">
<a href="<%= Routes.feed_path(@conn, :show, feed.id) %>" class="nav-link">
<%= if feed.favicon do %>
<img src="<%= feed.favicon %>" alt="<%= feed.title %> favicon" class="favicon">
<% end %>
<%= feed.title %>
</a>
</li>
<% end %>
</ul>
</details>
</li>
<% end %>
<li class="nav-item">
<h3 class="sidebar-title">
<a href="<%= Routes.pipeline_path(@conn, :index) %>" class="nav-link">Pipelines</a>
</h3>
</li>
<% end %>
</ul>
<ul class="nav flex-column mt-auto">
<%= unless is_nil(@conn.assigns[:user]) do %>
<li class="nav-item"><a href="<%= Routes.account_path(@conn, :show) %>" class="nav-link">Account</a></li>
<li class="nav-item"><a href="<%= Routes.login_path(@conn, :logout) %>" class="nav-link">Log Out</a></li>
<% end %>
</ul>
</div>
</div>
<div class="col sidebar-background"><label for="show-sidebar"></label></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
<p class="alert alert-primary" role="alert" phx-click="lv:clear-flash" phx-value-key="info"><%= live_flash(@flash, :info) %></p>
<p class="alert alert-danger" role="alert" phx-click="lv:clear-flash" phx-value-key="error"><%= live_flash(@flash, :error) %></p>
<%= @inner_content %>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<title>Frenzy</title>
<%= csrf_meta_tag() %>
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<header>
<main class="main mt-4" role="main">
<div class="container">
<label class="sidebar-toggle" for="show-sidebar">
<span class="oi oi-menu" title="Open Menu"></span>
</label>
<%= @inner_content %>
</div>
</main>
<input id="show-sidebar" type="checkbox" style="display: none;">
<div class="container-fluid sidebar-container">
<div class="row">
<div class="col-sm-6 col-md-3 py-2 sidebar">
<label for="show-sidebar" class="mt-2 sidebar-toggle">
<span class="oi oi-x" title="Close Menu"></span>
</label>
<div class="d-flex flex-column sidebar-nav">
<ul class="nav flex-column">
<li class="nav-item">
<h1 class="sidebar-title">
<a href="/" class="nav-link">Frenzy</a>
</h1>
</li>
<%= unless is_nil(@conn.assigns[:user]) do %>
<li class="nav-item">
<h3 class="sidebar-title">
<a href="<%= Routes.group_path(@conn, :index) %>" class="nav-link">Groups</a>
</h3>
</li>
<%= for group <- user_groups(@user) do %>
<li class="nav-item">
<details open="">
<summary>
<a href="<%= Routes.group_path(@conn, :show, group.id) %>"><%= group.title %></a>
</summary>
<ul class="nav flex-column">
<%= for feed <- group.feeds do %>
<li class="nav-item feed-nav-item">
<a href="<%= Routes.feed_path(@conn, :show, feed.id) %>" class="nav-link">
<%= if feed.favicon do %>
<img src="<%= feed.favicon %>" alt="<%= feed.title %> favicon" class="favicon">
<% end %>
<%= feed.title %>
</a>
</li>
<% end %>
</ul>
</details>
</li>
<% end %>
<li class="nav-item">
<h3 class="sidebar-title">
<a href="<%= Routes.pipeline_path(@conn, :index) %>" class="nav-link">Pipelines</a>
</h3>
</li>
<% end %>
</ul>
<ul class="nav flex-column mt-auto">
<%= unless is_nil(@conn.assigns[:user]) do %>
<li class="nav-item"><a href="<%= Routes.account_path(@conn, :show) %>" class="nav-link">Account</a></li>
<li class="nav-item"><a href="<%= Routes.login_path(@conn, :logout) %>" class="nav-link">Log Out</a></li>
<% end %>
</ul>
</div>
</div>
<div class="col sidebar-background"><label for="show-sidebar"></label></div>
</div>
</div>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

View File

@ -1,5 +1,7 @@
<h1>Edit Pipeline</h1> <h1>Edit Pipeline</h1>
<a href="<%= Routes.pipeline_path(@conn, :edit, @pipeline.id) %>" class="btn btn-primary">Edit UI</a>
<%= form_tag Routes.pipeline_path(@conn, :update, @pipeline.id), method: :put do %> <%= form_tag Routes.pipeline_path(@conn, :update, @pipeline.id), method: :put do %>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label" for="name">Name</label> <label class="col-sm-2 col-form-label" for="name">Name</label>

View File

@ -1,3 +1,13 @@
defmodule FrenzyWeb.FeedView do defmodule FrenzyWeb.FeedView do
use FrenzyWeb, :view use FrenzyWeb, :view
alias Frenzy.Feed
@spec feed_site_url(feed :: Feed.t()) :: String.t()
def feed_site_url(%Feed{site_url: site_url}) when is_binary(site_url) do
site_url
end
def feed_site_url(%Feed{feed_url: feed_url}) do
URI.merge(feed_url, "/") |> to_string()
end
end end

View File

@ -1,6 +1,6 @@
defmodule FrenzyWeb.LayoutView do defmodule FrenzyWeb.LayoutView do
use FrenzyWeb, :view use FrenzyWeb, :view
alias Frenzy.{Repo, Group, Feed} alias Frenzy.{Repo, Group}
import Ecto.Query import Ecto.Query
def user_groups(user) do def user_groups(user) do

14
mix.exs
View File

@ -33,16 +33,16 @@ defmodule Frenzy.MixProject do
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[ [
{:phoenix, "~> 1.4.0"}, {:phoenix, "~> 1.5.2"},
{:phoenix_pubsub, "~> 1.1"}, {:phoenix_pubsub, "~> 2.0"},
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.1"},
{:ecto_sql, "~> 3.0"}, {:ecto_sql, "~> 3.4"},
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.11"}, {:phoenix_html, "~> 2.11"},
{:phoenix_live_reload, "~> 1.2", only: :dev}, {:phoenix_live_reload, "~> 1.2", only: :dev},
{:gettext, "~> 0.11"}, {:gettext, "~> 0.11"},
{:jason, "~> 1.0"}, {:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"}, {:plug_cowboy, "~> 2.1"},
{:httpoison, "~> 1.6.2"}, {:httpoison, "~> 1.6.2"},
{:hackney, "~> 1.16"}, {:hackney, "~> 1.16"},
{:feed_parser, {:feed_parser,
@ -52,7 +52,9 @@ defmodule Frenzy.MixProject do
{:bcrypt_elixir, "~> 2.0"}, {:bcrypt_elixir, "~> 2.0"},
{:dialyxir, "~> 1.0.0-rc.6", only: :dev, runtime: false}, {:dialyxir, "~> 1.0.0-rc.6", only: :dev, runtime: false},
{:xml_builder, "~> 2.1.1"}, {:xml_builder, "~> 2.1.1"},
{:floki, "~> 0.23"} {:floki, "~> 0.23"},
{:phoenix_live_view,
git: "https://github.com/phoenixframework/phoenix_live_view", branch: "master"}
] ]
end end

View File

@ -5,13 +5,13 @@
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.1.1", "0abd6bae41acc01c369bb3eafe46399f301bf4e1bacebafdb89252bbb8a1a32d", [:mix], [], "hexpm", "b77aef9eb7ec7a4c01cc3d0683332796052ab71067d858d5dacde967427de0a3"}, "comeonin": {:hex, :comeonin, "5.1.1", "0abd6bae41acc01c369bb3eafe46399f301bf4e1bacebafdb89252bbb8a1a32d", [:mix], [], "hexpm", "b77aef9eb7ec7a4c01cc3d0683332796052ab71067d858d5dacde967427de0a3"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"cowboy": {:hex, :cowboy, "2.6.0", "dc1ff5354c89e36a3e3ef8d10433396dcff0dcbb1d4223b58c64c2d51a6d88d9", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "ff7cf0fb7a2762f423d845468b32ed4edb5c7e0d78e6875b0628153239a7bb34"}, "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm", "59f952d504b9921a6f53226fdb8b4a671bb694277c7ccf1ddeaadcdc6d43d88e"}, "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
"db_connection": {:hex, :db_connection, "2.0.2", "440c05518b0bdca0469dafaf45403597430448c1281def14ef9ccaa41833ea1e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "da1cfb8ba1e2b77b594305ea62cce8d8c64776199f0d9a849718c4f0fceb9ad2"}, "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm", "bbd124e240e3ff40f407d50fced3736049e72a73d547f69201484d3a624ab569"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "49496d63267bc1a4614ffd5f67c45d9fc3ea62701a6797975bc98bc156d2763f"}, "dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "49496d63267bc1a4614ffd5f67c45d9fc3ea62701a6797975bc98bc156d2763f"},
"ecto": {:hex, :ecto, "3.0.3", "018a3df0956636f84eb3033d807485a7d3dea8474f47b90da5cb8073444c4384", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "30b0956495d12b53dbabe7965bfb40e4cd5c49c2f0b051b23fb94ac2854006ea"}, "ecto": {:hex, :ecto, "3.4.4", "a2c881e80dc756d648197ae0d936216c0308370332c5e77a2325a10293eef845", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4bd3ad62abc3b21fb629f0f7a3dab23a192fca837d257dd08449fba7373561"},
"ecto_sql": {:hex, :ecto_sql, "3.0.2", "0e04cbc183b91ea0085c502226befcd237a4ac31c204fd4be8d4db6676b5f10d", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.2.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c047508d619a04ff193ea6e92f92701815fdee195be93bfd89bbee3025898b5e"}, "ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
"elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm", "382eeea8e02dfe6c468f6729b6cf20fe5b14390671d38c7363e59621c7ab4efc"}, "elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm", "382eeea8e02dfe6c468f6729b6cf20fe5b14390671d38c7363e59621c7ab4efc"},
"erlex": {:hex, :erlex, "0.2.4", "23791959df45fe8f01f388c6f7eb733cc361668cbeedd801bf491c55a029917b", [:mix], [], "hexpm", "4a12ebc7cd8f24f2d0fce93d279fa34eb5068e0e885bb841d558c4d83c52c439"}, "erlex": {:hex, :erlex, "0.2.4", "23791959df45fe8f01f388c6f7eb733cc361668cbeedd801bf491c55a029917b", [:mix], [], "hexpm", "4a12ebc7cd8f24f2d0fce93d279fa34eb5068e0e885bb841d558c4d83c52c439"},
"feed_parser": {:git, "https://git.shadowfacts.net/shadowfacts/feed_parser.git", "8c42d4587328698e8d29d2ad562e478abb146f75", [branch: "master"]}, "feed_parser": {:git, "https://git.shadowfacts.net/shadowfacts/feed_parser.git", "8c42d4587328698e8d29d2ad562e478abb146f75", [branch: "master"]},
@ -23,27 +23,28 @@
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm", "3e3d7156a272950373ce5a4018b1490bea26676f8d6a7d409f6fac8568b8cb9a"}, "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm", "3e3d7156a272950373ce5a4018b1490bea26676f8d6a7d409f6fac8568b8cb9a"},
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"}, "idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm", "5e839994289d60326aa86020c4fbd9c6938af188ecddab2579f07b66cd665328"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "22da8f659cf13d3ba73b767f66b8c389113ddf0ef7b94225cc84e94b85eac90e"}, "phoenix": {:hex, :phoenix, "1.5.3", "bfe0404e48ea03dfe17f141eff34e1e058a23f15f109885bbdcf62be303b49ff", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e16febeb9640d8b33895a691a56481464b82836d338bb3a23125cd7b6157c25"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "fe15d9fee5b82f5e64800502011ffe530650d42e1710ae9b14bc4c9be38bf303"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
"phoenix_html": {:hex, :phoenix_html, "2.12.0", "1fb3c2e48b4b66d75564d8d63df6d53655469216d6b553e7e14ced2b46f97622", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "8bc079e2092f4447c8c75029b297cfc3256b8aa46dff02a16eaf1ade5eae96f6"}, "phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b61c87a23fabcd85ff369fb9c041d9c01787d210322749026f56a69a914b7503"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b61c87a23fabcd85ff369fb9c041d9c01787d210322749026f56a69a914b7503"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm", "a3d890aaa3156d51056179dcaaadaf32b844f71656bb27c58756f2b97875c36c"}, "phoenix_live_view": {:git, "https://github.com/phoenixframework/phoenix_live_view", "a622fed5e496561eb05a7fce5423d238c2597142", [branch: "master"]},
"plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm", "daa5fee4209c12c3c48b05a96cf88c320b627c9575f987554dcdc1fdcdf2c15e"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
"plug_cowboy": {:hex, :plug_cowboy, "2.0.0", "ab0c92728f2ba43c544cce85f0f220d8d30fc0c90eaa1e6203683ab039655062", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "fa9087d93f4962d099b9c4246dbea97c2f2e6aab1029e8b8206e733a1274cecc"}, "plug": {:hex, :plug, "1.10.2", "0079345cfdf9e17da3858b83eb46bc54beb91554c587b96438f55c1477af5a86", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7898d0eb4767efb3b925fd7f9d1870d15e66e9c33b89c58d8d2ad89aa75ab3c1"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm", "73c1682f0e414cfb5d9b95c8e8cd6ffcfdae699e3b05e1db744e58b7be857759"}, "plug_cowboy": {:hex, :plug_cowboy, "2.2.2", "7a09aa5d10e79b92d332a288f21cc49406b1b994cbda0fde76160e7f4cc890ea", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e82364b29311dbad3753d588febd7e5ef05062cd6697d8c231e0e007adab3727"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
"postgrex": {:hex, :postgrex, "0.14.0", "f3d6ffea1ca8a156e0633900a5338a3d17b00435227726baed8982718232b694", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ee6d8bc31ec7064f87030b98e431cf7ef0537052339de289b575b0a8872f501e"}, "postgrex": {:hex, :postgrex, "0.15.5", "aec40306a622d459b01bff890fa42f1430dac61593b122754144ad9033a2152f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ed90c81e1525f65a2ba2279dbcebf030d6d13328daa2f8088b9661eb9143af7f"},
"ranch": {:hex, :ranch, "1.7.0", "9583f47160ca62af7f8d5db11454068eaa32b56eeadf984d4f46e61a076df5f2", [:rebar3], [], "hexpm", "59f7501c3a56125b2fc5684c3048fac9d043c0bf4d173941b12ca927949af189"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
"readability": {:git, "https://github.com/shadowfacts/readability.git", "71fa17caaf8103ef213e2c7dde4b447a48669122", [branch: "master"]}, "readability": {:git, "https://github.com/shadowfacts/readability.git", "71fa17caaf8103ef213e2c7dde4b447a48669122", [branch: "master"]},
"saxy": {:hex, :saxy, "0.6.0", "cdb2f2fcd8133d1f3f8b0cf6a131ee1ca348dca613de266e9a239db850c4a093", [:mix], [], "hexpm"}, "saxy": {:hex, :saxy, "0.6.0", "cdb2f2fcd8133d1f3f8b0cf6a131ee1ca348dca613de266e9a239db850c4a093", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"telemetry": {:hex, :telemetry, "0.2.0", "5b40caa3efe4deb30fb12d7cd8ed4f556f6d6bd15c374c2366772161311ce377", [:mix], [], "hexpm", "4e9071b8d1795d0f1ae00584594c3faf430c88821b69e4bd09b02e7840231f32"}, "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"}, "timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"},
"tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cf1345dfbce6acdfd4e23cbb36e96e53d1981bc89181cd0b936f4f398f4c0b78"}, "tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cf1345dfbce6acdfd4e23cbb36e96e53d1981bc89181cd0b936f4f398f4c0b78"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
// for phoenix_html support, including form and button helpers
// copy the following scripts into your javascript bundle:
// * https://raw.githubusercontent.com/phoenixframework/phoenix_html/v2.10.0/priv/static/phoenix_html.js

View File

@ -0,0 +1,4 @@
defmodule Frenzy.KeypathTest do
use ExUnit.Case
doctest Frenzy.Keypath
end