?n+Nf}QVxw~{;;LFF`^GUg3qJyw!9NL z55Lf7l0&K5FToWfuRW;I`x5NBXzc*GejgQy$1D71Iq(Q z$&x|V(9gQ8FwX~0_GX*_`ufKa8T*lCqZ`=258y$TCw5CED1Fy~+=52N6Br}|5({H& zw>P6ZfV8A;4HyIfSD(!mq22)4Y+4In&yAo+PXTmmR@U^Ovs>G4dIxYl(G|e~HjLoQ z$~%!gGY9kz;Ao^HPDVK~0C3F#y*&Z(EEG_XSc*`X>jP7<(SJM#mYjqG6No&`9>sc} zgco955D{vJ$*E~XCzPXaV1iLE2ST CyZ^l-cp3;PlCYbQ}oW3_bQ;XjcKxBZmMNG zC9m31Slmi~6DLWrQjka&6T+g>@7AHZmbqbBjA(!G7F;?jof|q_7k+CS#^p>I6yvJ- zwF)4}*BUz<%n-594kTM-RKF?ffI^`{O 6!Kh0@(Sj;Al% z8O|4;A-kV&fd0V=6g_K$a!L&zkHW~9a`_A_n!^<+LIQ1Y?Yt}fGz%r}Y841In-#s$ z@`zAd#Glv+0bg5yL`Yp7zgU5e&N2N1ARbKC2^cJSOrgT>ktJC13bOpyj;{W%*{c7X cuJ-TwAN3Mc{XL@gQ2+n{07*qoM6N<$f*7B!iU0rr literal 0 HcmV?d00001 diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1ddccbd598c6b8a72ea3b462875eaa20a37971ad GIT binary patch literal 15406 zcmeHOS9Fv`6#gym>Vxk*d3^NAH>HTZ5s)4csR0xV3M!%?peRK-3MiI?G!;ce(?fc) zKtc)$Jqa}=5J({jNjB|zzuC-W_TRr` }t%%uc&^?my+uH#dfHhjEv2@4W`c zBaFDa4dY?MFh-2<{eJL1!}y8w9(t&+`~k!8+;12Uavg5MEqwVLS%&zSksrm!JnBt| z8A-tq+6=a> n>{6D`_)nC6-@{}k1
3-z}pDd-G z&Q7X5l0!B5nUpf^X;q#yaV)h|U!v}gcJekiQP=HOL9&mtdw!s{x@*+i-A(l;N(pkv zF(6K#^AaIsFMh?BKYo X`R88h>F!ePJ9#_ (%(X+HO0JKY8j?)Y;lXO%-S8`Z13fJHA=| zvN+0lYnJ$4btMIK?PxAFoH;@0FJ<~Os!xnL_w6}Ub>J5 Iao&S5E zj}HDudGF3wWx=iisb9qZWev) DWFpXlu{$v+c8pfRo;~xEx<*w1k5aM(V zcxt?r-b2jSpU=>b+n@i?>fFpb (1n;t-w`}g>rkyiR_I9)FIwb$RE eU7eyT_U@_!d&T*hw`8s>@ATOd z#95BU_*ShoSE%*sWkudwjZy?YOZW8m$LIQ`D?abd!=YMq7yO#Bus?G-RzmTwB z Q$$lo5eowxaxh5mix z4;BBjdw*2lPbT#FcOfCP{IM6A1M=iI4mE$+&!={5rPDue6NI#GlZTqW9H(78l>A}8 z7p_@GseHCdd4`hz&GHg46)va6hlRiO{ukQ$FZGOgsQ4GIu|BJtFIEcuTdjZGO?6pi z+VlP*wS1lSU)-r=s6SOkC$@h{<=<^mbndqueCO&GRjRX66ffMXWT@D;la7DAuAlg9 ztSdSqi^=oeLe);nr{eLW>1KJcs%w|%@p=hklI?Qeo-Hr}yZSP}@!TnK_m%CeV%$0F zijT@B0WoZvf=>d^X6UuEr8%I!%xE}WMyW49r)0L`t>?j3PJ1;bfMA2@_4IVoq{k_J z?!*8(#CDZlFT&D8dG9pqLEJ;brVsx+@KEYO-M{T{f&WWBa1w@SjhReNI0+XU`zDUP zCw|-`v5e7pc+RwiZQk_VM;!RKK}N_5nfqlQxrN(wIJ7gbk8uC)h#xm{i`09R$u4!r zV^DKO=0#N;1@qlapOL&)-DHofa>iO m`&lMc|8%w&Jc zNaDAkos>Gg|6UbtdCmOGzun|$W8!V3gfTdCyKw(boG_Zo2Y@TWRxaDRVNmjeN5=dq zjPJheNVq>I@Ei_CC+M^7daX3&MT~c>)@sHk8Kb;idsR^V$>Sv7CWGlegKhR&PnO_; z_cbR19(roWHn9c=%A $%6h;vL@eLI0gx@6#?_ z`qdi0u7MLc%?aF}@undI@aF8rGyCP|bq)L>ATTBm-S`pSI85&x9gLp@^G1a?6+OU@ zz;3KE|5`zwYGRf@dFqqYeEEVZN22y{HUa-VvunFQ?fzc8YwP+z{gN+NyUHMCG0T5? z=Xb7cYdzUuBZ67})EO^``6tVvPxE`c YSRdZu39v`e(TdRxcAiPVj-jUZU@LkuE>p zGI7@1g-Dklwn;nNb9Mn`s3Z3{^Zps``GdV1K6&t~f)Acf$P@m0n!i<~$q&7z@tKsh zC^qm;z - x#|^6T=srHADw3ibJmHGwxV*m^l{z8ZD&r*i*pwH?9!PkZG>HHK)I zKQ(+$mch6G_xbil$@&e(eh&BkZ?)eH%kS@xZ?+--%l=cb?`O3Zt#pq({cYOK{=xdT z>qq7TZ_`6C@)vJhsW%n28hpKg+XT?UX~Jh5K58-u`-ibLDGToRtyizeVu8yPt^b%Z z;~1-AY|l)
`mrM?yw9ocC@hQSH%Q@65QRDyb5;66u>8^F m`ykhu@E`E`Kt!+qM!ye=@`Z@x^$_4c1Nn!;um%RJfqwyzGmTXM literal 0 HcmV?d00001 diff --git a/frontend/public/index.html b/frontend/public/index.html new file mode 100644 index 0000000..2588bb9 --- /dev/null +++ b/frontend/public/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + Github Quotes for Readme + + + + + + + + \ No newline at end of file diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 0000000..877b1a3 --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,20 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} \ No newline at end of file diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/frontend/src/components/App/App.css b/frontend/src/components/App/App.css new file mode 100644 index 0000000..74b5e05 --- /dev/null +++ b/frontend/src/components/App/App.css @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/frontend/src/components/App/App.js b/frontend/src/components/App/App.js new file mode 100644 index 0000000..8759387 --- /dev/null +++ b/frontend/src/components/App/App.js @@ -0,0 +1,10 @@ +import React from 'react'; +import Dashboard from '../Dashboard'; + +function App(props) { + return ( ++ ); +} + +export default App; diff --git a/frontend/src/components/App/App.test.js b/frontend/src/components/App/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/frontend/src/components/App/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render( ); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/frontend/src/components/Dashboard/index.js b/frontend/src/components/Dashboard/index.js new file mode 100644 index 0000000..32a159d --- /dev/null +++ b/frontend/src/components/Dashboard/index.js @@ -0,0 +1,46 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import useScrollTrigger from '@material-ui/core/useScrollTrigger'; +import Container from '@material-ui/core/Container'; +import { dashConstants } from '../../config/constants'; +import Home from '../Pages/Home'; + +function ElevationScroll(props) { + const { children, window } = props; + // Note that you normally won't need to set the window ref as useScrollTrigger + // will default to window. + // This is only being set here because the demo is in an iframe. + const trigger = useScrollTrigger({ + disableHysteresis: true, + threshold: 0, + target: window ? window() : undefined, + }); + + return React.cloneElement(children, { + elevation: trigger ? 4 : 0, + }); +} + +function Dashboard(props) { + return ( + + + ); +} + +export default Dashboard; diff --git a/frontend/src/components/Pages/Home/index.js b/frontend/src/components/Pages/Home/index.js new file mode 100644 index 0000000..588ec46 --- /dev/null +++ b/frontend/src/components/Pages/Home/index.js @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import TemplateCard from '../../organisms/TemplateCard'; +import { themes, animations, layouts } from '../../../config/cardTemplate'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +const Home = () => { + + const [theme, setTheme] = useState(themes[0]); + const [animation, setAnimation] = useState(animations[0]); + const [layout, setLayout] = useState(layouts[0]); + + return ( ++ + ++ ++ +{dashConstants.APP_NAME} ++ + ++ + + ) +} + +export default Home; \ No newline at end of file diff --git a/frontend/src/components/organisms/TemplateCard/index.js b/frontend/src/components/organisms/TemplateCard/index.js new file mode 100644 index 0000000..2e03b25 --- /dev/null +++ b/frontend/src/components/organisms/TemplateCard/index.js @@ -0,0 +1,99 @@ +import React, { useState, useEffect } from 'react'; +import { + Paper, + TextField, + Grid, + Button, + Snackbar, + Slide, + CircularProgress +} from '@material-ui/core'; +import getTemplate from '../../../util/template/getTemplate'; +import Template from '../../../util/template/Template'; +import mainLayouts from '../../../util/layouts'; +import mainAnimations from '../../../util/animation'; +import mainThemes from '../../../util/themes'; + +const TemplateCard = (props) => { + + const [showSnackbar, setShowSnackbar] = useState(false); + const [snackbarMessage, setSnackbarMessage] = useState(''); + const [isImageLoaded, setImageLoaded] = useState(false); + + const template = new Template(); + const data = { + quote: 'Github quotes for README', + author: 'Open Source' + } + template.setTheme(mainThemes[props.theme]); + template.setData(data); + template.setAnimation(mainAnimations[props.animation]) + template.setLayout(mainLayouts[props.layout]); + + const file = new Blob([getTemplate(template)], {type: 'image/svg+xml'}); + const url = URL.createObjectURL(file); + + const copyToClipboard = () => { + navigator.clipboard.writeText('![Quote](' + quoteUrl + ')').then(() => { + setSnackbarMessage('Copied to Clipboard!'); + setShowSnackbar(true); + }).catch(() => { + setSnackbarMessage('Unable to copy'); + setShowSnackbar(true); + }) + } + + const handleSnackbarClose = () => { + setShowSnackbar(false); + } + + const quoteUrl = `https://github-readme-quotes.herokuapp.com/quote?theme=${props.theme}&animation=${props.animation}&layout=${props.layout}`; + + function SlideTransition(prop) { + returnMake your own personalised style for the Quotes + ++ + + ++ +{ + if (newValue != null) + setTheme(newValue) + }} + renderInput={(params) => } + /> + + +{ + if (newValue != null) + setLayout(newValue) + }} + style={{ width: 300 }} + renderInput={(params) => } + /> + + + +{ + if (newValue != null) + setAnimation(newValue) + }} + style={{ width: 300 }} + renderInput={(params) => } + /> + + + + ++ ++ + + { + layouts.filter((item) => item !== layout).map((restLayout) => { + return ( +Other layouts + {console.log("All Layouts ", layouts)} ++ + ) + }) + } ++ ; + } + + useEffect(() => { + setImageLoaded(false); + }, [quoteUrl]) + + return ( + + + ) +} + +export default TemplateCard; \ No newline at end of file diff --git a/frontend/src/config/cardTemplate/index.js b/frontend/src/config/cardTemplate/index.js new file mode 100644 index 0000000..47ef625 --- /dev/null +++ b/frontend/src/config/cardTemplate/index.js @@ -0,0 +1,46 @@ +export const themes = ['default', + 'default_repocard', + 'dark', + 'radical', + 'merko', + 'gruvbox', + 'tokyonight', + 'onedark', + 'cobalt', + 'synthwave', + 'highcontrast', + 'dracula', + 'prussian', + 'monokai', + 'vue', + 'vue-dark', + 'shades-of-purple', + 'nightowl', + 'buefy', + 'blue-green', + 'algolia', + 'great-gatsby', + 'darcula', + 'bear', + 'solarized-dark', + 'solarized-light', + 'chartreuse-dark', + 'nord', + 'gotham', + 'material-palenight', + 'graywhite', + 'vision-friendly-dark', + 'ayu-mirage', + 'midnight-purple', + 'calm', + 'flag-india', + 'omni', + 'react', + 'jolly', + 'maroongold', + 'yeblu', + 'blueberry', + 'slateorange', + 'kacho_ga']; +export const animations = ['default', 'grow_out_in']; +export const layouts = ['default', 'samuel', 'churchill', 'socrates', 'zues']; \ No newline at end of file diff --git a/frontend/src/config/constants/index.js b/frontend/src/config/constants/index.js new file mode 100644 index 0000000..4588438 --- /dev/null +++ b/frontend/src/config/constants/index.js @@ -0,0 +1,3 @@ +export const dashConstants = { + APP_NAME : 'Github Dynamic Quotes for Readme' +}; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/frontend/src/index.js b/frontend/src/index.js new file mode 100644 index 0000000..651819c --- /dev/null +++ b/frontend/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './components/App/App'; +import reportWebVitals from './reportWebVitals'; + +ReactDOM.render( ++ setImageLoaded(true)} style={{ width: '100%', display: isImageLoaded ? '' : 'none' }} /> +++ + + ++ ++ + + ++ + , + document.getElementById('root') +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/frontend/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/reportWebVitals.js b/frontend/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/frontend/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/frontend/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/frontend/src/util/animation/index.js b/frontend/src/util/animation/index.js new file mode 100644 index 0000000..7339011 --- /dev/null +++ b/frontend/src/util/animation/index.js @@ -0,0 +1,22 @@ +const animations = { + default:{ + animation:"", + keyframes:"" + }, + + grow_out_in: { + animation: `animation:grow-out-in 2s linear infinite alternate;`, + keyframes: `@keyframes grow-out-in{ + 0% { + box-shadow:0 2px 4px -2px rgba(0,0,0,.25); + transform:scale(.95); + } + 100% { + box-shadow:0 0 4px 2px rgba(0,0,0,.25); + transform:scale(1); + } + }`, + }, +}; + +export default animations; diff --git a/frontend/src/util/layouts/index.js b/frontend/src/util/layouts/index.js new file mode 100644 index 0000000..69cd04d --- /dev/null +++ b/frontend/src/util/layouts/index.js @@ -0,0 +1,364 @@ +const layouts = { + default: { + style: (template) => { + return ` * { + padding: 0; + margin: 0; + box-sizing: border-box; + } + .container { + font-family: Arial, Helvetica, sans-serif; + padding: 40px 20px; + width: 600px; + background-color: #${template.theme.bg_color}; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 5px; + ${template.animation.animation}; + } + ${template.animation.keyframes} + .container h3 { + font-size: 19px; + margin-bottom: 5px; + font-weight: 500; + font-style: oblique; + color: #${template.theme.quote_color}; + } + .container h3::before { + content: open-quote; + font-size: 25px; + } + .container h3::after { + content: close-quote; + vertical-align: sub; + font-size: 25px; + } + .container p { + /* float: right; */ + /* margin-right: 20px; */ + font-style: italic; + padding: 5px; + text-align: right; + color: #${template.theme.author_color}; + }`; + }, + structure: (template) => { + return `+ +`; + }, + }, + socrates: { + style: (template) => { + return `.square-brackets-quote { + display:inline-block; + font-family:Arial,Helvetica,sans-serif; + margin:1em; + width:600px; + ${template.animation.animation}; + } + ${template.animation.keyframes} + .square-brackets-quote blockquote { + border:solid 1em #${template.theme.bg_color === "fffefe" + ? "ccc" + : template.theme.bg_color + }; + display:inline-block; + margin:0; + padding:1em; + position:relative; + font-size:15px; + + } + .square-brackets-quote blockquote::before { + background-color: #fff; + bottom: -1em; + content: ""; + left: 2em; + position: absolute; + right: 2em; + top: -1em; + } + .square-brackets-quote cite { + color:#757575; + display: block; + font-size:small; + font-style: normal; + text-align: right; + text-transform:uppercase; + } + * { + position: relative; + z-index: 1; + } + + + `; + }, + structure: (template) => { + return `${template.quote}
+- ${template.author === "Unknown" ? "Anonymous" : template.author + }
++`; + }, + }, + churchill: { + style: (template) => { + return `#ct{ + height:auto; + width:600px; + border:1px solid #f1c40f; + margin: 20px 50px 20px 10px; + text-align:center; + position:relative; + color:#${template.theme.quote_color}; + padding:15px; + background:#${template.theme.bg_color}; + ${template.animation.animation}; + } + ${template.animation.keyframes} + span{ + background:#${template.theme.bg_color}; + color:#${template.theme.author_color}; + padding:0 10px; + font-size:20px; + position:relative; + top:-28px; + } + .corner{ + height:30px; + width:30px; + border-radius:50%; + border:1px solid #fff; + transform:rotate(-45deg); + position:absolute; + background:#fff; + } + + #left_top{ + top:-16px; + left:-16px; + border-color:transparent transparent #f1c40f transparent; + } + #right_top{ + top:-16px; + right:-16px; + border-color:transparent transparent transparent #f1c40f; + } + #left_bottom{ + bottom:-16px; + left:-16px; + border-color:transparent #f1c40f transparent transparent ; + } + #right_bottom{ + bottom:-16px; + right:-16px; + border-color:#f1c40f transparent transparent transparent; + } + p{ + padding-top:0px; + font-size:17px + }`; + }, + structure: (template) => { + return `++${template.quote}
+ ${template.author === "Unknown" + ? "Anonymous" + : template.author + } ++ + + + + ${template.author === "Unknown" + ? "Anonymous" + : template.author + } +`; + }, + }, + samuel: { + style: (template) => { + return `.quote { + display: inline-block; + margin: 1em; + width:600px; + ${template.animation.animation}; + } + ${template.animation.keyframes} + blockquote { + border: solid 6px #${template.theme.bg_color === "fffefe" + ? "757575" + : template.theme.bg_color + }; + display: inline-block; + margin: 0; + font-size:16px; + padding: 1em; + position: relative; + } + blockquote::before { + background-color: #fff; + bottom: -10%; + content: ""; + left: 0; + position: absolute; + right: 0; + top: -10%; + transform: rotate(-15deg) skew(5deg); + } + cite { + display: block; + font-style: italic; + text-align: right; + } + cite::before { + content: "- "; + } + * { + position: relative; + z-index: 1; + } + + `; + }, + structure: (template) => { + return `++${template.quote}
++`; + }, + }, + zues: { + style: (template) => { + return ` + + .container{ + background-color:#000; + width:600px; + height:auto; + padding:30px 20px 40px 40px; + ${template.animation.animation}; + } + ${template.animation.keyframes} + + .quote4{ + background-color:#000; + color:#fff; + width:450px; + text-align:justify; + border-left: thick double #C08552; + border-right: thick double #C08552; + padding:40px 10px; + position:relative; + transform: skew(-.312rad); + height:auto; + } + + .quote4::before, .quote4::after{ + position:absolute; + font-size:105px; + font-family: 'Dosis', sans-serif; + background:#000; + display:block; + height:30px; + width:45px; + text-align:center; + color:#DAB49D; + left:0; + right:0; + margin:auto; + z-index:100; + } + + .quote4::before{ + content:"“"; + top:-10px; + line-height:80px; + z-index:1; + } + + .quote4::after{ + content:"”"; + bottom:-25px; + line-height: 70px; + } + + .quote4 .first, .quote4 .txt{ + width:90%; + margin:auto; + transform: skew(.312rad); + } + .quote4 .first{ + margin-top:10px; + width:100%; + color: #DAB49D; + font-size:14px; + font-family: 'Dosis', sans-serif; + text-transform:uppercase; + letter-spacing:1px; + } + .quote4 .txt{ + color:#F3E9DC; + font-size:16px; + font-family: 'Roboto Slab', serif; + + } + + .quote4 .from{ + text-align:center; + margin-top:15px; + font-size:13px; + font-family: 'Exo', sans-serif; + color: #5E3023; + } + + .quote4 .border::before, .quote4 .border::after{ + content:""; + width:280px; + height:3px; + position:absolute; + display:block; + left:0; + right:0; + margin:auto; + } + + .quote4 .border::after{ + border-bottom: 2px solid #C08552; + bottom: 0px; + } + + .quote4 .border::before{ + border-top: 2px solid #C08552; + top:0px; + }`; + }, + structure: (template) => { + return ` +++${template.quote}
+ ${template.author === "Unknown" + ? "Anonymous" + : template.author + } +++ `; + }, + }, +}; + +export default layouts; diff --git a/frontend/src/util/template/Template.js b/frontend/src/util/template/Template.js new file mode 100644 index 0000000..ff49e21 --- /dev/null +++ b/frontend/src/util/template/Template.js @@ -0,0 +1,44 @@ +import layouts from '../layouts'; + +class Template { + + setTheme(theme) { + this.theme = theme; + } + + setData(data) { + this.quote = data.quote; + this.author = data.author; + } + + setAnimation(animation) { + this.animation = animation; + } + + setLayout(layout) { + this.layout = layout; + this.setStyle(layout.style); + this.setStructure(layout.structure); + this.calculateHeight(this.quote.length); + } + setStyle(style) { + this.css = style(this); + } + + setStructure(structure) { + this.structure = structure(this); + } + + calculateHeight(length) { + let lines; + if (this.layout !== layouts["zues"]) { + lines = Math.floor(length / 64); + this.height = lines > 2 ? (lines - 2) * 22 + 173 : 173; + } else { + lines = Math.floor(length / 62); + this.height = lines * 18 + 198; + } + } +} + +export default Template; diff --git a/frontend/src/util/template/getTemplate.js b/frontend/src/util/template/getTemplate.js new file mode 100644 index 0000000..77a176b --- /dev/null +++ b/frontend/src/util/template/getTemplate.js @@ -0,0 +1,17 @@ +const getTemplate = (template) => { + return ` + `; + }; + +export default getTemplate; \ No newline at end of file diff --git a/frontend/src/util/themes/index.js b/frontend/src/util/themes/index.js new file mode 100644 index 0000000..cc19edb --- /dev/null +++ b/frontend/src/util/themes/index.js @@ -0,0 +1,225 @@ +const themes = { + "default": { + quote_color: "2f80ed", + author_color: "333", + bg_color: "fffefe", + }, + "default_repocard": { + quote_color: "2f80ed", + author_color: "333", + bg_color: "fffefe", + }, + "dark": { + quote_color: "fff", + author_color: "9f9f9f", + bg_color: "151515", + }, + "radical": { + quote_color: "fe428e", + author_color: "a9fef7", + bg_color: "141321", + }, + "merko": { + quote_color: "abd200", + author_color: "68b587", + bg_color: "0a0f0b", + }, + "gruvbox": { + quote_color: "fabd2f", + author_color: "8ec07c", + bg_color: "282828", + }, + "tokyonight": { + quote_color: "70a5fd", + author_color: "38bdae", + bg_color: "1a1b27", + }, + "onedark": { + quote_color: "e4bf7a", + author_color: "df6d74", + bg_color: "282c34", + }, + "cobalt": { + quote_color: "e683d9", + author_color: "75eeb2", + bg_color: "193549", + }, + "synthwave": { + quote_color: "e2e9ec", + author_color: "e5289e", + bg_color: "2b213a", + }, + "highcontrast": { + quote_color: "e7f216", + author_color: "fff", + bg_color: "000", + }, + "dracula": { + quote_color: "ff6e96", + author_color: "f8f8f2", + bg_color: "282a36", + }, + "prussian": { + quote_color: "bddfff", + author_color: "6e93b5", + bg_color: "172f45", + }, + "monokai": { + quote_color: "eb1f6a", + author_color: "f1f1eb", + bg_color: "272822", + }, + "vue": { + quote_color: "41b883", + author_color: "273849", + bg_color: "fffefe", + }, + "vue-dark": { + quote_color: "41b883", + author_color: "fffefe", + bg_color: "273849", + }, + "shades-of-purple": { + quote_color: "fad000", + author_color: "a599e9", + bg_color: "2d2b55", + }, + "nightowl": { + quote_color: "c792ea", + author_color: "7fdbca", + bg_color: "011627", + }, + "buefy": { + quote_color: "7957d5", + author_color: "363636", + bg_color: "ffffff", + }, + "blue-green": { + quote_color: "2f97c1", + author_color: "0cf574", + bg_color: "040f0f", + }, + "algolia": { + quote_color: "00AEFF", + author_color: "FFFFFF", + bg_color: "050F2C", + }, + "great-gatsby": { + quote_color: "ffa726", + author_color: "ffd95b", + bg_color: "000000", + }, + "darcula": { + quote_color: "BA5F17", + author_color: "BEBEBE", + bg_color: "242424", + }, + "bear": { + quote_color: "e03c8a", + author_color: "bcb28d", + bg_color: "1f2023", + }, + "solarized-dark": { + quote_color: "268bd2", + author_color: "859900", + bg_color: "002b36", + }, + "solarized-light": { + quote_color: "268bd2", + author_color: "859900", + bg_color: "fdf6e3", + }, + "chartreuse-dark": { + quote_color: "7fff00", + author_color: "fff", + bg_color: "000", + }, + "nord": { + quote_color: "81a1c1", + author_color: "d8dee9", + bg_color: "2e3440", + }, + "gotham": { + quote_color: "2aa889", + author_color: "99d1ce", + bg_color: "0c1014", + }, + "material-palenight": { + quote_color: "c792ea", + author_color: "a6accd", + bg_color: "292d3e", + }, + "graywhite": { + quote_color: "24292e", + author_color: "24292e", + bg_color: "ffffff", + }, + "vision-friendly-dark": { + quote_color: "ffb000", + author_color: "ffffff", + bg_color: "000000", + }, + "ayu-mirage": { + quote_color: "f4cd7c", + author_color: "c7c8c2", + bg_color: "1f2430", + }, + "midnight-purple": { + quote_color: "9745f5", + author_color: "ffffff", + bg_color: "000000", + }, + "calm": { + quote_color: "e07a5f", + author_color: "ebcfb2", + bg_color: "373f51", + }, + "flag-india": { + quote_color: "ff8f1c", + author_color: "509E2F", + bg_color: "ffffff", + }, + "omni": { + quote_color: "FF79C6", + author_color: "E1E1E6", + bg_color: "191622", + }, + "react": { + quote_color: "61dafb", + author_color: "ffffff", + bg_color: "20232a", + }, + "jolly": { + quote_color: "ff64da", + author_color: "ffffff", + bg_color: "291B3E", + }, + "maroongold": { + quote_color: "F7EF8A", + author_color: "E0AA3E", + bg_color: "260000", + }, + "yeblu": { + quote_color: "ffff00", + author_color: "ffffff", + bg_color: "002046", + }, + "blueberry": { + quote_color: "82aaff", + author_color: "27e8a7", + bg_color: "242938" + }, + "slateorange": { + quote_color: "faa627", + author_color: "ffffff", + bg_color: "36393f" + }, + "kacho_ga": { + quote_color: "bf4a3f", + author_color: "d9c8a9", + bg_color: "402b23" + } + }; + + + module.exports = themes; \ No newline at end of file From 0f40941860b56012cead64dab711a3f275b720b8 Mon Sep 17 00:00:00 2001 From: nishantpersonal+ ++${template.quote}+${template.author === "Unknown" + ? "Anonymous" + : template.author + }+Date: Mon, 7 Dec 2020 18:22:27 +0530 Subject: [PATCH 3/6] Width refactor in zues layout and change in static quote in frontend --- .../organisms/TemplateCard/index.js | 179 +++++++++--------- src/layouts/layout.js | 2 +- 2 files changed, 94 insertions(+), 87 deletions(-) diff --git a/frontend/src/components/organisms/TemplateCard/index.js b/frontend/src/components/organisms/TemplateCard/index.js index 2e03b25..3d4c6e4 100644 --- a/frontend/src/components/organisms/TemplateCard/index.js +++ b/frontend/src/components/organisms/TemplateCard/index.js @@ -1,99 +1,106 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect } from "react"; import { - Paper, - TextField, - Grid, - Button, - Snackbar, - Slide, - CircularProgress -} from '@material-ui/core'; -import getTemplate from '../../../util/template/getTemplate'; -import Template from '../../../util/template/Template'; -import mainLayouts from '../../../util/layouts'; -import mainAnimations from '../../../util/animation'; -import mainThemes from '../../../util/themes'; + Paper, + TextField, + Grid, + Button, + Snackbar, + Slide, + CircularProgress, +} from "@material-ui/core"; +import getTemplate from "../../../util/template/getTemplate"; +import Template from "../../../util/template/Template"; +import mainLayouts from "../../../util/layouts"; +import mainAnimations from "../../../util/animation"; +import mainThemes from "../../../util/themes"; const TemplateCard = (props) => { + const [showSnackbar, setShowSnackbar] = useState(false); + const [snackbarMessage, setSnackbarMessage] = useState(""); + const [isImageLoaded, setImageLoaded] = useState(false); - const [showSnackbar, setShowSnackbar] = useState(false); - const [snackbarMessage, setSnackbarMessage] = useState(''); - const [isImageLoaded, setImageLoaded] = useState(false); + const template = new Template(); + const data = { + quote: "This is going to be the Github quote for your README", + author: "Open Source", + }; + template.setTheme(mainThemes[props.theme]); + template.setData(data); + template.setAnimation(mainAnimations[props.animation]); + template.setLayout(mainLayouts[props.layout]); - const template = new Template(); - const data = { - quote: 'Github quotes for README', - author: 'Open Source' - } - template.setTheme(mainThemes[props.theme]); - template.setData(data); - template.setAnimation(mainAnimations[props.animation]) - template.setLayout(mainLayouts[props.layout]); + const file = new Blob([getTemplate(template)], { type: "image/svg+xml" }); + const url = URL.createObjectURL(file); - const file = new Blob([getTemplate(template)], {type: 'image/svg+xml'}); - const url = URL.createObjectURL(file); + const copyToClipboard = () => { + navigator.clipboard + .writeText("![Quote](" + quoteUrl + ")") + .then(() => { + setSnackbarMessage("Copied to Clipboard!"); + setShowSnackbar(true); + }) + .catch(() => { + setSnackbarMessage("Unable to copy"); + setShowSnackbar(true); + }); + }; - const copyToClipboard = () => { - navigator.clipboard.writeText('![Quote](' + quoteUrl + ')').then(() => { - setSnackbarMessage('Copied to Clipboard!'); - setShowSnackbar(true); - }).catch(() => { - setSnackbarMessage('Unable to copy'); - setShowSnackbar(true); - }) - } + const handleSnackbarClose = () => { + setShowSnackbar(false); + }; - const handleSnackbarClose = () => { - setShowSnackbar(false); - } + const quoteUrl = `https://github-readme-quotes.herokuapp.com/quote?theme=${props.theme}&animation=${props.animation}&layout=${props.layout}`; - const quoteUrl = `https://github-readme-quotes.herokuapp.com/quote?theme=${props.theme}&animation=${props.animation}&layout=${props.layout}`; + function SlideTransition(prop) { + return ; + } - function SlideTransition(prop) { - return ; - } + useEffect(() => { + setImageLoaded(false); + }, [quoteUrl]); - useEffect(() => { - setImageLoaded(false); - }, [quoteUrl]) - - return ( - - + ); +}; -export default TemplateCard; \ No newline at end of file +export default TemplateCard; diff --git a/src/layouts/layout.js b/src/layouts/layout.js index 9afa4c3..4dbe53b 100644 --- a/src/layouts/layout.js +++ b/src/layouts/layout.js @@ -251,7 +251,7 @@ const layouts = { .container{ background-color:#000; - width:600px; + width:550px; height:auto; padding:30px 20px 40px 40px; ${template.animation.animation}; From 364359ee660cb3dd02b9fb2b1dbe52b7cae7b73d Mon Sep 17 00:00:00 2001 From: S K B <47430686+shravan20@users.noreply.github.com> Date: Mon, 7 Dec 2020 19:07:18 +0530 Subject: [PATCH 4/6] Release/docs/v1.2 (#101) * [ Major Docs Updated ] | Documentation | updated ./README.md * [ Major Docs Updated ] | Documentation | updated ./README.md --- README.md | 8 +++++++- assets/UIScreen.png | Bin 0 -> 57119 bytes assets/uiScreen.gif | Bin 0 -> 9885014 bytes frontend/src/util/layouts/index.js | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 assets/UIScreen.png create mode 100644 assets/uiScreen.gif diff --git a/README.md b/README.md index 065c04a..1153b0b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Dynamic quote generator for your GitHub readmes ![banner](./assets/README.png) -![alt](https://img.shields.io/apm/l/vim-mode?label=license&logo=dark-green) ![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-success) ![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github) [![GitHub contributors](https://img.shields.io/github/contributors/shravan20/github-readme-quotes.svg)](https://github.com/shravan20/github-readme-quotes/graphs/contributors) ![Pull Request Counts](https://img.shields.io/bitbucket/pr/shravan20/github-readme-quotes) ![last commit](https://img.shields.io/github/last-commit/shravan20/github-readme-quotes) ![deployment-status](https://img.shields.io/website?url=https%3A%2F%2Fgithub-readme-quotes.herokuapp.com%2Fquote) +![alt](https://img.shields.io/apm/l/vim-mode?label=license&logo=dark-green) ![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-success) ![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github) [![GitHub contributors](https://img.shields.io/github/contributors/shravan20/github-readme-quotes.svg)](https://github.com/shravan20/github-readme-quotes/graphs/contributors) ![Pull Request Counts](https://img.shields.io/bitbucket/pr/shravan20/github-readme-quotes) ![last commit](https://img.shields.io/github/last-commit/shravan20/github-readme-quotes) ![deployment-status](https://img.shields.io/website?url=https%3A%2F%2Fgithub-readme-quotes.herokuapp.com%2Fquote) [![Build Status](https://travis-ci.org/joemccann/dillinger.svg?branch=master)](https://github.com/shravan20/github-readme-quotes/) @@ -13,6 +13,12 @@ Dynamic quote generator for your GitHub readmes- setImageLoaded(true)} style={{ width: '100%', display: isImageLoaded ? '' : 'none' }} /> --- - + return ( +- -- - - -+ - ) -} ++ setImageLoaded(true)} + style={{ width: "100%", display: isImageLoaded ? "" : "none" }} + /> +++ + -+ ++ + + +- +
+### Use [User Interface](http://github-readme-quotes.herokuapp.com/), to view designed quotes and copy link and paste it directly + + + +--- + ## Features | Sl No | Feature Name | Description | diff --git a/assets/UIScreen.png b/assets/UIScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..1e30a0bffe9b4df208f22b0d620eaa782ee5309b GIT binary patch literal 57119 zcmeFZWmJ`2+cvsrq(MqRQbCb!k(Lq^knWW3?pA5A5D*Xn=@u#JRzLyikd*F{hVNYW zeLv6pd}HiA#(sb7AA5iA`k{+;UDsT5p7V_3IL-+XDoS$rI8-D=p5bf4Y-tV}e?iJT!G&9FtR zWkuH@5IES@Fe&Xv4~5Mf?Kw=IIGUPsc-cF_Y!L_%aW5wmGh1^vdQ)>tYX?!rt=dLL zdg~{mj9UDUxE?v(H@C8u_i-^-^HEYa^RYF%`-D+k3`fLE7zVI6cQc{)vbS?^74{Nk z{A*lcc#r&;lac ~Te~%$; z?rP>@?c`?d=s=Gg)5O%#-A$B{5w6q!GdOYs{yn;b>pw;Sb8sTBaB_2SasKD+Zq^q6 zH@73N{MYTshlN$Fz0B>jrLFDF9b91wqKw?UT!Md%_FtEE{^PO$m%v|FkwXbTus5+Z z*Rp=%X7wM>{nr(Fb8Aa0Sj+$RjIxWhIamR?UU-O+6KPA%e~bxx!};&cL0ahl{n!6| z$$!hn|6{KIG1q^~0{^X>|Hr%j$6Ws{3;efk{vYr9|CYIM{tMojI{>Eh1n_3PHpdFU z58YMjfjTz4{IH*dBM|h62hx)2UgPV(Jl)iX#xb`ofBJ5re0xZL1w|?dA(i_xOY!v- znRI?6z05vpS~xu;Ha3pDn$K4KO*ax7dHG-opDo^`l&v^tf@?es_D#M!$3^ZV_?Ajz zzG?nSDS}&5dOt^IauJOGdS$R4v1eV|AH|dY*XtuLo&X8)Kd+ZcT%+>;x*{#yY|Qkp zE5s}4p)voy^6~2b!4ExcPOeLQmdfDcw;0^ rMRl@ni%ZHPC;EYpa zm8P}fQ1&p7cCHaaQbIElHO}$LT+j1Sp0&fqLdL_gj1-ZF{kC67FH&iY=S`#6)iG%G z Oro?)06xaV*Ynbdr`H(9w;gO%8mZpD5 zs&&YGk!fGP?tokEn{2i TmBuC-1=&JzKQ7f(yi%y0?l#%L5$1TKSzFYXq3q-Q4@9s(fxI4xbF_!JD&Hp zI2|n|3Tlotp>Q}I3Urp2(&(SuxuCP%yREmH3B&D7GcTT8YU}Lp^PL_yr)8Aw>~S@C zNBlkcDOJZCqq&as&7bWrN<9T7HS rr@VmeP4w0(JBY6;o;Bos%Cs z(ybh^l#P`f0W_q$n(n1PH_bHe)|rJ^2cEQ!KBRM=y*qj~#3WMl8Y9f}VZUc3%j)M( zt>HJPV3Tn@m2w(he!=|XDKk&2R9z{)Lx(G%dzUIAxI=VW&U7h|HdDoLzjxmmfhp^g zt2sf_nsC%qkca=}(VwPgMb6=MN#CkC|BlCgz2%smvC+>}yf#NVsE7bs+Q>b J#9CrUS;!G!=$Pk*Bh2p|PnmBSnRlNB~`fOfgQuKA?GXuw&Z@<{&Z;;@n zY#o&Ne7K(9_?4#N$dcywvCJ*jldu!70Ad$j+?{0w%(nqYjNzL`Y-_Ln@vA8g#~bBk z4>(y6P32)@)$V*XZA%oDqG3e^+=!;GewCEkorG{rr$UoR@kWBq;%6_9L>t)IHe%Z5 z$&G9Y5R|iC3iE!l7AKTnzT&cDeQ9n}rBmKAH#-$1#=iTKaZ_!4(q1&%+jyDL@gobV zv0X*^%`PgTCJcWIpO&n**M839I9bf|t{-!dJi|% %4lXy>3KO F?iA6z*y@$`n4+<*tIFKjSUeJ`d-D=X+FhW zFRp$Q`m}rHq-R$;#)tfCH(OhK<6N2hFjKX6lG(Ntr o|&4oan;1*K&}Xqs#1%T6yDUnUja zsxTNlm$3i#U1I1m(2KgcmZZyoPJ)am#l4gmU9zdpV}#2ii!;UC;;3? no*8||B6NPr`#m8(9o=58I _`9t7=+(b8 z&MZvlN6hq-ZQy!*?Cjp@-2b%X97XUgBqoYG|5O)&ct6$qTtxYbc{;)$Euwll*Sx~S z`jGL%?9d?QfTv*7=aYmNL`hmJKGlOft#7^`v !$x7QfFlG5@Mg%LALpwp$uC)NamCr5Pn3zYLoB^=^;OBY8EQn c!$7>=G}Zl)>Uw|GSZ<*%uI0B=#?`PT ?YL$WeTnNs;o-o1>#G{$ZDu+pTIOZpZ)lHv z@0t0`(?7|wA`}v
q1xG9t@wk&9IS_@-E=Xv1s6^)PekZC z7NH(?(7I2I&es|WI?t=T@|v1e^xMA3;Wyk*8YfRY3!}p!%gXUq8vl*ux<;`TqZ?Di z?HYI=nSNxq?nO=%jFdHn-xT|*pfPi{xI|Wr`%k)`SdYQWtaMTL5AJ=KKYkWD?eQgc zq1EU~Dmxu* c)MeM?Ov8h%k&b-G26M*NQ=f~L_g*X}D5RyT+fW7OpPV7R%G9NLb`@S`70)u?}D zv8v1gC)SMq5*obN$z8K4b3;1s6X^t=>(#>*fh 2vrTEQcg>eD4E+%se&u`1Q{5%*uXE@ZXS(qWtBd9w zn2!{Bx5X=Fbh~ME@^E^r{V~wKE6o%%89%u}-lSv0{G&TBCMK%v$aS^cujurnnD5IC zD?Pi*$YsIKtNdt%482;W--0G!`KVWIQsoAV9s;;9`zb3 K0@U?*N~f{lKgsfcZdlg zvBhcb^fn0 z jh z5E_y1>ZxU}ag9=8X2g)puCA{BH|h<`;m*0q?4?_8aJ>xeWS)$?<6Nz`HMt>M`@ZAj zIK@lKqOQ8`hnh1#tcSP;SKZkZQVz#j?bdZjF}wqaRnsV}3b*T^4Be1jl6 }Ggywc|Z#2QjZ)Yfn3}c*k=*_bb zZSr}A!VU=J&(sF^2q!CVybw 9<~#EdvqhN?gep&N z<1C nu{8|SOntXYe?ei={`KBm>EYtJfw4(J7rMc{ zSuvNNxD35_TBh0`LlMUXlTx{Bh-w}F$VP5^w98#~i(qnAbo8wUN41SftxYOqxBRQ( zaBq-Y`F*UwMi+pU^bO~?1$m@@1z({5)nH3PSCw2MqR+f2-03=j(^XGW3@`B_Ll{K@ z-$da~%(dC(YkcJRhvhkz3-UzkzR!7@UPg--6V)zovoA65@guV|1tVo%M0{%YyUX4_ z>5{G`vd9=bO;Xa?{&+mV`Uuahd<>qLhemkyZ v60%tPk^72cMf7_+?Q%UUd`SkR&2OPb _!OImUO$75P~%MkcFdaQQv>QVr}DeS>yM ~EG%IZDfqka;<)e9 zCQh8-FcvpzKhqUW(d(56aa0l<9v$g=eBj`;!$+3Jf8R_LFd@pN5&@uF99y~Xhhf2G zv^Zo6>W3PlQL|b+d|e#aSy(w5=lbRTL#;!u1N?xB!?Fs}#v+HG{SBX&?f=Mz#9WJT zNt~c;89r$D3ppNB*yk0&l!$*^A)J3;%NZt}aq60nMx6at!&ZTXP=lUPv_V2&^k}&M z#_x}Jj2+CvF;lO%eb89W8&qA|xKl92f5x`6k;KeZ&Lbi9goDE|?!L!6)PM~ddRwal zSz&{nnZ@p42}y}0$%_4_6!$-FOg _v+xwG#m87S^2Z$x~qMm;-tgsyZ> zu9qh;V(#DdDxR@ZD9$zCvi8fB06hGs4Z*alo^E~E0{Ii|gDr@PccWI03613$k9rQc zFa6ZiOU!~(@Gu-`9!lx@q3GMbOMjqj8DlosYq}G#`{jRgU=lk&KT$LX!^PZzadICY zqEKr`H1`*?3*Jn3x=K=QMxAyZHcHjl5GQ(M<}-Xqeocv2S0`Hhb7Vzu;;MxvCZs*I zay>(0t$qO=hwYA#n0B;iW^%Hvkg`KpVENd*chG<~X)&r1BK%N|damh=w@8<~O5S0l z*YYc4;CHXo_5O76yGEhu2NBj=nfW-*E<@~<#n~H^A}LR88WwX$hH2R`zqA0y>nJKl z%gVsEy!5(=8_p5=eViXA!XZLIQPrM)pyKJ4s*V=+wllmpQusdqt_5?09o2r 7DS(W-blJ^CCnOp@7&wMu}?g?IXo~l;3 z>ZZngpt6v_TeFX^s2s>AFcM~1M$XGOeE)k#zFq5R*jPM=;so*L+?iueU|{ynSXQHY zaC?VnDzY|5hGg)BZ8SvsD0TQQskTdGe&KNJ+AF2u7MdT>=fy3wcC_pfB@fzZ9KL;U z#F#~osXtA dnq$4wnmpzGF|9 zzN4P+6Z$>UF?Gj`YRb3F*FD|BZPJ1qlx~t6mwE7nJT5cYY$f+K0|E%ygyByBL2RL4 zhz@)4%%;vz$~%-_cLcYL%vUq-_)^G85}|94Q>PD9dq!47u`*RWPm_tbD|WDdv-Hl} z)cnn=-c}br&2oXCJp58U*N4h)*&An-DTr?Sd`iBfOYt|j&!`%sj^O$w{OU%rv51R~ z{eI#iik_BPV&YnBX2VTP&V7 f;?h6^^Mu;sDWvH zG8$M26|fMZaz4Rczn2dZlONwAD6$u?6lUANH!&@ai>1?$tM_<^`;+uGhVxgKs@t^K z%h|d@?LWR#rWKc9MM#GXE*(?p#IH82EZP%5o_Ds|-!alyaxSudt~@dlT2!zzeLjL? zMCTaY>nT7K`l5l|zTe#xAMc$JE;D()uY-j-!d7mGd-WLgPCm~;fBY!c-MPJfe;fQV z0}ea?*s~O=W$NYGa`E$a85f%TWaCv$@v|j;H6e8^{CmomwbKo1BP38m>0cs%zRa{b z+D42#_WboBb>|i VE{aB>(Zcd>j5<$kueF7{^SwD7y9WSN_9Mh(@` z^{1}I6$fHTa&Cne{H!0nd&zA>SMjX2>h7>~Rj}%92_qv$i-jbe#-#KdJ A z`5))RUk`{2RGx33Zv344J=T#f(}CI-B4@T+6dy1%yW5SI@+vQK+Im~Jtk{Hhw_T{l zWAZUQ9bN6@ZNk(VJ53Kd51sa$NC+-SV;o+K5{*ui1)m!LT4~ ozAjD!BHU(+%f){aAa!zOm_G`h5* z`{;KyV;iErC7-E4?G>N2DjwtL_ntdT?Hm!2>5MCA1y;D%E6CpOsNp|C 60V_h-+75Bmq?}X|YPU9NMuqY`Nk-uHt?CB1hUs z3MID4J0vDel UC3*4kM(1~?2in5h z0^}ZzI;(YYf`oaWzT@n^sSYT$NwNeAZz?%Ljg6}$XXwH3N$oeF$t%Ok8bcHPt)JV7 z=0_NKAH (T! 5M+Shc4EEhr`}%;Z~GmGhP#D?q4p zby9{^P3C~)GsN^^!|mdyXWK5q76OE_eZ^j+zsZ{)SUlhNx~o(Y*PiewWc#<$LfD$t zXR$A&-n>^CSYDwZM8)=q(7HkE;VNh+L*Iy$lYK=)6a;fz*CTnxnXE6Xo05+|aP@cM zz#?n$hO)H9Cz@T6o5gew+d_&3+ic~jwIjtH-_bsgNfQS-QXlDF4Z-NPER&rF=Mg1E zu?t22-u;Kr^0+l%f6 A|B!8u6HJK;U&HGhSxjcA2D?Kk#Xrx zqdKTH>wFk^F$YfP5{yXGqMBKdy-K=a$@K4mCGi9<5?J5hQm(kY! 4i9$=X_o!>m7iBIssCvegon$sQ2xh5 zssHoP-)8#1FvLk(e}lj8*C#wQcU_*wNbY0u*$iUyB;hqTH(y>|_*Lu4`Gn`%7mtk- zBctKcQZBgh>yu9-0Y%HpRu7YSFp6|5LP| &?6J3E{DKC!Uaon>NVJfZ7A z_88->zrDISm(X U5uNZ*B>sbaa<4z@*HF>W`fFS*fB*hnDtXb}-r1R{ zRbn{W7%2W;Jdi+7Pj7i;=T1`rc1V!_=^vBdUtd~HR&)FL`SsO#yFPr!cO`+tz!VL1 zj`-lqEC?a8Pl1;KzZVzZ))*-$;Kb62|G;GO>4wmv=infSqZO5sl4@xzzDT|2d6Cvt zYFK89!NbctyRs6ox70fi4O#8RfOB!xw}XS~JA^8Ed3l%d8}V=7K1V^rG+rId6Y|`o zQB_r?$3~r-pLgD#k;J<$OsbZn^h}?_(g=K%6$9BvRcFxA(GhZ*kw`k-7_V4soa?3} zUeSyqXD>BwMrAW -zj&v43Qodn=asT-$haW$FAcBhYsv2CxC5g6|(gINt z`UVDLPSTFV{{H^R@qc}BH#0jURJrW$?COe)dHg(vT9A>CkM!-^x1$aI_f8JCI4hH1 zzrNZLM;BLNJ0ivS(zy4%Xtmc4BXtY}5-u{Tawo?tY_Sv-6(?(4u{^h?*xehu6F8ZK zg{e3=IIM;Xb@EOZ78XptzvEvXD`#09E{ZxiSj)5>Dc1gM_4pY%w%5u2?RWgPm`M+_ z`6q4&2~nDLL`#NZ;?JzDMg2& ~w2lut%qm#7~VhReaIgvOzaq?a@ZEfwjJv1x=3Fg=7u1i0t zCMGKFag!cCAIMRj7r#6vd?#R!Lq|uaU+Yn%Z?84QlTYEey}SEUSlI{`%kcW!=O31T zHmatjm|vSr)p=7bns5HEfEn>P@MRM_8K>@ zMGq{s6@Cc+o-CMYQ0uY1v(uj#_qnu`NUzF4rpn uww4WN=N&(9ke%LA%z2-PF{S%YK}X&-y32`|6-1#7C)VJI<|m zg`$x!;8D!V@1rfgziaO7#A4U0Y|DL|>2z^+>{4}DKrWE?@H{Fqvec}TV1H!*3;8sB zNq=Kt;QFuHszDwRuWiJ9N6gi % <5*UIMz(zYIye+l+>8C zVj`N3cIFx46)H9 ;V@g2Tge*)Z);As#tJMMd{lhj5bx>{}}BCy=pTbZQTqh2S!7 zLP*QVSpQ Il(_3~uV-u6dtH%?c zt VxO`e-JUkWWmjS5c?LP;@Z&@%_n*^TTPggVmuqcbF{k_3JT@k_Ak_ z|BntvjCM9_w^rAudfxHlUb%9Gi<>*RzFsskGO|k~o6+3O-Thfrl^~=er-N0E&-N3V zBFa)yA(Vb%-n%y-gDd)tVFW>}*xL1WcelcKBa@@Hj!tt|7fwV(1T3WKU~R;CxsO2| zoORNk%VS;pV|I3AaNLU*L@D2`D%X+ak&C^yR6s#GIR%nQ_~Va!Jd{wf6N9!G>B} zTMIeQp(~{dKZEW0VA=crW^uXysmF~QH=3H75CjASz|TTZ@viZp3pz{&H8hCjYn2dy zaj^(#XD-f9g X?ic}yWL3_ z*wo&hC4IM8za}It@Zy@cx3`mrhpdtk0WuYnlRtm+hScx;V1(-Q^5S$$yVMv3T{5V= zoY!WgIA(NIJ4Mu&9A?*^ArsYCV$`T`*xWVP_~8R0&2RV0kCeM;5z>re_eV;MI+50Z z;y|*MgiFC;ygpj?qI-dhiwijvBNG$1>*7N%FE91n9tfZIj}Km}XlrBNxpT+M$A?is zfLtz?rn$_lv(#(Hbbs>~_2S~9>4!@wUSGSqq>xE WfrP7=7)N&BH7hYKbn}!0AoKo((Vrpt?mR44B?A5Cb9f}~HhK0?;Y!E7Q zqL-gwCrlyBV&mXQnV2x(;pP8{XN91@A4bp)Wx8G9+qZ9$PEK4Qp`p&}qugiv1Ik={ zd~D^Mnpp;2ry>Ba;~&sNM79!&pV7-_shXVZue7v=5n|-2vkn&NX@Qwo%N$izUqQyY z3A0xVkBUN8KTf!JHo+*Mf7B6jScp0+85A)V@Uid-D7a4{w_&U1scTo*#CLGR-d+8u zn&$+D1s#fkiRr%A<@sUz-tUD4>-9jGl_YCCPC!6F3#`0tqpYk<3d#g>0zGNsjXfv5 z@Aws8C}g!jJyG-<&QYdA-VWuGtZL&|trlF7f@<>&$^x#8j7$hUwv?vkTP}x5A>?iN zRD8D{JHk*fUCDHnH`PDD!;wR~tqwjg^M(9_j$CN5f!=7XXUPY3)^2y@J8B)za4B$* z(=Rm+?)Yn?-->c^smzyu{>&%I{Q3$cWhn=TVtVY5?XzQ7=Rk3B@!790ndT1Y#QbjY z*^jHQxhrHkgp#xCV!wR(a_&z{i Aq<`s#f>k<**+jH3|0!^%nm z>3{{Mm<~}(b#S@c&Drk4sry;}>$h+Hw>qJEuvSX$xIBIO6g!5Bp9E}K>b9a9V+Q!< zHax{ZPcKpJvGJ~2l!r$Z%4j`(yuz~v|I^5gJ(s&3G1M$Kn3-|FFgVYo62$`>;3@_u zCnp0t`y-LX=1{DVoSYn$a*IbMow3*Fj!AKzA=5E(b`W ZO^to`}pw&Y`i2# z0y5!~)Ya7?ja~;y!TeA1*_mHD)C!~ry=Bv36!SbjKAz(>s&X)3;N?}DuLg@)*M%7d z$ey2{W7Oyu?~ENz2O7;?SJl1O(b0i)8|0&!#rn5N$;pwctf{GKX^Xh}*CuaG)lY71 z(Te&YopH3xOfI4~BO?PHU;=VEP#CkkE^%>jpT*zN!r@7Jh@5YY>yq2<)Pn~Ph}TkH zy~^0pF`vf$;?r9&9RXf;$8{RkQdS5Udd}nE{CGBu?lbM-?iQCM(NoB&WsuVTkOJIo z?^Q|@V;?NgQbrE>*M$AD4%)juE1IWaF*0T(m_K=fO(XjVFnE{#?w>!h5)ud(7njn& z%L|fINN{L4&rrAO4{7!F^%V?mbWBar`k(AK*Lm;F{QiBz;c$Pyt< `s4tB_ayOR#m2_UD=UvImmu>t5UR(S zviQi-Jv=-Nr|qOe02XBeqW${)dpl%41^pb20?kA*f8m~<9=^(g?uh~|&KQ@Y x)%8lbz)lLg=naLmNKo80*nBJstmSE#nOwuSmNsy%G{e0*&$818cgoE^C& z2m&owSy_1@f8z*JbNf0U)bpNXK|-JkeFFnm!x$2>AeD0o3d+HpBRogI$S4vt0jI15 zS|zfYn&dwF%fZTG$LJ>~CkTX^ni`S}l~z==gXxG#NK_XNJ2)H9Lh9;7_#CHbru`4E z!`#~~m%uv2)9tJ4)%kUvtQm}37zXuAL-)HkHa5E1(u#_Tj$Czhb>mlk0GT{}_6&(k zkdeD2i>cl^)OdN}i^%W!9jRZCv5~Qc#B~?HkP1SE%bi=d5C}XfKJ2Eb*`GEc^x=Ei zd55<*aHj;P{X*NLC|XBGs@g19IrX4S>c)|wqW~0cfiyF_xcD5OS|Gfcu>zo)b9iNC zdg;Z*MQK%)93=B MN-(dg&g;l#Q`SWRAj_(1Pm`^6B+?TJ5P(g)lf5x_FAwlCU5 z0~p5k_zyTJ!;Ks0foD5m)f)hlZ?UpU)%zUufq#)Kxw^QJgsP37C;$~`mr&n1$q!2; z=e%!hOi#{f7!7-wAf&uAJCh~qy70LFBu&^I2Lc}!o(o=`cQ^wjbuPu7f x)WfdLl(a$C#?Aa??pmz#0+HJxjf090sgYeR<^ z=iC#4{Emhwx D&_yKe8(+w9z2`4mi3z`MGKk9$!lsi;^9xQo|)a~Qi$4f_^+ z4MoG!0sbT{42+CWq=_DEwRfrZPJ`eOsI9B}_NdORGd9<-UKj@l=krCsWOCQ%sA6P( zJMo-PL2`SC)g}|D4clp#XIO`uzwTRE-8wrvYol*{&7!PQ@8ka2W+=ah)AaW3+eh