Met Gala Bracket JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Met Gala | |
* @author: Eoin Thomas O'Hehir [tom.ohehir@teamaol.com] | |
* @description: Met Gala page functionality | |
*/ | |
'use strict'; | |
/** | |
* Module prototype. | |
*/ | |
var metGala = exports = module.exports = function(app, sandbox, config) { | |
return { | |
config: { | |
'people': [], | |
'roundsArray': [], | |
'rounds': 0, | |
'matches': 0, | |
'currRound': 1, | |
'currMatch': 1, // The current match number out of the total matches available | |
'displayMatch': 1, // Based on what round it is – changes back to 1 every new round | |
'matchupString': '', | |
'matchupDetails': {} // Shows round number, the people in left/right positions and the person chosen | |
}, | |
init: function init(config) { | |
var self = this; | |
if (window.location.href.indexOf("aol.com/lifestyle/met-gala/bracket/") != -1) { | |
// If on met gala bracket display page | |
self.getPeople(); | |
} else if (window.location.href.indexOf("aol.com/lifestyle/met-gala/") != -1) { | |
// If on met gala bracket select page | |
self.getPeople(); | |
self.updateProgress(0); | |
} | |
}, | |
getViewportSize: function getViewportSize() { | |
var size = { | |
'height': window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, | |
'width' : window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth | |
} | |
return size; | |
}, | |
getPeople: function getPeople() { | |
var self = this, | |
initialBracket = {}; | |
initialBracket = document.getElementsByClassName('met-gala-person'); | |
// Loop through people and pull out index, title and image | |
for (var i = 0; i < initialBracket.length; i++) { | |
var index = initialBracket[i].getAttribute('data-index'), | |
title = initialBracket[i].getAttribute('data-title'), | |
image = initialBracket[i].getAttribute('data-image'), | |
designer = initialBracket[i].getAttribute('data-designer'), | |
attributes = { 'title': title, 'image': image, 'designer': designer, 'available': true }; | |
self.config.people.push({ | |
'index': index, | |
'attributes': attributes | |
}); | |
} | |
self.calcMatchesPerRound(self.config.people); | |
}, | |
calcMatchesPerRound: function calcMatchesPerRound(participants) { | |
var self = this, | |
count = 1; | |
self.config.matches = participants.length - 1; | |
for (var i = (participants.length / 2); i > 1; i = (i / 2) ) { | |
self.config.roundsArray.push( {'count': count, 'matches': i} ); | |
count++; | |
} | |
self.config.roundsArray.push( {'count': count, 'matches': 1} ); | |
self.config.rounds = self.config.roundsArray.length; | |
if (window.location.href.indexOf("aol.com/lifestyle/met-gala/bracket/") != -1) { | |
// If on met gala bracket display page | |
self.calcMatchupDetails(); | |
} else if (window.location.href.indexOf("aol.com/lifestyle/met-gala/") != -1) { | |
// If on met gala bracket select page | |
self.generateMatchupsString(self.config.matches); | |
self.generateSlider(); | |
} | |
}, | |
generateMatchupsString: function generateMatchupsString(matches) { | |
var self = this; | |
// Generates initial matchup string based on how many matches there are | |
for (var i = matches; i > 0; i--) { | |
self.config.matchupString += '0'; | |
} | |
}, | |
generateSlider: function generateSlider() { | |
var self = this; | |
$('.met-gala-bracket-slider').slick({ | |
arrows: false, | |
cssEase: 'cubic-bezier(0.230, 1.000, 0.320, 1.000)', | |
draggable: false, | |
ease: 'cubic-bezier(0.230, 1.000, 0.320, 1.000)', | |
fade: true, | |
infinite: false, | |
slidesToScroll: 1, | |
slidesToShow: 1 | |
}); | |
self.selectPeople(); | |
}, | |
selectPeople: function selectPeople() { | |
var self = this; | |
self.bracketClickListener(); | |
$('.met-gala-bracket-slider').on('beforeChange', function(event, slick, currentSlide, nextSlide) { | |
$('.met-gala-bracket-slider').off('click'); | |
self.updateProgress(nextSlide); | |
self.currentLocation(nextSlide); | |
}); | |
$('.met-gala-bracket-slider').on('afterChange', function(event, slick, currentSlide) { | |
// Wait to reassign click listener until after slide transition | |
self.bracketClickListener(); | |
}); | |
}, | |
bracketClickListener: function bracketClickListener() { | |
var self = this; | |
// Apply selected style to selected choice, remove availability from unselected choice, save selected choice to matchup string and localStorage, navigate to next slide | |
$('.met-gala-bracket-slider').on('click', function(e) { | |
var target = $(e.target).parent().parent('.met-gala-bracket-slider-slide-choice'); | |
if ( target.is(':first-child') && $(e.target).hasClass('met-gala-bracket-slider-slide-choice-target') ) { | |
if ( !target.parent().hasClass('choice-left-selected') && !target.parent().hasClass('choice-right-selected') ) { | |
target.parent().addClass('choice-left-selected'); | |
var peopleIndex = target.next('.met-gala-bracket-slider-slide-choice').data('index'); | |
self.config.people[peopleIndex - 1]['attributes']['available'] = false; | |
self.saveChoices(); | |
$('.met-gala-bracket-slider').slick('slickNext'); | |
} | |
} else if ( $(e.target).hasClass('met-gala-bracket-slider-slide-choice-target') ) { | |
if ( !target.parent().hasClass('choice-left-selected') && !target.parent().hasClass('choice-right-selected') ) { | |
target.parent().addClass('choice-right-selected'); | |
var peopleIndex = target.prev('.met-gala-bracket-slider-slide-choice').data('index'); | |
self.config.people[peopleIndex - 1]['attributes']['available'] = false; | |
self.saveChoices(); | |
$('.met-gala-bracket-slider').slick('slickNext'); | |
} | |
} | |
}); | |
}, | |
updateProgress: function updateProgress(upcomingSlide) { | |
var self = this; | |
// Update width of progress bar depending on how far along the user is in the matchups | |
var currentPosition = upcomingSlide + 1; | |
var totalMatchups = self.config.matches; | |
$('.met-gala-header-progress-bar').css({ width: ( (currentPosition / totalMatchups) * 100) + '%' }); | |
}, | |
currentLocation: function currentLocation(upcomingSlide) { | |
var self = this; | |
// Before navigating every slide, calculate the the upcoming round number and upcoming match number the user is located on, then update the numbers in header to reflect that. | |
var currentMatch = upcomingSlide + 1; | |
self.config.currMatch = upcomingSlide + 1; | |
self.config.displayMatch++; | |
if (self.config.displayMatch < 10) { | |
self.config.displayMatch = '0' + self.config.displayMatch; | |
} | |
// Update current match number | |
$('.matchup-current').text(self.config.displayMatch); | |
}, | |
saveChoices: function saveChoices() { | |
var self = this; | |
// Loop through slides, see which slides are selected and edit matchup string based on that. Then, save string to localStorage. | |
// 0 means unselected, 1 means the left choice is selected, 2 means the right choice is selected. | |
// This returns a value of "newRound" if the newRound function is triggered. | |
var slides = document.querySelectorAll('.met-gala-bracket-slider-slide'); | |
var slidesNum = slides.length; | |
for (var i = 0; i < slidesNum; i++ ) { | |
if ( $(slides[i]).hasClass('choice-left-selected') ) { | |
self.config.matchupString = self.config.matchupString.substr(0, i) + '1' + self.config.matchupString.substr(i + 1); | |
} else if ( $(slides[i]).hasClass('choice-right-selected') ) { | |
self.config.matchupString = self.config.matchupString.substr(0, i) + '2' + self.config.matchupString.substr(i + 1); | |
} | |
} | |
localStorage.setItem( 'aol-met-gala-matchups', self.config.matchupString ); | |
localStorage.getItem( 'aol-met-gala-matchups' ); | |
self.newRound(self.config.matchupString); | |
}, | |
newRound: function newRound(matchupString) { | |
var self = this; | |
// If user has made selections for every single item up to the current round, advance to next round with new selections | |
var currRound = self.config.currRound; | |
var currentMaxMatches = 0; | |
// Find the maximum number of matches possible, given current round | |
for (var i = 0; i < currRound; i++) { | |
var totalMatchesInRound = self.config.roundsArray[i]['matches']; | |
currentMaxMatches = currentMaxMatches + totalMatchesInRound; | |
} | |
// Perform indexOf on matchupString to find first instance of 0, to see if user has entered a response for all possible matchups so far | |
var firstZero = matchupString.indexOf('0'); | |
// If user has entered all possible responses so far, increment round number, display correct match number and match total, then add choices still available as new slides in slider | |
if (firstZero == currentMaxMatches) { | |
self.config.currRound++; | |
$('.round-number').text(self.config.currRound); | |
var newMatchTotal = self.config.roundsArray[self.config.currRound - 1]['matches']; | |
if (newMatchTotal < 10) { | |
newMatchTotal = '0' + newMatchTotal; | |
} | |
// Subtract previous round's match total from displayMatch to show proper display match number | |
self.config.displayMatch = self.config.displayMatch - self.config.roundsArray[self.config.currRound - 2]['matches']; | |
$('.matchup-total').text(newMatchTotal); | |
self.addNewRoundChoices(); | |
} else if (currentMaxMatches == self.config.matches && firstZero == -1) { | |
// If all possible responses are given for all matchups, navigate to bracket display page | |
var matchupString = localStorage.getItem( 'aol-met-gala-matchups' ); | |
// If on met gala page, send user to bracket display page | |
if (window.location.href.indexOf("aol.com/lifestyle/met-gala") != -1) { | |
window.location.href = './bracket/' + matchupString; | |
} | |
} | |
}, | |
addNewRoundChoices: function addNewRoundChoices() { | |
var self = this, | |
availablePeople = [], | |
slidesString = ''; | |
// Only run if user has future rounds to advance to | |
if (self.config.currRound <= self.config.rounds) { | |
// Get remaining available choices | |
for (var i = 0; i < self.config.people.length; i++) { | |
if (self.config.people[i]['attributes']['available'] === true) { | |
availablePeople.push( self.config.people[i] ); | |
} | |
} | |
// Loop through remaining available choices and add those slides to slider | |
for (var i = 0; i < availablePeople.length; i++) { | |
if (i % 2 == 0) { // If index is even, add opening slide div tag | |
slidesString += '<div class="met-gala-bracket-slider-slide">'; | |
} | |
slidesString += '<div class="met-gala-bracket-slider-slide-choice" data-index="' + availablePeople[i]['index'] + '">' + | |
'<div class="content">' + | |
'<div class="met-gala-bracket-slider-slide-choice-target"></div>' + | |
'<h3 class="met-gala-bracket-slider-slide-choice-title">' + availablePeople[i]['attributes']['title'] + '<br><span class="designer">'+ availablePeople[i]['attributes']['designer'] +'</span></h3>' + | |
'<div class="met-gala-bracket-slider-slide-choice-image" style="background-image: url(' + availablePeople[i]['attributes']['image'] + ');"></div>' + | |
'</div>' + | |
'</div>'; | |
if (i % 2 != 0) { // If index is odd, add vs circle and closing slide div tag | |
slidesString += '<div class="met-gala-bracket-slider-slide-vs">' + | |
'<div class="met-gala-bracket-slider-slide-vs-text">' + | |
'<span class="vs">VS</span>' + | |
'<span class="navicon navicon-heart"></span>' + | |
'</div>' + | |
'</div>'+ | |
'</div>'; | |
} | |
} | |
$('.met-gala-bracket-slider').slick('slickAdd', slidesString); | |
$('.met-gala-bracket-slider').slick('slickNext'); | |
} | |
}, | |
calcMatchupDetails: function calcMatchupDetails() { | |
var self = this; | |
// Calculates matchup details - round, people in matchup, person chosen in matchup - based on matchupString | |
var tempPeople = self.config.people; | |
var matchupSelections = $('.met-gala-people').data('selected').toString(); | |
var userMatchups = {}; | |
var matchupOffset = 0; | |
var matchupCount = 0; | |
var errorMessage = '<h3 class="met-gala-final-bracket-header-text-title">Something went wrong. Please go back and try again.</h3>' | |
if ( matchupSelections.length != self.config.matches || matchupSelections.search(/([\D]|[3-9]|[0])+/i) != -1 ) { | |
$('.met-gala-final-bracket-display').html(errorMessage); | |
return false; | |
} | |
// Round Loop | |
for (var i = 0; i < self.config.rounds; i++) { | |
// Match Loop | |
for (var j = 0; j < self.config.roundsArray[i]['matches']; j++) { | |
var leftOrRight = matchupSelections.charAt(matchupCount); | |
var leftPerson = tempPeople[matchupOffset]; | |
var rightPerson = tempPeople[matchupOffset + 1]; | |
var chosenPerson; | |
if (leftOrRight == 1) { | |
// Left is chosen | |
chosenPerson = leftPerson; | |
// Right is removed | |
tempPeople.splice(matchupOffset + 1, 1); | |
} else if (leftOrRight == 2) { | |
// Right is chosen | |
chosenPerson = rightPerson; | |
// Left is removed | |
tempPeople.splice(matchupOffset, 1); | |
} | |
userMatchups[ (matchupCount) ] = {}; | |
userMatchups[ (matchupCount) ]['round'] = i+1; | |
userMatchups[ (matchupCount) ]['match'] = j+1; | |
userMatchups[ (matchupCount) ]['left'] = leftPerson; | |
userMatchups[ (matchupCount) ]['right'] = rightPerson; | |
userMatchups[ (matchupCount) ]['chosen'] = chosenPerson; | |
matchupOffset++; | |
matchupCount++; | |
} | |
matchupOffset = 0; | |
} | |
self.config.matchupDetails = userMatchups; | |
self.writeBracket(); | |
}, | |
writeBracket: function writeBracket() { | |
var self = this, | |
htmlString = '', | |
roundCount = self.config.rounds + 1, | |
matchupDetails = self.config.matchupDetails; | |
for (var i = (self.config.matches - 1); i >= 0; i--) { | |
var currRound = matchupDetails[i]['round']; | |
if (currRound < roundCount) { | |
if (roundCount != self.config.rounds + 1) { | |
htmlString += '</div>'; | |
} | |
if (currRound == self.config.rounds) { | |
htmlString += '<div class="round round'+ (currRound) +' round-final">'; | |
htmlString += '<h3 class="round-title">Your Top Pick</h3>'; | |
} else { | |
htmlString += '<div class="round round'+ (currRound) +'">'; | |
htmlString += '<h3 class="round-title">Round '+ currRound +'</h3>'; | |
} | |
roundCount = currRound; | |
} | |
htmlString += '<div class="match">' + | |
'<div class="vs"><span class="vs-text">VS</span></div>'; | |
if (matchupDetails[i]['left'] == matchupDetails[i]['chosen']) { | |
htmlString += '<div class="left chosen">'; | |
} else { | |
htmlString += '<div class="left">'; | |
} | |
htmlString += '<img class="image" src="'+ matchupDetails[i]['left']['attributes']['image'] +'" alt=""/>' + | |
'<div class="title">'+ matchupDetails[i]['left']['attributes']['title'] +'</div>' + | |
'</div>' ; | |
if (matchupDetails[i]['right'] == matchupDetails[i]['chosen']) { | |
htmlString += '<div class="right chosen">'; | |
} else { | |
htmlString += '<div class="right">'; | |
} | |
htmlString += '<img class="image" src="'+ matchupDetails[i]['right']['attributes']['image'] +'" alt=""/>' + | |
'<div class="title">'+ matchupDetails[i]['right']['attributes']['title'] +'</div>' + | |
'</div>' + | |
'</div>'; | |
if (i == 0) { | |
htmlString += '</div>'; | |
} | |
} | |
$('.met-gala-final-bracket-display').html(htmlString); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment