commit a17cc15bbde230971db1a93bff210d2af331b1ee Author: simon987 Date: Mon Aug 26 16:14:34 2019 -0400 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9aa9d49 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1062418 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +*.iml diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..7ef9e4e --- /dev/null +++ b/css/main.css @@ -0,0 +1,404 @@ +.img-wrapper { + width: 33.33%; + display: inline-block; +} + +.img { + width: 100%; + margin-bottom: 0.25rem; +} + +form { + padding: 1rem; + background: #b3d4fc; + margin-bottom: 0.5rem; +} + +form textarea { + width: 100%; + padding: 0.4rem 0.2rem; +} + +form .row { + margin-bottom: 0.5rem; +} + +body,html { + padding: 0; + margin: 0; +} + +.col .row { + margin-left: -0.25rem; + margin-right: -0.25rem; +} + +.row .col { + float: left; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0 0.25rem; + min-height: 1px; +} + +.row .col.s1 { + width: 8.3333333333%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s2 { + width: 16.6666666667%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s3 { + width: 25%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s4 { + width: 33.3333333333%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s5 { + width: 41.6666666667%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s6 { + width: 50%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s7 { + width: 58.3333333333%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s8 { + width: 66.6666666667%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s9 { + width: 75%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s10 { + width: 83.3333333333%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s11 { + width: 91.6666666667%; + margin-left: auto; + left: auto; + right: auto; +} + +.row .col.s12 { + width: 100%; + margin-left: auto; + left: auto; + right: auto; +} + + +/*! HTML5 Boilerplate v7.2.0 | MIT License | https://html5boilerplate.com/ */ + +/* main.css 2.0.0 | MIT License | https://github.com/h5bp/main.css#readme */ +/* + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + */ + +/* ========================================================================== + Base styles: opinionated defaults + ========================================================================== */ + +html { + color: #222; + font-size: 1em; + line-height: 1.4; +} + +/* + * Remove text-shadow in selection highlight: + * https://twitter.com/miketaylr/status/12228805301 + * + * Vendor-prefixed and regular ::selection selectors cannot be combined: + * https://stackoverflow.com/a/16982510/7133471 + * + * Customize the background color to match your design. + */ + +::-moz-selection { + background: #b3d4fc; + text-shadow: none; +} + +::selection { + background: #b3d4fc; + text-shadow: none; +} + +/* + * A better looking default horizontal rule + */ + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +/* + * Remove the gap between audio, canvas, iframes, + * images, videos and the bottom of their containers: + * https://github.com/h5bp/html5-boilerplate/issues/440 + */ + +audio, +canvas, +iframe, +img, +svg, +video { + vertical-align: middle; +} + +/* + * Remove default fieldset styles. + */ + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +/* + * Allow only vertical resizing of textareas. + */ + +textarea { + resize: vertical; +} + +/* ========================================================================== + Browser Upgrade Prompt + ========================================================================== */ + +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== + Author's custom styles + ========================================================================== */ + +/* ========================================================================== + Helper classes + ========================================================================== */ + +/* + * Hide visually and from screen readers + */ + +.hidden { + display: none !important; +} + +/* +* Hide only visually, but have it available for screen readers: +* https://snook.ca/archives/html_and_css/hiding-content-for-accessibility +* +* 1. For long content, line feeds are not interpreted as spaces and small width +* causes content to wrap 1 word per line: +* https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe +*/ + +.sr-only { + border: 0; + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + white-space: nowrap; + width: 1px; + /* 1 */ +} + +/* +* Extends the .sr-only class to allow the element +* to be focusable when navigated to via the keyboard: +* https://www.drupal.org/node/897638 +*/ + +.sr-only.focusable:active, +.sr-only.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + white-space: inherit; + width: auto; +} + +/* +* Hide visually and from screen readers, but maintain layout +*/ + +.invisible { + visibility: hidden; +} + +/* +* Clearfix: contain floats +* +* For modern browsers +* 1. The space content is one way to avoid an Opera bug when the +* `contenteditable` attribute is included anywhere else in the document. +* Otherwise it causes space to appear at the top and bottom of elements +* that receive the `clearfix` class. +* 2. The use of `table` rather than `block` is only necessary if using +* `:before` to contain the top-margins of child elements. +*/ + +.clearfix:before, +.clearfix:after { + content: " "; + /* 1 */ + display: table; + /* 2 */ +} + +.clearfix:after { + clear: both; +} + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + These examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 35em) { + /* Style adjustments for viewports that meet the condition */ +} + +@media print, +(-webkit-min-device-pixel-ratio: 1.25), +(min-resolution: 1.25dppx), +(min-resolution: 120dpi) { + /* Style adjustments for high resolution devices */ +} + +/* ========================================================================== + Print styles. + Inlined to avoid the additional HTTP request: + https://www.phpied.com/delay-loading-your-print-css/ + ========================================================================== */ + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; + /* Black prints faster */ + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + /* + * Don't show links that are fragment identifiers, + * or use the `javascript:` pseudo protocol + */ + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre { + white-space: pre-wrap !important; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + /* + * Printing Tables: + * https://web.archive.org/web/20180815150934/http://css-discuss.incutio.com/wiki/Printing_Tables + */ + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} + diff --git a/css/normalize.css b/css/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/css/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/img/.gitignore b/img/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html new file mode 100644 index 0000000..0091b59 --- /dev/null +++ b/index.html @@ -0,0 +1,83 @@ + + + + + + feed_viz V1.0 + + + + + + + + + + + +
+
+
+ + + + + +
+ Idle + +
+
+ +
+ + +
+ +
+ + + + + + +
+ + + + +
+
+ +
+
+ +
+ + + + + diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..63d1b25 --- /dev/null +++ b/js/main.js @@ -0,0 +1,286 @@ +let maxImages = 300; +let currentImages = 0; + +let socket = undefined; + +let colCount = 6; +let cols = []; +let colHeights = []; + +let loadNsfw = false; + +let storage = window.localStorage; + +window.onload = function () { + setDefaultPresets(); + updatePresets(); + + const storageColCount = storage.getItem("col-count"); + if (storageColCount) { + document.getElementById("col-count").value = storageColCount; + colCount = storageColCount; + } + + const storageMaxImages = storage.getItem("max-images"); + if (storageMaxImages) { + document.getElementById("max-images").value = storageMaxImages; + maxImages = storageMaxImages; + } + + if (storage.getItem("nsfw") === "true") { + document.getElementById("nsfw").checked = true; + loadNsfw = true; + } +}; + +function resetGallery() { + let out = document.getElementById("output"); + while (out.hasChildNodes()) { + out.removeChild(out.lastChild); + } + cols = []; + colHeights = []; + currentImages = 0; + document.getElementById("output").appendChild(createGallery(colCount)); +} + +function onConnectClick() { + + if (socket) { + socket.close() + } + + document.getElementById("status").innerHTML = "Connecting..."; + document.getElementById("status").style = "color: orange"; + + resetGallery(); + + let topics = document.getElementById("topics").value + .toLowerCase() + .split(/[\s,]+/ig) + .filter(topic => topic !== ""); + + if (topics.length === 0) { + alert("You must specify at least one topic"); + return + } + + connect(topics); +} + +function connect(topics) { + + socket = new WebSocket("ws://localhost:3090/socket"); + + socket.onmessage = msg => { + let j = JSON.parse(msg.data); + + if (j.urls && ((loadNsfw && j.over_18) || !j.over_18)) { + j.urls + .filter(url => /http?s:\/\/.*(.jpg|.jpeg|.bmp|.png|.gif|.jpeg:orig|.jpg:orig)$/.test(url)) + .forEach(url => appendToGallery(createImage(url))); + } + }; + + socket.onopen = () => { + socket.send(JSON.stringify({ + exchange: "reddit", + topics: topics + })); + + document.getElementById("status").innerHTML = "Connected"; + document.getElementById("status").style = "color: green"; + }; + + socket.onclose = () => { + document.getElementById("status").innerHTML = "Disconnected"; + document.getElementById("status").style = "color: red"; + }; + + socket.onerror = (e) => { + document.getElementById("status").innerHTML = "Websocket error"; + document.getElementById("status").style = "color: orange"; + console.log(e) + }; + + window.onbeforeunload = function () { + socket.close(); + } +} + +function createImage(src) { + const img = document.createElement("img"); + img.setAttribute("class", "img"); + img.setAttribute("src", src); + + img.onerror = function () { + //Don't display broken images + img.remove(); + return true; + }; + + return img; +} + +function createGallery(count) { + + const gallery = document.createElement('div'); + gallery.setAttribute('class', 'row'); + + for (let i = 0; i < count; i++) { + let col = document.createElement('div'); + col.setAttribute('class', `col s${Math.floor(12 / count)}`); + gallery.appendChild(col); + cols.push(col); + colHeights.push(0); + } + + return gallery; +} + +function popRow() { + for (let i = 0; i < colCount; i++) { + if (cols[i].hasChildNodes()) { + colHeights[i] -= cols[i].firstChild.height; + cols[i].firstChild.remove(); + currentImages -= 1; + } + } +} + +function appendToGallery(child) { + + currentImages++; + + if (currentImages > maxImages) { + popRow() + } + + let minHeight = Number.MAX_VALUE; + let min = 0; + + for (let i = 0; i < cols.length; i++) { + if (colHeights[i] < minHeight) { + minHeight = colHeights[i]; + min = i; + } + } + + cols[min].appendChild(child); + if (!child.height) { + colHeights[min] += 2400 / colCount; + child.onload = function () { + colHeights[min] += this.height; + colHeights[min] -= 2400 / colCount; + }; + } else { + colHeights[min] += child.height; + } +} + +function getPreset(name) { + let j = JSON.parse(storage.getItem("presets")); + return j[name] +} + +function setPreset(name, topics) { + if (storage.getItem("presets") === null) { + storage.setItem("presets", "{}") + } + + let j = JSON.parse(storage.getItem("presets")); + j[name] = topics; + storage.setItem("presets", JSON.stringify(j)) +} + +function setDefaultPresets() { + setPreset("Reddit animals", "*.aww, *.awww, *.cats, *.eyebleach, *.cute, *.dogs, " + + "*.koalas, *.lynxes, comment.dogs, *.dogpictures, submission.puppies, submission.puppy, " + + "submission.doge"); + setPreset("Reddit birds", "submission.birds, submission.bird, submission.parrots, " + + "submission.birdpics, submission.birdwatching, submission.birding, submission.birdphotography"); + setPreset("Reddit ocean", "*.ocean, *.oceangifs, *.swimming, " + + "*.algae, *.costalforaging, *.earthscience, *.freediving, " + + "*.hydrology *.lifeaquatic, *.marinelife, *.octopuses, " + + "*.scuba, *.seacreatureporn, *.seaweed, *.sharks, " + + "*.shipwrecks, *.water, *.whales, *.underwaterphotography"); +} + +function onPresetSelect() { + let selection = document.getElementById("presets").value; + document.getElementById("topics").value = getPreset(selection); +} + +function onSavePresetClick() { + let name = document.getElementById("preset-name").value; + let topics = document.getElementById("topics").value; + + if (name === "") { + alert("Invalid preset name!") + } + + setPreset(name, topics); + updatePresets(); +} + +function updatePresets() { + let j = JSON.parse(storage.getItem("presets")); + const select = document.getElementById("presets"); + + while (select.hasChildNodes()) { + select.removeChild(select.lastChild); + } + + const opt = document.createElement("option"); + opt.setAttribute("disabled", ""); + opt.setAttribute("value", "-"); + opt.appendChild(document.createTextNode("Load Preset")); + select.appendChild(opt); + + Object.keys(j).forEach(key => { + const opt = document.createElement("option"); + opt.setAttribute("value", key); + opt.appendChild(document.createTextNode(key)); + select.appendChild(opt) + }); + + select.value = "-" +} + +function onMaxImageChange() { + maxImages = document.getElementById("max-images").value; + storage.setItem("max-images", maxImages); +} + +function onNsfwChange() { + loadNsfw = document.getElementById("nsfw").checked; + storage.setItem("nsfw", loadNsfw); +} + +function onColCountChange() { + + colCount = document.getElementById("col-count").value; + storage.setItem("col-count", colCount); + + let images = []; + const row = document.getElementById("output").firstChild; + + if (!row) { + return + } + + while (row.hasChildNodes()) { + let col = row.lastChild; + + while (col.hasChildNodes()) { + images.push(col.lastChild); + col.removeChild(col.lastChild); + } + row.removeChild(col); + } + + resetGallery(); + + images.forEach(img => appendToGallery(img)); +} +