New repo who dis

This commit is contained in:
Alex Huddleston 2019-12-07 20:41:23 -06:00
commit 21dbefab2d
10 changed files with 1291 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/elm-stuff/
/downloads/
/guidelines/
assets/images/420/
assets/images/full/
assets/other/
assets/javascripts/main.js

18
add-iamge.sh Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
IMG=$1
if ! [ -f $IMG ]; then
echo "Please specify a path to an image"
exit
fi
PATHFULL=assets/images/full
PATH420=assets/images/420
mkdir -p $PATHFULL $PATH420
file=$(basename $IMG)
convert $IMG -resize 420x420 "${PATH420}/${file%.*}.png"
convert $IMG "${PATHFULL}/${file%.*}.png"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,2 @@
function isMobile(){return (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))){return true;}else{return false;}})(navigator.userAgent||navigator.vendor||window.opera);}

402
assets/styles/styles.css Normal file
View file

@ -0,0 +1,402 @@
body {
background-color: #ffffff;
margin: 0;
font-family: sans-serif;
}
.mobile-nav .dropdown {
position: relative !important;
}
.mobile-nav.hidden {
display: none;
}
header {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1;
color: #999999;
background-color: #292929;
border-color: #101010;
border-style: solid;
border-width: 0;
border-bottom-width: 1px;
}
.desktop-header {
height: 50px;
}
.mobile-header {
max-height: 100vh;
overflow: auto;
}
.mobile-header .show-nav {
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 50px;
}
.mobile-header .show-nav.active {
color: #ffffff;
background-color: #2266ff;
}
.mobile-header .show-nav p {
margin: 0;
padding: 0 1em;
}
.carat {
display: inline-block;
width: 0;
height: 0;
margin-left: 2px;
vertical-align: middle;
border-top: 4px dashed;
border-top-color: currentcolor;
border-top: 4px solid;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
}
nav {
background-color: #292929;
}
.desktop-nav {
margin: 0 auto;
height: 100%;
}
.nav-wrapper {
background-color: #292929;
}
.nav-wrapper ul {
padding: 0;
list-style-type: none;
}
.nav-wrapper > ul {
margin: 0;
}
.desktop-nav-wrapper > ul {
display: flex;
height: 100%;
}
.mobile-header .show-nav,
.nav-wrapper > ul > li {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
.desktop-nav-wrapper > ul > li {
margin: 0;
text-align: center;
}
.nav-wrapper > ul > li:hover,
.nav-wrapper > ul > li .dropdown-active {
color: #ffffff;
background-color: #2266ff;
}
.desktop-nav-wrapper > ul > li:hover,
.desktop-nav-wrapper > ul > li .dropdown-active {
border-color: #2266ff;
border-style: solid;
border-width: 0;
border-bottom-width: 1px;
margin-bottom: -1px;
}
.nav-wrapper > ul > li .button-wrapper {
cursor: pointer;
}
.desktop-nav-wrapper > ul > li .button-wrapper {
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
}
.desktop-nav-wrapper > ul > li .button-wrapper p {
margin: 0;
padding: 0 1em;
white-space: nowrap;
}
.mobile-nav-wrapper > ul > li .button-wrapper p {
margin: 0;
padding: 1em;
white-space: nowrap;
}
.nav-wrapper > ul > li .nav-link {
display: block;
text-decoration: none;
color: inherit;
}
.mobile-nav .nav-item > div {
display: block !important;
}
.desktop-nav-wrapper > ul > li.home-item .button-wrapper p {
font-weight: lighter;
}
.mobile-nav-wrapper > ul > li.home-item .button-wrapper p {
font-weight: bold;
}
.nav-wrapper > ul > li.nav-item .button-wrapper p {
font-weight: bold;
font-size: smaller;
}
.desktop-nav-wrapper > ul > li .dropdown {
color: #292929;
background-color: #ffffff;
text-align: left;
min-width: 10em;
border-bottom: 1px solid #999;
margin-top: 1px;
}
.mobile-nav-wrapper > ul > li .dropdown {
color: #cccccc;
background-color: #404040;
display: none;
}
.mobile-nav-wrapper > ul > li .dropdown-active {
display: block;
}
.desktop-nav-wrapper > ul > li .dropdown,
.desktop-nav-wrapper > ul > li ul.dropdown > li.sub-item:last-child p {
border-bottom-left-radius: 0.4em;
border-bottom-right-radius: 0.4em;
}
.desktop-nav-wrapper > ul > li .dropdown li.sub-item {
cursor: pointer;
}
.nav-wrapper > ul > li .dropdown li.sub-item p {
margin: 0;
font-size: smaller;
}
.mobile-nav-wrapper > ul > li .dropdown li.sub-item p {
padding: 0.8em 1em;
}
.desktop-nav-wrapper > ul > li .dropdown li.sub-item p {
padding: 0.4em 1em;
}
.nav-wrapper > ul > li .dropdown li.sub-item p:hover {
color: #ffffff;
background-color: #2266ff;
}
.welcome {
background-color: #444444;
background-image: url('/assets/images/full/pexels-photo-downscale.png');
background-attachment: local;
background-size: cover;
position: relative;
z-index: 0;
height: 100vh;
max-height: 500px;
margin-top: 51px;
}
.welcome .dimmer {
opacity: 0.7;
background-color: #000000;
color: #ffffff;
}
.welcome .centered {
margin-left: auto;
margin-right: auto;
}
.welcome .content {
padding: 0.5em 1em;
}
.welcome .content h1 {
font-size: 3em;
}
.welcome .content p {
padding: 0.5em 0;
font-size: 1.2em;
}
.welcome .content h1,
.welcome .content p {
margin: 0;
}
.gallery {
padding: 1em;
background-color: #efefef;
border-bottom: 1px solid #dbdbdb;
}
.gallery .gallery-wrapper {
margin: 0 auto;
width: 100%;
}
.gallery .gallery-info {
margin: 0 1em;
}
.gallery .gallery-columns {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 0 0.5em;
}
.gallery .gallery-column {
margin: 0 auto;
flex: 1;
max-width: 465px;
}
.gallery .gallery-image {
margin: 1em 0.5em;
}
.gallery .gallery-image-wrapper {
padding: 1em;
background-color: #ffffff;
box-shadow: 0px 2px 3px #999999;
}
.gallery .gallery-image img {
display: block;
width: 100%;
max-width: 420px;
}
footer {
background-color: #f7f7f7;
}
footer a {
text-decoration: none;
color: #00b0ff;
}
footer a:hover {
color: #5555f0;
}
footer a:active {
color: #00b0ff;
}
footer .footer-wrapper {
margin: 0 auto;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
}
footer .footer-column {
margin: 0 auto;
min-width: 300px;
max-width: 500px;
}
footer .footer-column-wrapper {
padding: 1em;
}
@media (min-width: 632px) {
.desktop-nav {
width: 900px;
}
.gallery .gallery-wrapper {
width: 600px;
}
footer .footer-wrapper {
width: 600px;
}
footer .footer-column {
width: 300px;
}
}
@media (min-width: 932px) {
.desktop-nav {
width: 900px;
}
.welcome .centered {
width: 900px;
}
.gallery .gallery-wrapper {
width: 900px;
}
footer .footer-wrapper {
width: 900px
}
footer .footer-column {
width: 300px;
}
}
@media (min-width: 1200px) {
.desktop-nav {
width: 1170px;
}
.welcome .centered {
width: 1170px;
}
.gallery .gallery-wrapper {
width: 1170px;
}
footer .footer-wrapper {
width: 1170px;
}
footer .footer-column {
width: 390px;
}
}

28
elm.json Normal file
View file

@ -0,0 +1,28 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"hercules-ci/elm-dropdown": "1.0.1"
},
"indirect": {
"elm/json": "1.1.3",
"elm/parser": "1.1.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2",
"elm-community/json-extra": "4.2.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

25
index.html Normal file
View file

@ -0,0 +1,25 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Werefox Software</title>
<link rel="shortcut icon" href="/assets/images/favicon/favicon.png">
<link href="/assets/styles/styles.css" rel="stylesheet" type="text/css">
<script src="/assets/javascripts/detect_mobile.js"></script>
<script src="/assets/javascripts/main.js"></script>
</head>
<body>
<div id="elm-app">
<h1>Please enable javascript to view this website</h1>
</div>
<script>
var node = document.getElementById("elm-app");
var app = Elm.Main.init({
node: node,
flags: { mobile: isMobile() }
});
</script>
</body>
</html>

1
index.php Normal file
View file

@ -0,0 +1 @@
<?php header( 'Location: /index.html' ) ; ?>

3
make.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
elm make --output="assets/javascripts/main.js" src/Main.elm

805
src/Main.elm Normal file
View file

@ -0,0 +1,805 @@
module Main exposing (main)
{-| This is the main module of the application.
# main function
@docs main
-}
import Html exposing (Attribute, Html, a, article, button, div, footer, h1, h2, h3, h4, header, img, li, nav, p, section, span, text, ul)
import Html.Attributes exposing (class, href, rel, src, title)
import Html.Events exposing (onClick)
import Array exposing (Array)
import Dropdown exposing (ToggleEvent(..), drawer, dropdown, toggle)
import Browser.Events exposing (onResize)
import Browser.Dom exposing (getViewport)
import Browser exposing (document)
import Task exposing (perform)
-- MAIN
{-| The main function of the application
-}
main : Program Flags Model Msg
main =
document
{ init = init
, view = (\model -> { title = "Your Mom", body = view model })
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ flags : Flags
, nav : Navigation
, welcome : WelcomeBanner
, gallery : Gallery
, footer : List FooterSection
}
type alias Navigation =
{ smallNav : Bool
, showNav : Bool
, navText : String
, items : Array NavItem
}
type NavItem
= HomeLink String String
| InitDropdown String (Array NavItem)
| Dropdown String DropdownConfig (Array NavItem)
| NavLink String String
type alias DropdownConfig =
{ name : String
, event : ToggleEvent
, attribute : Attribute Msg
, state : Dropdown.State
, message : Bool -> Msg
}
type alias WelcomeBanner =
{ title : String
, description : String
}
type alias Gallery =
{ title : String
, description : String
, url : String
, urltext : String
, columns : Int
, images : List GalleryItem
}
type GalleryItem
= Image GalleryImage
| Project GalleryProject
| ProjectLink GalleryLink
type alias GalleryImage =
{ url : String
, mouseoverText : String
}
type alias GalleryProject =
{ url : String
, mouseoverText : String
, title : String
, description : List String
}
type alias GalleryLink =
{ url : String
, mouseoverText : String
, title : String
, description : List String
, links : List ( String, String )
}
type alias FooterSection =
{ title : String
, description : String
, link : String
, url : String
}
type alias Flags =
{ mobile : Bool
}
-- INIT
init : Flags -> ( Model, Cmd Msg )
init flags =
( { flags = flags
, nav =
{ showNav = False
, smallNav = False
, navText = "Navigation"
, items =
initDropdowns
[ HomeLink "Home" "/"
, InitDropdown
"Social Media"
(initDropdowns
[ NavLink "Facebook" "https://facebook.com/shadow8t4"
, NavLink "Twitter" "https://twitter.com/Shadow8t4"
, NavLink "Google+" "https://plus.google.com/u/2/118253409016956205819"
]
)
, InitDropdown
"Games"
(initDropdowns
[ NavLink "Angels and Demons" "https://gitea.werefoxsoftware.com/shadow8t4/angels-and-demons"
, NavLink "Monster Chase" "https://gitea.werefoxsoftware.com/shadow8t4/MochaPine64Backup"
, NavLink "Monster Chase Server" "https://gitea.werefoxsoftware.com/shadow8t4/MochaServerPine64Backup"
, NavLink "Revival Survival" "/revival-survival"
, NavLink "Project Undercover" "/project-undercover"
, NavLink "Project Undercover Git Repo" "https://gitea.werefoxsoftware.com/shadow8t4/Project-Undercover"
, NavLink "So Bow-y Cute" "https://double-darling-duo-deluxe.itch.io/so-bow-y-cute"
]
)
, InitDropdown
"Other Projects"
(initDropdowns
[ NavLink "Re-Procedural City" "https://gitea.werefoxsoftware.com/shadow8t4/Re-ProceduralCity"
, NavLink "Procedural City" "https://gitea.werefoxsoftware.com/shadow8t4/ProceduralCity"
, NavLink "PSVR Git Repo" "https://gitea.werefoxsoftware.com/shadow8t4/Public-Speaking-VR"
, NavLink "PSVR Research Paper" "/assets/other/Public_Speaking_in_VR_Research_Paper.pdf"
]
)
]
}
, welcome = welcomeBanner "Werefox Software" "A self-hosted portfolio site for and by Alex Huddleston"
, gallery =
initGallery
"Portfolio Gallery"
"A few projects I've worked on. The source code for many of these can be viewed on my self-hosted git service (hosted through Gitea)."
"https://gitea.werefoxsoftware.com"
"Git subdomain"
[ galleryLink
"angels-and-demons.png"
"A completed game board."
"Angels and Demons"
[ "Angels and Demons is a board game concept I created during my time in college, specifically"
++ " during my Game History class. I decided on my own time to take up converting it to a"
++ " video game. Right now, it's focused for mainly Android and WebGL builds. It's currently"
++ " (as of June 2018) in very early development. The basic mechanics and logic work, but "
++ " it can only be played locally with very little indication of turns or progress. There"
++ " are also no instructions aside from a rough document I made for the class, which has"
++ " been converted to the repository's readme."
]
[ ( "Git Repo"
, "https://gitea.werefoxsoftware.com/shadow8t4/angels-and-demons"
)
]
, galleryLink
"projectundercover-guard.png"
"The guard's view in ProjectUndercover."
"ProjectUndercover - guard view"
[ "Project Undercover is a game about pretending to be an AI. Or at least -- that's part of it."
++ " Players will compete against one another, with one side trying to blend in with a crowd"
++ " of non-player characters, and the other side attempting to identify them. It takes place"
++ " at a party, and the undercover players are agents trying to infiltrate and complete"
++ " several missions before the guard, or overseer, catches them."
, "The overseer is limited"
++ " by a set of cameras, and slowly receives information over the course of the game to"
++ " help identify the agents, thus putting the heat on them."
, "This picture is from the perspective of one of the guard's cameras."
]
[ ( "Git Repo"
, "https://gitea.werefoxsoftware.com/shadow8t4/Project-Undercover"
)
]
, galleryLink
"revival-survival.png"
"A game where you save a soul you accidentally reaped as the grim reaper."
"Revival Survival"
[ "This was my entry for Chillennium 2017, a game jam held and hosted by students at Texas A&M."
++ " I was in a group with 3 other students, and mostly worked on the sound design and"
++ " game mechanics from the game. The game was made using Unity."
, "In Revival Survival, you play as the Grim Reaper in a 2D platformer that has to return"
++ " the soul of someone they accidentally reaped. Throughout the game you are met with"
++ " adversaries - hellhounds that want to eat the soul you reaped and paladins who wish to avenge"
++ " that soul you reaped."
, "You can find a link to play this game in browser on the nav bar at the top of this page, as well"
++ " as the link below. I've also provided a link to the itch.io submission page, where you"
++ " can download a Windows standalone copy of the game."
]
[ ( "itch.io Page Link"
, "https://d4-team.itch.io/revival-survival"
)
, ( " / "
, "/"
)
, ( "In-browser Game Link"
, "/revival-survival"
)
]
, galleryLink
"mocha_bordered.png"
"The status screen of the game."
"Monster Chase"
[ "Monster Chase was a fitness game/app developed during my senior capstone class in college."
++ " The development team consisted of me and 4 other computer science majors, and the"
++ " objective was to create a fitness game designed to track users' steps through engaging"
++ " gameplay of the user being virtually chased by a monster that they could customize and"
++ " use to challenge other players online. The game makes use of the FitBit API if a device"
++ " and subsequent account are available to help track data and utilizes and separate server"
++ " to keep track of points for an online leaderboard functionality. While the finaly build"
++ "is still in many cases a prototype compared to what we initially intended to develop, it"
++ "is functional and the source code for both the app and server are available in their"
++ "subsequent repositories."
, "During the development of the game, I worked mainly on the front-end design and functionality"
++ " of the game through the Unity editor. Most scripted events and transitions in the front end"
++ " were also programmed by me, or I at least had a hand in."
]
[ ( "Git Repo "
, "https://gitea.werefoxsoftware.com/shadow8t4/MochaPine64Backup"
)
, ( " / "
, "/"
)
, ( "Server Git Repo"
, "https://gitea.werefoxsoftware.com/shadow8t4/MochaServerPine64Backup"
)
]
, galleryProject
"projectundercover-spy.png"
"A spy's view in ProjectUndercover."
"Project Undercover - spy view"
[ "This is another picture from Project Undercover, this time from the perspective"
++ " of a spy. In this picture, the spy is finishing a waving interaction with an AI spy."
]
, galleryLink
"public-speaking-vr.png"
"A Case Study on Public Speaking in Virtual Reality."
"Public Speaking in VR"
[ "In this project, and the resulting research paper, a group I was assigned in and I show the results of"
++ " a case study on the effectiveness and realism of using Virtual Reality technology to simulate the"
++ " experience of public speaking in an effort to practice ones speech skills."
, "We used a Development"
++ " Kit 2 version of the Oculus Rift and the Unity editor to create and run our simulation. In the"
++ " simulation, we presented users a large classroom environment with a handful of characters to"
++ " present a mock speech in front of. We provided the users with an Xbox game controller to allow"
++ " for easier camera movement as well as a way to switch through the given slides in the mock presentation."
, "The resulting research paper from the case study and a link to the project's github page can be found"
++ " below."
]
[ ( "Git Repo "
, "https://gitea.werefoxsoftware.com/shadow8t4/Public-Speaking-VR"
)
, ( " / "
, "/"
)
, ( "Research Paper"
, "/assets/other/Public_Speaking_in_VR_Research_Paper.pdf"
)
]
, galleryLink
"re-procedural-city.png"
"An image of the re-implementation's output."
"Re:Procedural City"
[ "A Rust implementation of the Procedural City project. This was done to gain some insight"
++ " into the programming language and to refamiliarize myself with the work itself. In"
++ " addition, the project is done through solely open-source libraries as opposed to the"
++ " ones used in the original project provided by my professor, meaning that documentation"
++ " of the library functions is much more accessible."
]
[ ( "Git Repo"
, "https://gitea.werefoxsoftware.com/shadow8t4/Re-ProceduralCity"
)
]
, galleryLink
"procedural-city.png"
"A picture of some output from ProceduralCity"
"Procedural City"
[ "In this project my partner, Jeremy Martin, and I presented a way of procedurally generating a basic city by"
++ " creating an .obj mesh file using some given template meshes. The goal was that when given a"
++ " user input population density and some template meshes, the program would output"
++ " a resulting mesh of a procedurally generated city using the template"
++ " meshes along with some defined rules. The program takes the input files and duplicates it at"
++ " procedurally determined intersections, creating a basic city structure in the"
++ " resulting output .obj mesh file."
, " Unfortunately, the program was unable to be fully"
++ " completed before our deadline, and currently takes one input mesh file and duplicates it"
++ " based on user-defined amounts of layers from a central point with user-defined spacing."
, "Specific documentation on how to operate the program is detailed on the respective github page."
]
[ ( "Git Repo"
, "https://gitea.werefoxsoftware.com/shadow8t4/ProceduralCity"
)
]
, galleryLink
"so-bow-y-cute.png"
"A game where you become cute."
"So Bow-y Cute"
[ "This was my entry for Chillennium 2016, a game jam held and hosted by students at Texas A&M."
++ " This was my first time at the game jam and I was randomly patnered with one other person,"
++ " who did the art for this game. Everything else was done by me. The game was made using Unity."
, "In So Bow-y Cute - When your parent decides to limit you cuteness potential by prohibiting all bows, you must"
++ " rebel against their tyranny and become the cutest you possibly can. Be careful not to get"
++ " caught! or else you're out of luck."
, "You can find a link to the itch.io page below."
]
[ ( "itch.io Page Link"
, "https://double-darling-duo-deluxe.itch.io/so-bow-y-cute"
)
]
]
, footer =
[ initFooterSection
""
""
""
"/"
, initFooterSection
""
"This website was programmed in elm with the help of my friend Riley."
"source code"
"https://gitea.werefox.dev/shadow8t4/portfolio-site"
, initFooterSection
""
""
""
"/"
]
}
, Task.perform WindowWidth (getViewport
|> Task.map (\viewport -> floor viewport.scene.width))
)
smallImagePath : String -> String
smallImagePath filename =
"/assets/images/420/" ++ filename
fullImagePath : String -> String
fullImagePath filename =
"/assets/images/full/" ++ filename
defaultDropdownConfig : DropdownConfig
defaultDropdownConfig =
{ name = "dropdown1"
, event = OnClick
, attribute = class "dropdown-active"
, state = False
, message = ToggleDropdown 0
}
initDropdown : Int -> NavItem -> NavItem
initDropdown index item =
case item of
Dropdown name config items ->
Dropdown
name
{ config
| name = "dropdown" ++ (String.fromInt index)
, message = ToggleDropdown index
}
items
InitDropdown name items ->
Dropdown
name
{ defaultDropdownConfig
| name = "dropdown" ++ (String.fromInt index)
, message = ToggleDropdown index
}
items
other ->
other
initDropdowns : List NavItem -> Array NavItem
initDropdowns =
Array.indexedMap initDropdown << Array.fromList
welcomeBanner : String -> String -> WelcomeBanner
welcomeBanner title description =
{ title = title, description = description }
galleryImage : String -> String -> GalleryItem
galleryImage url text =
Image { url = url, mouseoverText = text }
galleryProject : String -> String -> String -> List String -> GalleryItem
galleryProject url text title description =
Project { url = url, mouseoverText = text, title = title, description = description }
galleryLink : String -> String -> String -> List String -> List ( String, String ) -> GalleryItem
galleryLink url text title description links =
ProjectLink { url = url, mouseoverText = text, title = title, description = description, links = links }
initGallery : String -> String -> String -> String -> List GalleryItem -> Gallery
initGallery title description url urltext images =
{ title = title, description = description, url = url, urltext = urltext, columns = 1, images = images }
initFooterSection : String -> String -> String -> String -> FooterSection
initFooterSection title description link url =
{ title = title, description = description, link = link, url = url }
-- VIEW
view : Model -> List (Html Msg)
view model =
[ headerBar model
, welcome model.welcome
, viewGallery model.gallery
, footerSections model.footer
]
welcome : WelcomeBanner -> Html Msg
welcome banner =
section [ class "welcome" ]
[ div [ class "welcome-wrapper" ]
[ div [ class "dimmer" ]
[ div [ class "centered" ]
[ article [ class "content" ]
[ h1 [ class "title" ] [ text banner.title ]
, p [ class "description" ] [ text banner.description ]
]
]
]
]
]
headerBar : Model -> Html Msg
headerBar model =
if model.flags.mobile || model.nav.smallNav then
mobileHeaderbar model
else
desktopHeaderBar model
desktopHeaderBar : Model -> Html Msg
desktopHeaderBar model =
header [ class "desktop-header" ]
[ nav [ class "desktop-nav" ]
[ div [ class "desktop-nav-wrapper", class "nav-wrapper" ]
[ ul [] (Array.map navItem model.nav.items |> Array.toList)
]
]
]
mobileHeaderbar : Model -> Html Msg
mobileHeaderbar model =
header [ class "mobile-header" ]
[ div
([ class "show-nav", onClick (ToggleNav (not model.nav.showNav)) ]
++ if model.nav.showNav then
[ class "active" ]
else
[]
)
[ p []
[ text model.nav.navText
, span [ class "carat" ] []
]
]
, nav
([ class "mobile-nav" ]
++ if model.nav.showNav then
[]
else
[ class "hidden" ]
)
[ div [ class "mobile-nav-wrapper", class "nav-wrapper" ]
[ ul [] (Array.map navItem model.nav.items |> Array.toList)
]
]
]
homeLink : String -> String -> Html Msg
homeLink name url =
li [ class "home-item" ]
[ div []
[ a [ class "nav-link", href url ]
[ div [ class "button-wrapper" ]
[ p [] [ text name ]
]
]
]
]
navItem : NavItem -> Html Msg
navItem item =
case item of
NavLink name url ->
navLink name url
Dropdown name config items ->
navDropdown name config items
HomeLink name url ->
homeLink name url
_ ->
text ""
navLink : String -> String -> Html Msg
navLink name url =
li [ class "nav-item" ]
[ div []
[ a [ class "nav-link", href url ]
[ div [ class "button-wrapper" ]
[ p [] [ text name ]
]
]
]
]
navDropdown : String -> DropdownConfig -> Array NavItem -> Html Msg
navDropdown name config items =
li [ class "nav-item" ]
[ dropdown div []
[
(toggle div
([ class "button-wrapper" ]
++ if config.state then
[ class "dropdown-active" ]
else
[]
)
[ p []
[ text name
, span [ class "carat" ] []
]
]
)
, (drawer ul [ class "dropdown" ] (Array.map subItem items |> Array.toList))
]
config.state
(dropdownConfig config)
]
subItem : NavItem -> Html Msg
subItem item =
case item of
NavLink name url ->
li [ class "sub-item" ]
[ a [ class "nav-link", href url ]
[ p [] [ text name ]
]
]
_ ->
text ""
dropdownConfig : DropdownConfig -> Dropdown.Config Msg
dropdownConfig config =
Dropdown.Config
config.name
config.event
config.attribute
config.message
viewGallery : Gallery -> Html Msg
viewGallery gallery =
let
arr : Array (List GalleryItem)
arr =
gallery.images
|> List.indexedMap (\index item -> ( index, item ))
|> List.foldl
(\( index, item ) acc ->
if (remainderBy gallery.columns index) < Array.length acc then
case
acc
|> Array.get (remainderBy gallery.columns index)
|> Maybe.map ((::) item)
of
Just column ->
Array.set (remainderBy gallery.columns index) column acc
Nothing ->
acc
else
Array.push [ item ] acc
)
Array.empty
in
div [ class "gallery" ]
[ section [ class "gallery-wrapper" ]
[ article [ class "gallery-info" ]
[ h2 [] [ text gallery.title ]
, p [] [ text gallery.description ]
, a [ href gallery.url ] [ text gallery.urltext ]
]
, div [ class "gallery-columns" ]
(arr
|> Array.map (div [ class "gallery-column" ] << List.map displayImage << List.reverse)
|> Array.toList
)
]
]
displayImage : GalleryItem -> Html Msg
displayImage item =
case item of
Image image ->
article [ class "gallery-image" ]
[ div [ class "gallery-image-wrapper" ]
[ a [ href (fullImagePath image.url) ]
[ img [ src (smallImagePath image.url), title image.mouseoverText ] []
]
]
]
Project project ->
article [ class "gallery-image" ]
[ div [ class "gallery-image-wrapper" ]
[ a [ href (fullImagePath project.url) ]
[ img [ src (smallImagePath project.url), title project.mouseoverText ] []
]
, div [ class "gallery-image-info" ]
[ h4 [] [ text project.title ]
, div [] (List.map (\t -> p [] [ text t ]) project.description)
]
]
]
ProjectLink projectlink ->
article [ class "gallery-image" ]
[ div [ class "gallery-image-wrapper" ]
[ a [ href (fullImagePath projectlink.url) ]
[ img [ src (smallImagePath projectlink.url), title projectlink.mouseoverText ] []
]
, div [ class "gallery-image-info" ]
[ h4 [] [ text projectlink.title ]
, div [] (List.map (\t -> p [] [ text t ]) projectlink.description)
, p [] (List.map (\( t, url ) -> a [ href url ] [ text t ]) projectlink.links)
]
]
]
footerSection : FooterSection -> Html Msg
footerSection item =
div [ class "footer-column" ]
[ article [ class "footer-column-wrapper" ]
[ h3 [] [ text item.title ]
, p [] [ text item.description ]
, p []
[ a [ href item.url ]
[ text item.link ]
]
]
]
footerSections : List FooterSection -> Html Msg
footerSections items =
footer []
[ section [ class "footer-wrapper" ] (List.map footerSection items)
]
-- UPDATE
type Msg
= WindowWidth Int
| ToggleDropdown Int Bool
| ToggleNav Bool
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
WindowWidth x ->
( { model
| gallery = updateGalleryWidth model.flags.mobile x model.gallery
, nav = updateNavWidth x model.nav
}
, Cmd.none
)
ToggleDropdown index state ->
( { model | nav = updateDropdown index state model.nav }
, Cmd.none
)
ToggleNav state ->
( { model | nav = toggleNav state model.nav }
, Cmd.none
)
toggleNav : Bool -> Navigation -> Navigation
toggleNav state nav =
{ nav | showNav = state }
updateGalleryWidth : Bool -> Int -> Gallery -> Gallery
updateGalleryWidth mobile width gallery =
if mobile then
gallery
else if width < 632 then
{ gallery | columns = 1 }
else if width < 932 then
{ gallery | columns = 2 }
else
{ gallery | columns = 3 }
updateNavWidth : Int -> Navigation -> Navigation
updateNavWidth width nav =
if width < 700 then
{ nav | smallNav = True }
else
{ nav | smallNav = False, showNav = False }
updateDropdown : Int -> Bool -> Navigation -> Navigation
updateDropdown index state nav =
case Array.get index nav.items of
Just dropdown ->
case dropdown of
Dropdown name config items ->
{ nav | items = Array.set index (Dropdown name { config | state = state } items) nav.items }
_ ->
nav
Nothing ->
nav
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
onResize (\width _ -> WindowWidth width)