Skip to content

Instantly share code, notes, and snippets.

@bathos
Last active June 27, 2016 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bathos/7db63d5434725a4c7dacfe72ddf4888a to your computer and use it in GitHub Desktop.
Save bathos/7db63d5434725a4c7dacfe72ddf4888a to your computer and use it in GitHub Desktop.
sketchy thing I had in an about:blank tab so I could keep track of where I am in sailor moon -- paste in console
// STATE ///////////////////////////////////////////////////////////////////////
const state = {
down: false,
index: 0
};
// DATA ////////////////////////////////////////////////////////////////////////
const scouts = [
{
index: 186,
name: 'Sailor Chibi Chibi',
symbol: '♥︎',
color: 'palevioletred'
},
{
index: 9,
name: 'Sailor Mars',
symbol: '♂',
color: 'tomato'
},
{
index: 32,
name: 'Sailor Venus',
symbol: '♀',
color: 'coral'
},
{
index: 91,
name: 'Sailor Uranus',
symbol: '♅',
color: 'gold'
},
{
index: 24,
name: 'Sailor Jupiter',
symbol: '♃',
color: 'forestgreen'
},
{
index: 0,
name: 'Sailor Moon',
symbol: '☾',
color: 'oldlace'
},
{
index: 102,
name: 'Sailor Chibi Moon',
symbol: '☾',
color: 'pink'
},
{
index: 91,
name: 'Sailor Neptune',
symbol: '♆',
color: 'cadetblue'
},
{
index: 7,
name: 'Sailor Mercury',
symbol: '☿',
color: 'royalblue'
},
{
index: 124,
name: 'Sailor Saturn',
symbol: '♄',
color: 'darkblue'
},
{
index: 74,
name: 'Sailor Pluto',
symbol: '♇',
color: 'indigo'
},
{
index: 172,
name: 'Sailor Starlights',
symbol: '★',
color: 'black'
},
];
const seasons = [
{
color: 'pink',
name: 'Sailor Moon',
episodes: [
'The Crybaby: Usagi’s Beautiful Transformation',
'Punishment Awaits: The House of Fortune Is the Monster Mansion',
'The Mysterious Sleeping Sickness: Protect the Girls in Love',
'Learn How to Be Skinny from Usagi',
'Scent of a Monster: Chanela Will Steal Your Love',
'Protect the Melody of Love: Usagi Plays Cupid',
'Usagi Learns Her Lesson: Becoming a Star Is Hard Work',
'The Girl Genius Is a Monster: The Brainwashing Cram School of Horror',
'Usagi’s Disaster: Beware of the Clock of Confusion',
'The Cursed Bus: Enter Mars, the Guardian of Fire',
'Usagi vs. Rei: Nightmare in Dream Land',
'I Want a Boyfriend: The Luxury Cruise Ship Is a Trap',
'Girls Unite: The End of Jadeite',
'A New Enemy Appears: Nephrite’s Evil Crest',
'Usagi’s Panic: Rei’s First Date',
'A Girl’s Dream: Usagi Becomes a Bride',
'Usagi’s a Model: The Flash of the Monster Camera',
'Shingo’s Love: The Grieving Doll',
'Usagi’s Joy: A Love Letter from Tuxedo Mask',
'The Summer, the Beach, Youth and Ghosts',
'Protect the Children’s Dreams: Friendship Through Anime',
'Romance Under the Moon: Usagi’s First Kiss',
'Wish Upon a Star: Naru’s First Love',
'Naru’s Tears: Nephrite Dies for Love',
'Jupiter, the Powerful Girl in Love',
'Restore Naru’s Smile: Usagi’s Friendship',
'Crushing on Ami: The Boy Who Can See the Future',
'The Painting of Love: Usagi and Mamoru Get Closer',
'Total Chaos: The Messy Love Rectangle',
'Grandpa Loses Control: Rei in Danger',
'Love and Chased: Luna’s Worst Day Ever',
'Umino’s Resolve: I’ll Protect Naru',
'Enter Venus, the Last Sailor Guardian',
'The Shining Silver Crystal: The Moon Princess Appears',
'Returning Memories: Usagi and Mamoru’s Past',
'Usagi’s Confusion: Is Tuxedo Mask Evil?',
'Let’s Become a Princess: Usagi’s Bizarre Training',
'The Snow, the Mountains, Friendship and Monsters',
'Paired with a Monster: Mako, the Ice Skating Queen',
'The Legendary Lake Yokai: The Bond of Usagi’s Family',
'I Won’t Run Away from Love Anymore: Ami vs. Mamoru',
'Sailor Venus’s Past: Minako’s Tragic Love',
'Usagi Abandoned: The Falling-Out of the Sailor Guardians',
'Usagi’s Awakening: A Message from the Distant Past',
'Death of the Sailor Guardians: The Tragic Final Battle',
'Usagi’s Eternal Wish: A Brand New Life'
]
},
{
color: 'thistle',
name: 'Sailor Moon R',
episodes: [
'Moon Returns: The Mysterious Aliens Appear',
'For Love and for Justice: Sailor Guardians Once Again',
'For Whom Is the White Rose? The Moonlight Knight Appears',
'Usagi’s Crisis: The Tiara Stops Working',
'A New Transformation: Usagi’s Power-Up',
'The Targeted Kindergarteners: Venus to the Rescue',
'Mamoru and Usagi’s Babysitting Mayhem',
'The School Festival Is for Me?! Queen Rei’s Song',
'Is Seijuro the Moonlight Knight? Mako on Fire',
'Steal a Kiss from Mamoru! An’s Project Snow White',
'After School Trouble: Usagi Is a Target',
'Disconnecting Love: The Raging Makai Tree',
'True Love Awakens: The Makai Tree’s Secret',
'Angel or Devil? The Mysterious Girl from the Sky',
'Usagi Devastated: Mamoru Declares a Break-Up',
'A Guardian’s Friendship: Goodbye, Ami',
'Women Must Be Strong and Beautiful: Rei’s New Special Technique',
'In Search of the Silver Crystal: Chibi-Usa’s Secret',
'Dispute Over Love: Minako and Makoto’s Conflict',
'Usagi’s Parental Love: The Curry Romance Triangle',
'The Beach, the Island and a Vacation: The Guardians’ Break',
'Protect Chibi-Usa: Clash of the Ten Warriors',
'Awaken the Sleeping Beauty: Mamoru’s Distress',
'Battle of the Flames of Love! Mars vs. Koan',
'For Friendship! Ami vs. Berthier',
'Rubeus the Heartless: The Tragic Sisters',
'A UFO Appears: The Sailor Guardians Abducted',
'Defeat Rubeus: The Battle in Space',
'The Mysterious New Guardian: Sailor Pluto Appears',
'Magic of Darkness: Esmeraude’s Invasion',
'Shared Feelings: Usagi and Mamoru in Love Once Again',
'Venus: Minako’s Nurse Mayhem',
'Artemis’s Adventure: The Monster Animal Kingdom',
'The Terrifying Illusion: Ami All Alone',
'The Dark Gate Is Completed? The Targeted Elementary School',
'Journey to the Future: Battle in the Space-Time Corridor',
'The Shocking Future: Demande’s Dark Ambition',
'Wiseman’s Evil Hand: Chibiusa Disappears',
'The Dark Queen: Birth of Black Lady',
'Saphir Dies: Wiseman’s Trap',
'Believing in Love and the Future: Usagi’s Decision',
'The Final Battle Between Light and Dark: Pledge of Love to the Future',
'Usagi and the Girls’ Resolve: Prelude to a New Battle'
]
},
{
color: 'paleturquoise',
name: 'Sailor Moon S',
episodes: [
'Premonition of the Apocalypse: The Mysterious New Guardians Appear',
'The Rod of Love Is Born: Usagi’s New Transformation',
'A Handsome Boy? Haruka Tenoh’s Secret',
'Usagi’s Idol: The Graceful Genius Michiru',
'Protect the Pure Heart: The Three-Way Battle',
'Let Moon Help With Your Love Problems',
'Coldhearted Uranus: Makoto in Danger',
'The Labyrinth of Water: Ami Targeted',
'To Save Our Friends: Moon and Uranus Join Forces',
'A Man’s Kindness: Yuichiro Heartbroken by Rei',
'I Want to Quit Being a Sailor Guardian: Minako’s Dilemma',
'Usagi in Tears: a Glass Slipper for My Birthday',
'The Stolen Pure Heart: Usagi’s Crisis',
'The Arrival of a Tiny Pretty Guardian',
'Making New Friends: Chibi Moon’s Adventure',
'I Want Power: Mako Lost in Doubt',
'The Bond of Destiny: Uranus’s Distant Past',
'Art Is an Explosion of Love: Chibi-Usa’s First Love',
'Usagi Dancing to the Waltz',
'The Shocking Moment: Everyone’s Identities Revealed',
'The Death of Uranus and Neptune: The Talisman Appear',
'The Holy Grail’s Mystical Power: Moon’s Double Transformation',
'Who is the True Messiah? Chaos of Light and Darkness',
'A House Filled With Evil Presence: The Beautiful Hotaru’s Secret',
'I Love Idols: Mimete’s Dilemma',
'Shadow of Silence: The Pale Glimmer of a Firefly',
'Sunny Skies After a Storm: A Friendship Dedicated to Hotaru',
'Higher and Stronger: A Cheer from Usagi',
'The Battle Inside the Demonic Space: The Sailor Guardians’ Gamble',
'The Messiah of Silence Awakens? Stars of Destiny',
'An Invasion from an Another Dimension: Mystery of Mugen Academy',
'A Bewitching Flower That Steals Hearts: Tellu, the Third Witch',
'Believe in Love: Ami, the Kindhearted Guardian',
'Shadows of Destruction: the Messiah of Silence Awakens',
'The Coming Terror of Darkness: Struggle of the Eight Guardians',
'The Shining Shooting Star: Saturn and the Messiah',
'A New Life: Parting of the Stars of Destiny',
'A Guardian’s Realization: Strength Lies Within a Pure Heart'
]
},
{
color: 'navajowhite',
name: 'Sailor Moon Super S',
episodes: [
'Meeting of Destiny: The Night Pegasus Dances',
'Super Transformation Once Again: Pegasus’s Power',
'Protect Mom’s Dream: Double Moon’s New Attack',
'Catch Pegasus: The Amazon’s Trap',
'The Perfect Couple: Usagi and Mamoru’s Love',
'Artemis Is Cheating? Enter the Mysterious Kitten',
'Makoto’s Friendship: A Girl Who Admired Pegasus',
'Connecting Hearts: Chibi-Usa and Pegasus',
'Protect Mamoru: Ninja Usagi’s Jealousy',
'Forest of Illusion: A Beautiful Fairy’s Invitation',
'Drive to the Heavens: The Dream Car Fueled with Love',
'Aiming for the Top: the Pretty Swordswoman’s Dilemma',
'We Love Fashion: The Stylish Guardians',
'Storm of Love: Minako’s Grand Two-Timing Plan',
'The Secret Mansion: A Menu of Love for You',
'Believe in Pegasus: The Four Guardians’ Super Transformation',
'Shining Summer Days: Ami Under the Sea-Breeze',
'Become a Prima: Usagi’s Ballet',
'Juban Holiday: The Carefree Princess',
'Destined Partners? Makoto’s Innocence',
'Shadow of Evil: The Trio’s Last Chance',
'Mirrors of Dreams: The Amazon’s Last Stage',
'The Amazoness: Nightmare From Behind the Mirrors',
'True Power Explodes: Ami’s Melody of the Heart',
'Flames of Passion: Mars’s Raging Super Attack',
'Dentist of Horrors? Palla-Palla’s House',
'Clash of Dreams: Minako and Makoto’s Broken Friendship',
'Overcome Your Fear: the Jump to Freedom',
'Don’t Lose Sight of Your Dreams: The Mirror of Truth',
'Pegasus Disappears: Wavering Friendship',
'Pegasus’s Secret: The Boy Who Protects the Dream World',
'Chibi-Usa’s Little Rhapsody of Love',
'Dream to be an Adult: The Amazoness’s Confusion',
'Terror in Motion: The Dark Queen’s Evil Hand',
'The Source of Darkness: Dead Moon Circus',
'Labyrinth of Mirrors: Chibi Moon Captured',
'The Golden Crystal Appears: Nehellenia’s Magic',
'When the Crystal Shines: The Beautiful Power of Dreams',
'Dreams Forever: Fill the Heavens with Light'
]
},
{
color: 'khaki',
name: 'Sailor Moon Sailor Stars',
episodes: [
'The Flower of Nightmares Scatters: The Queen of Darkness Returns',
'Saturn Awakens: The Ten Sailor Guardians Unite',
'The Cursed Mirror: Mamoru Caught in a Nightmare',
'Night of Destiny: Ordeals of a Guardian',
'For Love: the Endless Battle in the Dark World',
'Moon Power of Love: End of the Nightmare',
'Farewells and Encounters: the Transitioning Stars of Destiny',
'A School Storm: The Transfer Students Are Idols',
'Becoming an Idol: Minako’s Ambition',
'A Fighter’s True Spirit: Shocking Super Transformation',
'A Maker’s True Spirit: Shocking Super Transformation',
'Luna’s Discovery: The Real Face of Yaten',
'Friend or Foe? Star Lights and the Sailor Guardians',
'Calling of the Shining Stars: Enter Haruka and Michiru',
'Seiya and Usagi’s Heart-Pounding Date',
'Invaders from Outer Space: The Coming of Siren',
'The Screaming Dead: Terror of the Camp Monster',
'A Night Alone Together: Usagi in Danger',
'Taiki’s Song Filled With Passion and Faith',
'Chibi-Chibi’s Mystery: The Big Noisy Chase',
'The Shining Power of a Star: Chibi-Chibi’s Transformation',
'Invitation to Terror: Usagi’s Night Flight',
'Duty or Friendship: Conflict Between the Sailor Guardians',
'Truth Revealed: The Star Lights’ Past',
'Butterflies of Light: A New Chapter On the Horizon',
'Go for Your Dream: Minako Becomes an Idol',
'The Stolen Silver Crystal: Princess Kakyu Appears',
'Crusade for the Galaxy: Legend of the Sailor Wars',
'Princess Kakyu Perishes: Advent of Galaxia',
'Countdown to Destruction: The Sailor Guardians’ Final Battle',
'Ruler of the Galaxy: The Menace of Galaxia',
'Dying Stars: Uranus and Neptune’s Last Stand',
'The Light of Hope: The Final Battle for the Galaxy',
'Usagi’s Love: The Moonlight Illuminates the Galaxy'
]
}
];
const allEpisodes = seasons
.reduce((acc, { episodes, name: season }) => [
...acc,
...episodes.map((name, seasonIndex) =>
({ name, season, seasonIndex, seasonCount: episodes.length })
)
], []);
allEpisodes.forEach((episode, episodeIndex) => {
episode.scouts = scouts.filter(({ index }) => index <= episodeIndex);
});
// INITIAL RENDER //////////////////////////////////////////////////////////////
const seasonGradient = `linear-gradient(to right, ${ seasons
.reduce((acc, { color, episodes }) => {
const [ { fraction: start=0 }={} ] = acc.slice(-1);
const end = start + (episodes.length / allEpisodes.length)
return [ ...acc, { color, fraction: start }, { color, fraction: end } ];
}, [])
.map(({ color, fraction }) => `${ color } ${ fraction * 100 }%`)
.join(', ')
})`;
const backgroundURL =
`http://66.media.tumblr.com/10f21440351723698c77c2d3e5a0b2a6/` +
`tumblr_n2r8fglrVu1s1odqho1_1280.png`;
const bodyStyle = `
align-items : center; flex-direction:column;
background-blend-mode : lighten;
background-image : linear-gradient(to bottom, azure, lightblue),
url('${ backgroundURL }');
background-position : bottom center;
background-size : cover, cover;
display : flex;
justify-content : center;
margin : 0;
padding : 2rem;
`;
const buttonRowStyle = `
display : flex;
justify-content : space-around;
margin-bottom : -4.5rem;
width : 100%;
`;
const buttonStyle = `
-webkit-appearance : none;
background : azure;
border-radius : 0.2rem;
border : none;
box-shadow : 0 1px 6px -3px slategray;
color : dimgrey;
cursor : pointer;
font-style : italic;
height : 2rem;
transition : all 80ms linear;
width : 4rem;
`;
const episodeCountStyle = `
font-size : 1.5rem;
margin : 0;
`;
const episodeStyle = `
color : darkgray;
font-size : 2rem;
`;
const progressBGStyle = `
background-image : ${ seasonGradient };
border : 4px solid aliceblue;
box-shadow : 0 0px 20px -5px powderblue;
overflow : hidden;
`;
const progressFGStyle = `
background : linear-gradient(lightpink -200%, lightcoral 50%);
box-shadow : 0 0 15px 4px slateblue inset;
mix-blend-mode : color-burn;
transition : width 80ms ease-out;
`;
const progressNavStyle = `
cursor : pointer;
`;
const progressStyle = `
border-radius : 2rem;
height : 2rem;
width : 100%;
`;
const scoutStyle = `
align-items : center;
border : 2px solid white;
border-radius : 2.5rem;
color : white;
display : inline-flex;
font-family : STIXGeneral, monospace;
font-size : 1.5rem;
height : 2.5rem;
justify-content : center;
margin : 1.5rem 0.5rem;
width : 2.5rem;
`;
const seasonStyle = `
font-family : cursive;
font-size : 3rem;
`;
const textStyle = `
color : mintcream;
font-family : monospace;
margin : 1rem;
text-shadow : 1px 1px #d2dde9;
`;
document.documentElement.innerHTML = `
<head>
<title id="TITLE"></title>
</head>
<body style="${ bodyStyle }">
<div style="${ buttonRowStyle }">
<button id="PREV">PREV</button>
<button id="NEXT">NEXT</button>
</div>
<div id="SEASON" style="${ textStyle }${ seasonStyle }"></div>
<div style="${ textStyle }${ episodeCountStyle }">
Episode
<span id="OVERALLEPISODENUMBER"></span>
of ${ allEpisodes.length }
(<span id="SEASONEPISODENUMBER"></span> of
<span id="SEASONEPISODECOUNT"></span> in season)
</div>
<div id="EPISODENAME" style="${ textStyle }${ episodeStyle }"></div>
<div style="${ progressStyle }${ progressBGStyle }">
<div id="PROGRESSBAR" style="${ progressNavStyle }">
<div id="PROGRESS" style="${ progressStyle }${ progressFGStyle }"></div>
</div>
</div>
<div id="SCOUTS"></div>
</body>
`;
// RENDER //////////////////////////////////////////////////////////////////////
const render = () => {
const episode = allEpisodes[state.index];
const textMapping = {
EPISODENAME: episode.name,
OVERALLEPISODENUMBER: state.index + 1,
SEASON: episode.season,
SEASONEPISODECOUNT: episode.seasonCount,
SEASONEPISODENUMBER: episode.seasonIndex + 1,
TITLE: `${ episode.season } ${ episode.seasonIndex + 1 }: ${ episode.name }`
};
for (const [ id, text ] of Object.entries(textMapping))
document.getElementById(id).innerHTML = text;
document.getElementById('PROGRESS').style.width =
`${ (state.index + 1) / allEpisodes.length * 100 }%`;
document.getElementById('SCOUTS').innerHTML = episode.scouts.map(scout => `
<div
title="${ scout.name }"
style="
${ scoutStyle }
background-image: linear-gradient(
to bottom right,
transparent 15%,
${ scout.color } 150%);
">
${ scout.symbol }
</div>
`).join('');
};
render();
// EVENTS: NAV BUTTONS /////////////////////////////////////////////////////////
const increment = inc => () => {
const newIndex = Math.min(
allEpisodes.length - 1,
Math.max(0, state.index + inc)
);
if (newIndex !== state.index) {
state.index = newIndex;
render();
}
};
document.getElementById('PREV').addEventListener('click', increment(-1));
document.getElementById('NEXT').addEventListener('click', increment(+1));
// EVENTS: BAR /////////////////////////////////////////////////////////////////
const bar = document.getElementById('PROGRESSBAR');
const goto = event => {
if (state.down) {
const position = event.pageX - bar.offsetLeft;
const divisor = bar.clientWidth / allEpisodes.length;
const newIndex = Math.min(
allEpisodes.length - 1,
Math.max(0, Math.round(position / divisor))
);
if (newIndex !== state.index) {
state.index = newIndex;
render();
}
}
};
const mousedown = () => {
state.down = true;
document.body.style.cursor = bar.style.cursor = 'col-resize';
goto(event);
};
const mouseup = () => {
state.down = false;
document.body.style.cursor = '';
bar.style.cursor = 'pointer';
};
document.addEventListener('blur', mouseup);
document.addEventListener('mouseup', mouseup);
document.addEventListener('mousemove', goto);
bar.addEventListener('mousedown', mousedown);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment