├── .gitignore ├── Procfile ├── README.md ├── api_data ├── champions.json ├── dd_patch.json ├── index.js ├── items.json ├── masteries.json ├── runes.js ├── skills.json └── summoners.json ├── app.js ├── bash.txt ├── bin ├── update_server.sh └── www.js ├── config └── config.js ├── db.js ├── db └── championgg │ ├── webchampionpages.bson │ ├── webchampionpages.metadata.json │ ├── webchampionroles.bson │ ├── webchampionroles.metadata.json │ ├── webhomepagesummaries.bson │ ├── webhomepagesummaries.metadata.json │ ├── webmatchuppages.bson │ ├── webmatchuppages.metadata.json │ ├── weboverallroledatas.bson │ ├── weboverallroledatas.metadata.json │ ├── weboverallstats.bson │ ├── weboverallstats.metadata.json │ ├── webstatisticspages.bson │ └── webstatisticspages.metadata.json ├── gruntfile.js ├── headline.js ├── logic ├── lower_case_champ.js ├── produce_error.js └── role_hash_table.js ├── middleware └── overall_data.js ├── models ├── web_champion_page.js ├── web_champion_roles.js ├── web_home_page_summaries.js ├── web_matchup_page.js ├── web_overall_role_data.js ├── web_overall_stats.js └── web_statistics_page.js ├── package.json ├── public ├── cpmstar │ └── cpmstar_siteskin_iframebuster.html ├── css │ ├── master.css │ ├── master.min.css │ └── sprite.css ├── dist │ ├── css │ │ ├── bootstrap.min.css │ │ └── jquery-ui.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ └── js │ │ ├── angular-bootstrap.js │ │ ├── angular.js │ │ ├── chart.js │ │ ├── dirDisqus.js │ │ └── tc-angular-chartjs.js ├── favicon.ico ├── googled8153283379da1fb.html ├── img │ ├── bg.jpg │ ├── champion.jpg │ ├── header-bg.jpg │ ├── logo.png │ ├── mastery0.jpg │ ├── mastery1.jpg │ ├── mastery2.jpg │ └── small_champion.jpg ├── js │ ├── app.js │ ├── champion_data.js │ ├── champion_page.js │ ├── championgg_tooltip.js │ ├── chart_options.js │ ├── master.min.js │ ├── matchup_page.js │ ├── statistics_jquery.js │ └── statistics_page.js ├── opensearchdescription.xml ├── riot.html └── template │ └── typeahead │ └── typeahead-popup.html ├── routes ├── api_static.js ├── champion.js ├── faq.js ├── index.js ├── matchup.js ├── matchup_json.js └── statistics.js ├── update_data.sh ├── update_server.sh └── views ├── champion.ejs ├── champion ├── advertisement.ejs ├── champion_image_roles.ejs ├── champion_statistics.ejs ├── core_build.ejs ├── counters_matchups.ejs ├── first_items.ejs ├── gamelength_experience_summoners.ejs ├── masteries.ejs ├── reddit.ejs ├── runes.ejs ├── skill_order.ejs ├── viktor_upgrade.ejs └── winrate_playrate_damage_advert_trinket.ejs ├── error.ejs ├── faq.ejs ├── footer.ejs ├── header.ejs ├── index.ejs ├── matchup.ejs ├── new_champion.ejs ├── scripts.ejs └── statistics.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | # Logs 3 | logs 4 | *.log 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 28 | node_modules 29 | 30 | 31 | ### Intellij ### 32 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 33 | 34 | *.iml 35 | 36 | ## Directory-based project format: 37 | .idea/ 38 | # if you remove the above rule, at least ignore the following: 39 | 40 | # User-specific stuff: 41 | # .idea/workspace.xml 42 | # .idea/tasks.xml 43 | # .idea/dictionaries 44 | 45 | # Sensitive or high-churn files: 46 | # .idea/dataSources.ids 47 | # .idea/dataSources.xml 48 | # .idea/sqlDataSources.xml 49 | # .idea/dynamic.xml 50 | # .idea/uiDesigner.xml 51 | 52 | # Gradle: 53 | # .idea/gradle.xml 54 | # .idea/libraries 55 | 56 | # Mongo Explorer plugin: 57 | # .idea/mongoSettings.xml 58 | 59 | ## File-based project format: 60 | *.ipr 61 | *.iws 62 | 63 | ## Plugin-specific files: 64 | 65 | # IntelliJ 66 | out/ 67 | 68 | # mpeltonen/sbt-idea plugin 69 | .idea_modules/ 70 | 71 | # JIRA plugin 72 | atlassian-ide-plugin.xml 73 | 74 | # Crashlytics plugin (for Android Studio and IntelliJ) 75 | com_crashlytics_export_strings.xml 76 | crashlytics.properties 77 | crashlytics-build.properties 78 | 79 | 80 | ### SublimeText ### 81 | # cache files for sublime text 82 | *.tmlanguage.cache 83 | *.tmPreferences.cache 84 | *.stTheme.cache 85 | 86 | # workspace files are user-specific 87 | *.sublime-workspace 88 | 89 | # project files should be checked into the repository, unless a significant 90 | # proportion of contributors will probably not be using SublimeText 91 | # *.sublime-project 92 | 93 | # sftp configuration file 94 | sftp-config.json 95 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: node worker.js 2 | web: npm start 3 | clock: node clock.js 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | champion.gg 2 | ======== 3 | WEBSITE: http://champion.gg 4 | 5 | A MEAN project (with a dash of angular). 6 | In order to get a local version of champion.gg running you need to have MongoDb, Node and NPM installed. (ensure MongoDB is running when trying to run champion.gg) 7 | 8 | To get a working version set up you'll need to clone the repo, install the dependencies, build the database and then start the server from the command line. 9 | The commands to enter are listed below. 10 | 11 | # Getting set up 12 | 13 | Clone champion.gg: 14 | ```sh 15 | git clone https://github.com/joel1st/championweb.git 16 | ``` 17 | 18 | Install dependencies from project directory: 19 | ```sh 20 | npm install 21 | ``` 22 | 23 | Restore database from project directory 24 | ```sh 25 | mongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson 26 | mongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson 27 | mongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson 28 | mongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson 29 | mongorestore --db championgg --collection weboverallstats --drop db/championgg/weboverallstats.bson 30 | mongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson 31 | mongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson 32 | ``` 33 | 34 | Start Champion.gg 35 | ```sh 36 | npm start 37 | #if you have another web server running on port 80 you can set the port as such 38 | PORT=8888 npm start 39 | ``` 40 | You can now access champion.gg on http://localhost/ or if you set a port number http://localhost:8888/ 41 | 42 | # Development 43 | 44 | In order to work on champion.gg more effectively I've created a grunt tasks to facilitate automation of javascript hinting (helps avoid nasty javascript errors). 45 | ```sh 46 | grunt watch 47 | ``` 48 | 49 | To get assets ready for production: 50 | ```sh 51 | grunt production 52 | ``` 53 | -------------------------------------------------------------------------------- /api_data/champions.json: -------------------------------------------------------------------------------- 1 | {"Aatrox":{"id":266,"key":"Aatrox","name":"Aatrox","title":"the Darkin Blade"},"Ahri":{"id":103,"key":"Ahri","name":"Ahri","title":"the Nine-Tailed Fox"},"Akali":{"id":84,"key":"Akali","name":"Akali","title":"the Fist of Shadow"},"Alistar":{"id":12,"key":"Alistar","name":"Alistar","title":"the Minotaur"},"Amumu":{"id":32,"key":"Amumu","name":"Amumu","title":"the Sad Mummy"},"Anivia":{"id":34,"key":"Anivia","name":"Anivia","title":"the Cryophoenix"},"Annie":{"id":1,"key":"Annie","name":"Annie","title":"the Dark Child"},"Ashe":{"id":22,"key":"Ashe","name":"Ashe","title":"the Frost Archer"},"AurelionSol":{"id":136,"key":"AurelionSol","name":"Aurelion Sol","title":"The Star Forger"},"Azir":{"id":268,"key":"Azir","name":"Azir","title":"the Emperor of the Sands"},"Bard":{"id":432,"key":"Bard","name":"Bard","title":"the Wandering Caretaker"},"Blitzcrank":{"id":53,"key":"Blitzcrank","name":"Blitzcrank","title":"the Great Steam Golem"},"Brand":{"id":63,"key":"Brand","name":"Brand","title":"the Burning Vengeance"},"Braum":{"id":201,"key":"Braum","name":"Braum","title":"the Heart of the Freljord"},"Caitlyn":{"id":51,"key":"Caitlyn","name":"Caitlyn","title":"the Sheriff of Piltover"},"Camille":{"id":164,"key":"Camille","name":"Camille","title":"the Steel Shadow"},"Cassiopeia":{"id":69,"key":"Cassiopeia","name":"Cassiopeia","title":"the Serpent's Embrace"},"Chogath":{"id":31,"key":"Chogath","name":"Cho'Gath","title":"the Terror of the Void"},"Corki":{"id":42,"key":"Corki","name":"Corki","title":"the Daring Bombardier"},"Darius":{"id":122,"key":"Darius","name":"Darius","title":"the Hand of Noxus"},"Diana":{"id":131,"key":"Diana","name":"Diana","title":"Scorn of the Moon"},"Draven":{"id":119,"key":"Draven","name":"Draven","title":"the Glorious Executioner"},"DrMundo":{"id":36,"key":"DrMundo","name":"Dr. Mundo","title":"the Madman of Zaun"},"Ekko":{"id":245,"key":"Ekko","name":"Ekko","title":"the Boy Who Shattered Time"},"Elise":{"id":60,"key":"Elise","name":"Elise","title":"the Spider Queen"},"Evelynn":{"id":28,"key":"Evelynn","name":"Evelynn","title":"the Widowmaker"},"Ezreal":{"id":81,"key":"Ezreal","name":"Ezreal","title":"the Prodigal Explorer"},"Fiddlesticks":{"id":9,"key":"Fiddlesticks","name":"Fiddlesticks","title":"the Harbinger of Doom"},"Fiora":{"id":114,"key":"Fiora","name":"Fiora","title":"the Grand Duelist"},"Fizz":{"id":105,"key":"Fizz","name":"Fizz","title":"the Tidal Trickster"},"Galio":{"id":3,"key":"Galio","name":"Galio","title":"the Colossus"},"Gangplank":{"id":41,"key":"Gangplank","name":"Gangplank","title":"the Saltwater Scourge"},"Garen":{"id":86,"key":"Garen","name":"Garen","title":"The Might of Demacia"},"Gnar":{"id":150,"key":"Gnar","name":"Gnar","title":"the Missing Link"},"Gragas":{"id":79,"key":"Gragas","name":"Gragas","title":"the Rabble Rouser"},"Graves":{"id":104,"key":"Graves","name":"Graves","title":"the Outlaw"},"Hecarim":{"id":120,"key":"Hecarim","name":"Hecarim","title":"the Shadow of War"},"Heimerdinger":{"id":74,"key":"Heimerdinger","name":"Heimerdinger","title":"the Revered Inventor"},"Illaoi":{"id":420,"key":"Illaoi","name":"Illaoi","title":"the Kraken Priestess"},"Irelia":{"id":39,"key":"Irelia","name":"Irelia","title":"the Will of the Blades"},"Ivern":{"id":427,"key":"Ivern","name":"Ivern","title":"the Green Father"},"Janna":{"id":40,"key":"Janna","name":"Janna","title":"the Storm's Fury"},"JarvanIV":{"id":59,"key":"JarvanIV","name":"Jarvan IV","title":"the Exemplar of Demacia"},"Jax":{"id":24,"key":"Jax","name":"Jax","title":"Grandmaster at Arms"},"Jayce":{"id":126,"key":"Jayce","name":"Jayce","title":"the Defender of Tomorrow"},"Jhin":{"id":202,"key":"Jhin","name":"Jhin","title":"the Virtuoso"},"Jinx":{"id":222,"key":"Jinx","name":"Jinx","title":"the Loose Cannon"},"Kalista":{"id":429,"key":"Kalista","name":"Kalista","title":"the Spear of Vengeance"},"Karma":{"id":43,"key":"Karma","name":"Karma","title":"the Enlightened One"},"Karthus":{"id":30,"key":"Karthus","name":"Karthus","title":"the Deathsinger"},"Kassadin":{"id":38,"key":"Kassadin","name":"Kassadin","title":"the Void Walker"},"Katarina":{"id":55,"key":"Katarina","name":"Katarina","title":"the Sinister Blade"},"Kayle":{"id":10,"key":"Kayle","name":"Kayle","title":"The Judicator"},"Kennen":{"id":85,"key":"Kennen","name":"Kennen","title":"the Heart of the Tempest"},"Khazix":{"id":121,"key":"Khazix","name":"Kha'Zix","title":"the Voidreaver"},"Kindred":{"id":203,"key":"Kindred","name":"Kindred","title":"The Eternal Hunters"},"Kled":{"id":240,"key":"Kled","name":"Kled","title":"the Cantankerous Cavalier"},"KogMaw":{"id":96,"key":"KogMaw","name":"Kog'Maw","title":"the Mouth of the Abyss"},"Leblanc":{"id":7,"key":"Leblanc","name":"LeBlanc","title":"the Deceiver"},"LeeSin":{"id":64,"key":"LeeSin","name":"Lee Sin","title":"the Blind Monk"},"Leona":{"id":89,"key":"Leona","name":"Leona","title":"the Radiant Dawn"},"Lissandra":{"id":127,"key":"Lissandra","name":"Lissandra","title":"the Ice Witch"},"Lucian":{"id":236,"key":"Lucian","name":"Lucian","title":"the Purifier"},"Lulu":{"id":117,"key":"Lulu","name":"Lulu","title":"the Fae Sorceress"},"Lux":{"id":99,"key":"Lux","name":"Lux","title":"the Lady of Luminosity"},"Malphite":{"id":54,"key":"Malphite","name":"Malphite","title":"Shard of the Monolith"},"Malzahar":{"id":90,"key":"Malzahar","name":"Malzahar","title":"the Prophet of the Void"},"Maokai":{"id":57,"key":"Maokai","name":"Maokai","title":"the Twisted Treant"},"MasterYi":{"id":11,"key":"MasterYi","name":"Master Yi","title":"the Wuju Bladesman"},"MissFortune":{"id":21,"key":"MissFortune","name":"Miss Fortune","title":"the Bounty Hunter"},"MonkeyKing":{"id":62,"key":"MonkeyKing","name":"Wukong","title":"the Monkey King"},"Mordekaiser":{"id":82,"key":"Mordekaiser","name":"Mordekaiser","title":"the Iron Revenant"},"Morgana":{"id":25,"key":"Morgana","name":"Morgana","title":"Fallen Angel"},"Nami":{"id":267,"key":"Nami","name":"Nami","title":"the Tidecaller"},"Nasus":{"id":75,"key":"Nasus","name":"Nasus","title":"the Curator of the Sands"},"Nautilus":{"id":111,"key":"Nautilus","name":"Nautilus","title":"the Titan of the Depths"},"Nidalee":{"id":76,"key":"Nidalee","name":"Nidalee","title":"the Bestial Huntress"},"Nocturne":{"id":56,"key":"Nocturne","name":"Nocturne","title":"the Eternal Nightmare"},"Nunu":{"id":20,"key":"Nunu","name":"Nunu","title":"the Yeti Rider"},"Olaf":{"id":2,"key":"Olaf","name":"Olaf","title":"the Berserker"},"Orianna":{"id":61,"key":"Orianna","name":"Orianna","title":"the Lady of Clockwork"},"Pantheon":{"id":80,"key":"Pantheon","name":"Pantheon","title":"the Artisan of War"},"Poppy":{"id":78,"key":"Poppy","name":"Poppy","title":"Keeper of the Hammer"},"Quinn":{"id":133,"key":"Quinn","name":"Quinn","title":"Demacia's Wings"},"Rakan":{"id":497,"key":"Rakan","name":"Rakan","title":"The Charmer"},"Rammus":{"id":33,"key":"Rammus","name":"Rammus","title":"the Armordillo"},"RekSai":{"id":421,"key":"RekSai","name":"Rek'Sai","title":"the Void Burrower"},"Renekton":{"id":58,"key":"Renekton","name":"Renekton","title":"the Butcher of the Sands"},"Rengar":{"id":107,"key":"Rengar","name":"Rengar","title":"the Pridestalker"},"Riven":{"id":92,"key":"Riven","name":"Riven","title":"the Exile"},"Rumble":{"id":68,"key":"Rumble","name":"Rumble","title":"the Mechanized Menace"},"Ryze":{"id":13,"key":"Ryze","name":"Ryze","title":"the Rune Mage"},"Sejuani":{"id":113,"key":"Sejuani","name":"Sejuani","title":"Fury of the North"},"Shaco":{"id":35,"key":"Shaco","name":"Shaco","title":"the Demon Jester"},"Shen":{"id":98,"key":"Shen","name":"Shen","title":"the Eye of Twilight"},"Shyvana":{"id":102,"key":"Shyvana","name":"Shyvana","title":"the Half-Dragon"},"Singed":{"id":27,"key":"Singed","name":"Singed","title":"the Mad Chemist"},"Sion":{"id":14,"key":"Sion","name":"Sion","title":"The Undead Juggernaut"},"Sivir":{"id":15,"key":"Sivir","name":"Sivir","title":"the Battle Mistress"},"Skarner":{"id":72,"key":"Skarner","name":"Skarner","title":"the Crystal Vanguard"},"Sona":{"id":37,"key":"Sona","name":"Sona","title":"Maven of the Strings"},"Soraka":{"id":16,"key":"Soraka","name":"Soraka","title":"the Starchild"},"Swain":{"id":50,"key":"Swain","name":"Swain","title":"the Master Tactician"},"Syndra":{"id":134,"key":"Syndra","name":"Syndra","title":"the Dark Sovereign"},"TahmKench":{"id":223,"key":"TahmKench","name":"Tahm Kench","title":"the River King"},"Taliyah":{"id":163,"key":"Taliyah","name":"Taliyah","title":"the Stoneweaver"},"Talon":{"id":91,"key":"Talon","name":"Talon","title":"the Blade's Shadow"},"Taric":{"id":44,"key":"Taric","name":"Taric","title":"the Shield of Valoran"},"Teemo":{"id":17,"key":"Teemo","name":"Teemo","title":"the Swift Scout"},"Thresh":{"id":412,"key":"Thresh","name":"Thresh","title":"the Chain Warden"},"Tristana":{"id":18,"key":"Tristana","name":"Tristana","title":"the Yordle Gunner"},"Trundle":{"id":48,"key":"Trundle","name":"Trundle","title":"the Troll King"},"Tryndamere":{"id":23,"key":"Tryndamere","name":"Tryndamere","title":"the Barbarian King"},"TwistedFate":{"id":4,"key":"TwistedFate","name":"Twisted Fate","title":"the Card Master"},"Twitch":{"id":29,"key":"Twitch","name":"Twitch","title":"the Plague Rat"},"Udyr":{"id":77,"key":"Udyr","name":"Udyr","title":"the Spirit Walker"},"Urgot":{"id":6,"key":"Urgot","name":"Urgot","title":"the Headsman's Pride"},"Varus":{"id":110,"key":"Varus","name":"Varus","title":"the Arrow of Retribution"},"Vayne":{"id":67,"key":"Vayne","name":"Vayne","title":"the Night Hunter"},"Veigar":{"id":45,"key":"Veigar","name":"Veigar","title":"the Tiny Master of Evil"},"Velkoz":{"id":161,"key":"Velkoz","name":"Vel'Koz","title":"the Eye of the Void"},"Vi":{"id":254,"key":"Vi","name":"Vi","title":"the Piltover Enforcer"},"Viktor":{"id":112,"key":"Viktor","name":"Viktor","title":"the Machine Herald"},"Vladimir":{"id":8,"key":"Vladimir","name":"Vladimir","title":"the Crimson Reaper"},"Volibear":{"id":106,"key":"Volibear","name":"Volibear","title":"the Thunder's Roar"},"Warwick":{"id":19,"key":"Warwick","name":"Warwick","title":"the Uncaged Wrath of Zaun"},"Xayah":{"id":498,"key":"Xayah","name":"Xayah","title":"the Rebel"},"Xerath":{"id":101,"key":"Xerath","name":"Xerath","title":"the Magus Ascendant"},"XinZhao":{"id":5,"key":"XinZhao","name":"Xin Zhao","title":"the Seneschal of Demacia"},"Yasuo":{"id":157,"key":"Yasuo","name":"Yasuo","title":"the Unforgiven"},"Yorick":{"id":83,"key":"Yorick","name":"Yorick","title":"Shepherd of Souls"},"Zac":{"id":154,"key":"Zac","name":"Zac","title":"the Secret Weapon"},"Zed":{"id":238,"key":"Zed","name":"Zed","title":"the Master of Shadows"},"Ziggs":{"id":115,"key":"Ziggs","name":"Ziggs","title":"the Hexplosives Expert"},"Zilean":{"id":26,"key":"Zilean","name":"Zilean","title":"the Chronokeeper"},"Zyra":{"id":143,"key":"Zyra","name":"Zyra","title":"Rise of the Thorns"}} -------------------------------------------------------------------------------- /api_data/dd_patch.json: -------------------------------------------------------------------------------- 1 | {"ddPatch":"7.11.1"} -------------------------------------------------------------------------------- /api_data/index.js: -------------------------------------------------------------------------------- 1 | var items = require('./items'); 2 | var masteries = require('./masteries.json'); 3 | var runes = require('./runes'); 4 | var skills = require('./skills'); 5 | var summoners = require('./summoners'); 6 | 7 | module.exports = { 8 | items: items, 9 | masteries: masteries, 10 | runes: runes, 11 | skills: skills, 12 | summoners: summoners 13 | }; -------------------------------------------------------------------------------- /api_data/masteries.json: -------------------------------------------------------------------------------- 1 | {"6111":{"id":6111,"name":"Fury","description":["+0.8% Attack Speed","+1.6% Attack Speed","+2.4% Attack Speed","+3.2% Attack Speed","+4% Attack Speed"],"image":{"full":"6111.png","sprite":"mastery0.png","group":"mastery","x":0,"y":0,"w":48,"h":48},"ranks":5,"prereq":"0"},"6114":{"id":6114,"name":"Sorcery","description":["+0.4% increased Ability damage","+0.8% increased Ability damage","+1.2% increased Ability damage","+1.6% increased Ability damage","+2.0% increased Ability damage"],"image":{"full":"6114.png","sprite":"mastery0.png","group":"mastery","x":48,"y":0,"w":48,"h":48},"ranks":5,"prereq":"0"},"6121":{"id":6121,"name":"Fresh Blood","description":["Your first basic attack against a champion deals an additional 10 +1 per level damage (6 second cooldown)"],"image":{"full":"6121.png","sprite":"mastery0.png","group":"mastery","x":96,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6122":{"id":6122,"name":"Feast","description":["Killing a unit restores 20 Health (30 second cooldown)"],"image":{"full":"6122.png","sprite":"mastery0.png","group":"mastery","x":144,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6123":{"id":6123,"name":"Expose Weakness","description":["Damaging enemy champions causes them to take 3% more damage from your allies"],"image":{"full":"6123.png","sprite":"mastery0.png","group":"mastery","x":192,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6131":{"id":6131,"name":"Vampirism","description":["+0.4% Lifesteal and Spell Vamp","+0.8% Lifesteal and Spell Vamp","+1.2% Lifesteal and Spell Vamp","+1.6% Lifesteal and Spell Vamp","+2.0% Lifesteal and Spell Vamp"],"image":{"full":"6131.png","sprite":"mastery0.png","group":"mastery","x":240,"y":0,"w":48,"h":48},"ranks":5,"prereq":"0"},"6134":{"id":6134,"name":"Natural Talent","description":["Gain 0.4 + 0.09 per level Attack Damage, and 0.6 + 0.13 per level Ability Power (+2 Attack Damage and 3 Ability Power at level 18)","Gain 0.8 + 0.18 per level Attack Damage, and 1.2 + 0.27 per level Ability Power (+4 Attack Damage and 6 Ability Power at level 18)","Gain 1.2 + 0.27 per level Attack Damage, and 1.8 + 0.4 per level Ability Power (+6 Attack Damage and 9 Ability Power at level 18)","Gain 1.6 + 0.36 per level Attack Damage, and 2.4 + 0.53 per level Ability Power (+8 Attack Damage and 12 Ability Power at level 18)","Gain 2 + 0.44 per level Attack Damage, and 3 + 0.67 per level Ability Power (+10 Attack Damage and 15 Ability Power at level 18)"],"image":{"full":"6134.png","sprite":"mastery0.png","group":"mastery","x":288,"y":0,"w":48,"h":48},"ranks":5,"prereq":"0"},"6141":{"id":6141,"name":"Bounty Hunter","description":["Deal 1% increased damage for each unique enemy champion you have killed"],"image":{"full":"6141.png","sprite":"mastery0.png","group":"mastery","x":336,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6142":{"id":6142,"name":"Double Edged Sword","description":["Deal 3% additional damage, take 1.5% additional damage."],"image":{"full":"6142.png","sprite":"mastery0.png","group":"mastery","x":384,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6143":{"id":6143,"name":"Battle Trance","description":["Gain up to 3% increased damage over 3 seconds when in combat with enemy Champions"],"image":{"full":"6143.png","sprite":"mastery0.png","group":"mastery","x":432,"y":0,"w":48,"h":48},"ranks":1,"prereq":"0"},"6151":{"id":6151,"name":"Battering Blows","description":["+1.4% Armor Penetration","+2.8% Armor Penetration","+4.2% Armor Penetration","+5.6% Armor Penetration","+7% Armor Penetration"],"image":{"full":"6151.png","sprite":"mastery0.png","group":"mastery","x":0,"y":48,"w":48,"h":48},"ranks":5,"prereq":"0"},"6154":{"id":6154,"name":"Piercing Thoughts","description":["+1.4% Magic Penetration","+2.8% Magic Penetration","+4.2% Magic Penetration","+5.6% Magic Penetration","+7% Magic Penetration"],"image":{"full":"6154.png","sprite":"mastery0.png","group":"mastery","x":48,"y":48,"w":48,"h":48},"ranks":5,"prereq":"0"},"6161":{"id":6161,"name":"Warlord's Bloodlust","description":["Moving or attacking will charge an Energized attack. Energized attacks heal for 5-40% of your total Attack Damage (amplified by Critical Strikes) and grant 30% Movement Speed for 0.75 seconds."],"image":{"full":"6161.png","sprite":"mastery0.png","group":"mastery","x":96,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6162":{"id":6162,"name":"Fervor of Battle","description":["Hitting champions with basic attacks generates a Fervor stack (2 for melee attacks). Stacks of Fervor last 8 seconds (max 8 stacks)and increase your AD by 1-8 for each stack."],"image":{"full":"6162.png","sprite":"mastery0.png","group":"mastery","x":144,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6164":{"id":6164,"name":"Deathfire Touch","description":["Your damaging abilities cause enemy champions to take magic damage over 4 seconds.

Damage: 8 + 45% Bonus Attack Damage and 25% Ability Power

Deathfire Touch's duration is reduced for:
- Area of Effect: 2 second duration.
- Damage over Time: 1 second duration."],"image":{"full":"6164.png","sprite":"mastery0.png","group":"mastery","x":192,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6211":{"id":6211,"name":"Recovery","description":["+0.4 Health per 5 seconds","+0.8 Health per 5 seconds","+1.2 Health per 5 seconds","+1.6 Health per 5 seconds","+2.0 Health per 5 seconds"],"image":{"full":"6211.png","sprite":"mastery0.png","group":"mastery","x":0,"y":144,"w":48,"h":48},"ranks":5,"prereq":"0"},"6212":{"id":6212,"name":"Unyielding","description":["+1% Bonus Armor and Magic Resist","+2% Bonus Armor and Magic Resist","+3% Bonus Armor and Magic Resist","+4% Bonus Armor and Magic Resist","+5% Bonus Armor and Magic Resist"],"image":{"full":"6212.png","sprite":"mastery0.png","group":"mastery","x":48,"y":144,"w":48,"h":48},"ranks":5,"prereq":"0"},"6221":{"id":6221,"name":"Explorer","description":["+15 Movement Speed in Brush and River"],"image":{"full":"6221.png","sprite":"mastery0.png","group":"mastery","x":96,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6222":{"id":6222,"name":"Siegemaster","description":["Gain 8 Armor and Magic Resistance when near an allied tower"],"image":{"full":"6222.png","sprite":"mastery0.png","group":"mastery","x":192,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6223":{"id":6223,"name":"Tough Skin","description":["You take 2 less damage from champion and neutral monster basic attacks"],"image":{"full":"6223.png","sprite":"mastery0.png","group":"mastery","x":144,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6231":{"id":6231,"name":"Runic Armor","description":["Shields, healing, regeneration, and lifesteal on you are 1.6% stronger","Shields, healing, regeneration, and lifesteal on you are 3.2% stronger","Shields, healing, regeneration, and lifesteal on you are 4.8% stronger","Shields, healing, regeneration, and lifesteal on you are 6.4% stronger","Shields, healing, regeneration, and lifesteal on you are 8% stronger"],"image":{"full":"6231.png","sprite":"mastery0.png","group":"mastery","x":240,"y":144,"w":48,"h":48},"ranks":5,"prereq":"0"},"6232":{"id":6232,"name":"Veteran's Scars","description":["+10 Health","+20 Health","+30 Health","+40 Health","+50 Health"],"image":{"full":"6232.png","sprite":"mastery0.png","group":"mastery","x":288,"y":144,"w":48,"h":48},"ranks":5,"prereq":"0"},"6241":{"id":6241,"name":"Insight","description":["Reduces the cooldown of Summoner Spells by 15%"],"image":{"full":"6241.png","sprite":"mastery0.png","group":"mastery","x":336,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6242":{"id":6242,"name":"Perseverance","description":["+50% Base Health Regen, increased to +200% when below 25% Health"],"image":{"full":"6242.png","sprite":"mastery0.png","group":"mastery","x":384,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6243":{"id":6243,"name":"Fearless","description":["Gain 10% +1.5 per level bonus Armor and Magic Resist when damaged by an enemy champion for 2 seconds (9s Cooldown)"],"image":{"full":"6243.png","sprite":"mastery0.png","group":"mastery","x":432,"y":144,"w":48,"h":48},"ranks":1,"prereq":"0"},"6251":{"id":6251,"name":"Swiftness","description":["+3% Tenacity and Slow Resist","+6% Tenacity and Slow Resist","+9% Tenacity and Slow Resist","+12% Tenacity and Slow Resist","+15% Tenacity and Slow Resist"],"image":{"full":"6251.png","sprite":"mastery0.png","group":"mastery","x":0,"y":192,"w":48,"h":48},"ranks":5,"prereq":"0"},"6252":{"id":6252,"name":"Legendary Guardian","description":["+0.6 Armor and Magic Resist for each nearby enemy champion","+1.2 Armor and Magic Resist for each nearby enemy champion","+1.8 Armor and Magic Resist for each nearby enemy champion","+2.4 Armor and Magic Resist for each nearby enemy champion","+3 Armor and Magic Resist for each nearby enemy champion"],"image":{"full":"6252.png","sprite":"mastery0.png","group":"mastery","x":48,"y":192,"w":48,"h":48},"ranks":5,"prereq":"0"},"6261":{"id":6261,"name":"Grasp of the Undying","description":["Every 4 seconds in combat, your next attack against an enemy champion deals damage equal to 3% of your max Health and heals you for 1.5% of your max Health (halved for ranged champions, deals magic damage)"],"image":{"full":"6261.png","sprite":"mastery0.png","group":"mastery","x":96,"y":192,"w":48,"h":48},"ranks":1,"prereq":"0"},"6262":{"id":6262,"name":"Courage of the Colossus","description":["Gain a shield for 3-54 (+5% of your maximum health) for each nearby enemy champion for 3 seconds after hitting an enemy champion with a stun, taunt, snare, or knock up (45-30 second cooldown, based on level)."],"image":{"full":"6262.png","sprite":"mastery0.png","group":"mastery","x":144,"y":192,"w":48,"h":48},"ranks":1,"prereq":"0"},"6263":{"id":6263,"name":"Stoneborn Pact","description":["Gain 5% total health.
Your movement impairing effects brand enemy champions with an earthen rune for 4 seconds. Other allied champions who attack branded enemies heal for 5 + 2.5% of your maximum health over 2 seconds (halved if you are ranged)."],"image":{"full":"6263.png","sprite":"mastery0.png","group":"mastery","x":192,"y":192,"w":48,"h":48},"ranks":1,"prereq":"0"},"6311":{"id":6311,"name":"Wanderer","description":["+0.6% Movement Speed out of combat","+1.2% Movement Speed out of combat","+1.8% Movement Speed out of combat","+2.4% Movement Speed out of combat","+3% Movement Speed out of combat"],"image":{"full":"6311.png","sprite":"mastery0.png","group":"mastery","x":240,"y":48,"w":48,"h":48},"ranks":5,"prereq":"0"},"6312":{"id":6312,"name":"Savagery","description":["Single target attacks and spells deal 1 bonus damage to minions and monsters","Single target attacks and spells deal 2 bonus damage to minions and monsters","Single target attacks and spells deal 3 bonus damage to minions and monsters","Single target attacks and spells deal 4 bonus damage to minions and monsters","Single target attacks and spells deal 5 bonus damage to minions and monsters"],"image":{"full":"6312.png","sprite":"mastery0.png","group":"mastery","x":288,"y":48,"w":48,"h":48},"ranks":5,"prereq":"0"},"6321":{"id":6321,"name":"Runic Affinity","description":["Buffs from neutral monsters last 15% longer"],"image":{"full":"6321.png","sprite":"mastery0.png","group":"mastery","x":336,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6322":{"id":6322,"name":"Secret Stash","description":["Your Potions and Elixirs last 10% longer.

Your Health Potions are replaced with Biscuits that restore 15 Health and Mana instantly on use"],"image":{"full":"6322.png","sprite":"mastery0.png","group":"mastery","x":384,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6323":{"id":6323,"name":"Assassin","description":["Deal 2% increased damage to champions when no allied champions are nearby"],"image":{"full":"6323.png","sprite":"mastery0.png","group":"mastery","x":432,"y":48,"w":48,"h":48},"ranks":1,"prereq":"0"},"6331":{"id":6331,"name":"Merciless","description":["Deal 0.6% increased damage to champions below 40% Health","Deal 1.2% increased damage to champions below 40% Health","Deal 1.8% increased damage to champions below 40% Health","Deal 2.4% increased damage to champions below 40% Health","Deal 3% increased damage to champions below 40% Health"],"image":{"full":"6331.png","sprite":"mastery0.png","group":"mastery","x":0,"y":96,"w":48,"h":48},"ranks":5,"prereq":"0"},"6332":{"id":6332,"name":"Meditation","description":["Regenerate 0.25% of your missing Mana every 5 seconds","Regenerate 0.5% of your missing Mana every 5 seconds","Regenerate 0.75% of your missing Mana every 5 seconds","Regenerate 1.0% of your missing Mana every 5 seconds","Regenerate 1.25% of your missing Mana every 5 seconds"],"image":{"full":"6332.png","sprite":"mastery0.png","group":"mastery","x":48,"y":96,"w":48,"h":48},"ranks":5,"prereq":"0"},"6341":{"id":6341,"name":"Greenfather's Gift","description":["Stepping into brush causes your next damaging attack or ability to deal 3% of your target's current health as bonus magic damage (9s Cooldown)"],"image":{"full":"6341.png","sprite":"mastery0.png","group":"mastery","x":96,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"},"6342":{"id":6342,"name":"Bandit","description":["Gain 1 gold for each nearby minion killed by an ally.

Gain 3 gold (10 if melee) when hitting an enemy champion with a basic attack (5 second cooldown)"],"image":{"full":"6342.png","sprite":"mastery0.png","group":"mastery","x":144,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"},"6343":{"id":6343,"name":"Dangerous Game","description":["Champion kills and assists restore 5% of your missing Health and Mana"],"image":{"full":"6343.png","sprite":"mastery0.png","group":"mastery","x":192,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"},"6351":{"id":6351,"name":"Precision","description":["Gain 1.2 Lethality and 0.3 + 0.05 per level Magic Penetration","Gain 2.4 Lethality and 0.6 + 0.10 per level Magic Penetration","Gain 3.6 Lethality and 0.9 + 0.15 per level Magic Penetration","Gain 4.8 Lethality and 1.2 + 0.20 per level Magic Penetration","Gain 6 Lethality and 1.5 + 0.25 per level Magic Penetration"],"image":{"full":"6351.png","sprite":"mastery0.png","group":"mastery","x":240,"y":96,"w":48,"h":48},"ranks":5,"prereq":"0"},"6352":{"id":6352,"name":"Intelligence","description":["Your Cooldown Reduction cap is increased to 41% and you gain 1% Cooldown Reduction","Your Cooldown Reduction cap is increased to 42% and you gain 2% Cooldown Reduction","Your Cooldown Reduction cap is increased to 43% and you gain 3% Cooldown Reduction","Your Cooldown Reduction cap is increased to 44% and you gain 4% Cooldown Reduction","Your Cooldown Reduction cap is increased to 45% and you gain 5% Cooldown Reduction"],"image":{"full":"6352.png","sprite":"mastery0.png","group":"mastery","x":288,"y":96,"w":48,"h":48},"ranks":5,"prereq":"0"},"6361":{"id":6361,"name":"Stormraider's Surge","description":["Dealing 30% of a champion's max Health within 2.5 seconds grants you 40% Movement Speed and 75% Slow Resistance for 3 seconds (10 second cooldown)."],"image":{"full":"6361.png","sprite":"mastery0.png","group":"mastery","x":336,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"},"6362":{"id":6362,"name":"Thunderlord's Decree","description":["Your 3rd attack or damaging spell against the same enemy champion calls down a lightning strike, dealing magic damage in the area.

Damage: 10 per level, plus 30% of your Bonus Attack Damage, and 10% of your Ability Power (25-15 second cooldown, based on level)."],"image":{"full":"6362.png","sprite":"mastery0.png","group":"mastery","x":384,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"},"6363":{"id":6363,"name":"Windspeaker's Blessing","description":["Your heals and shields are 10% stronger. Additionally, your shields and heals on other allies increase their armor by 5-22 (based on level) and their magic resistance by half that amount for 3 seconds."],"image":{"full":"6363.png","sprite":"mastery0.png","group":"mastery","x":432,"y":96,"w":48,"h":48},"ranks":1,"prereq":"0"}} -------------------------------------------------------------------------------- /api_data/runes.js: -------------------------------------------------------------------------------- 1 | module.exports = ''; -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var express = require('express'); 3 | var http = require('http'); 4 | var path = require('path'); 5 | var favicon = require('serve-favicon'); 6 | var logger = require('morgan'); 7 | var bodyParser = require('body-parser'); 8 | var compress = require('compression'); 9 | 10 | //middle ware 11 | var overallData = require('./middleware/overall_data.js'); 12 | 13 | //routes 14 | var champion = require('./routes/champion'); 15 | var matchup = require('./routes/matchup'); 16 | var matchupJson = require('./routes/matchup_json'); 17 | var apiStatic = require('./routes/api_static'); 18 | var statistics = require('./routes/statistics'); 19 | var faq = require('./routes/faq'); 20 | var index = require('./routes/index'); 21 | 22 | var app = express(); 23 | 24 | app.get('/*', function(req, res, next) { // redirect to http instead of www 25 | if (req.headers.host.match(/^www/) !== null ) { 26 | res.redirect('http://' + req.headers.host.replace(/^www\./, '') + req.url); 27 | } else { 28 | next(); 29 | } 30 | }); 31 | 32 | // view engine setup 33 | app.set('views', path.join(__dirname, 'views')); 34 | app.set('view engine', 'ejs'); 35 | 36 | app.use(compress()); 37 | app.use(favicon(__dirname + '/public/favicon.ico')); 38 | app.use(logger('dev')); 39 | app.use(bodyParser.json({limit: '2kb', extended: true})); 40 | app.use(bodyParser.urlencoded({limit: '2kb', extended: true})); 41 | 42 | app.use(express.static(path.join(__dirname, 'public'), {maxAge:86400000})); //one day 43 | 44 | //pages 45 | //set cache headers for page now that we are utilizing cloudflare 46 | app.use(function(req, res, next){ 47 | res.setHeader('Cache-Control', 'public, max-age=86400'); //cache pages for 1 minute, if needed I can purge cache from cloud flare 48 | next(); 49 | }); 50 | 51 | app.use(overallData); 52 | 53 | app.use('/champion', champion); 54 | app.use('/matchup', matchup); 55 | app.use('/matchupJson', matchupJson); 56 | app.use('/static', apiStatic); 57 | 58 | app.use('/statistics', statistics); 59 | app.use('/faq', faq); 60 | app.use('/', index); 61 | 62 | /// catch 404 and forwarding to error handler 63 | app.use(function(req, res, next) { 64 | var err = new Error('Not Found'); 65 | err.status = 404; 66 | next(err); 67 | }); 68 | 69 | /// error handlers 70 | 71 | // development error handler 72 | // will print stacktrace 73 | 74 | if (app.get('env') === 'development') { 75 | app.use(function(err, req, res, next) { 76 | res.statusCode = err.status; 77 | res.render('error', { 78 | pageData:{ 79 | appName: 'core', 80 | name:'error', 81 | title: 'We got ourselves a problem...' 82 | }, 83 | message: err.message, 84 | error: err 85 | }); 86 | }); 87 | } else { 88 | // production error handler 89 | // no stacktraces leaked to user 90 | app.use(function(err, req, res, next) { 91 | res.statusCode = err.status; 92 | res.render('error', { 93 | pageData:{ 94 | appName: 'core', 95 | name:'error', 96 | title: 'We got ourselves a wild teemo problem...' 97 | }, 98 | message: err.message, 99 | error: {} 100 | }); 101 | }); 102 | } 103 | 104 | module.exports = app; 105 | -------------------------------------------------------------------------------- /bash.txt: -------------------------------------------------------------------------------- 1 | Mongodump file 2 | mongodump --db championgg --collection webchampionpages --out ./db 3 | mongodump --db championgg --collection webchampionroles --out ./db 4 | mongodump --db championgg --collection webmatchuppages --out ./db 5 | mongodump --db championgg --collection weboverallroledatas --out ./db 6 | mongodump --db championgg --collection webhomepagesummaries --out ./db 7 | mongodump --db championgg --collection webstatisticspages --out ./db 8 | mongodump --db championgg --collection weboverallrolestats --out ./db 9 | git add -A && git commit -m "updated data" && git push 10 | 11 | 12 | cd /code/championweb 13 | git pull 14 | mongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson 15 | mongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson 16 | mongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson 17 | mongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson 18 | mongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson 19 | mongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson 20 | mongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallstats.bson 21 | cd bin && NODE_ENV=production pm2 restart www.js 22 | 23 | 24 | NODE_ENV=production pm2 restart www.js 25 | NODE_ENV=updating pm2 restart www.js 26 | NODE_ENV=serverUpdate pm2 restart www.js 27 | 28 | NODE_ENV=production pm2 start www.js -i max 29 | NODE_ENV=updating pm2 start www.js -i max 30 | NODE_ENV=serverUpdate pm2 start www.js -i max 31 | 32 | 33 | UPDATES=aggregation node performUpdate 34 | 35 | mongodump --db leaguetimes --collection votes 36 | -------------------------------------------------------------------------------- /bin/update_server.sh: -------------------------------------------------------------------------------- 1 | sudo npm install 2 | mongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson 3 | mongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson 4 | mongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson 5 | mongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson 6 | mongorestore --db championgg --collection weboverallstats --drop db/championgg/weboverallstats.bson 7 | mongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson 8 | mongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson 9 | cd bin && NODE_ENV=production pm2 restart www.js 10 | -------------------------------------------------------------------------------- /bin/www.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | var debug = require('debug')('my-application'); 4 | var app = require('../app'); 5 | var db = require('../db'); 6 | 7 | app.set('port', process.env.PORT || 80); 8 | 9 | app.listen(app.get('port'), function() { 10 | console.log('Express server listening on port ' + app.get('port')); 11 | }); 12 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | module.exports.config = { 2 | worker: { 3 | local: { 4 | githubRoot: 'https://raw.githubusercontent.com/joel1st/championweb/master/', 5 | githubRoute : 'https://raw.githubusercontent.com/joel1st/championweb/master/db/championgg/', 6 | files: [ 7 | 'webchampionpages.bson', 8 | 'webchampionpages.metadata.json', 9 | 'webchampionroles.bson', 10 | 'webchampionroles.metadata.json', 11 | 'webhomepagesummaries.bson', 12 | 'webhomepagesummaries.metadata.json', 13 | 'webmatchuppages.bson', 14 | 'webmatchuppages.metadata.json', 15 | 'weboverallroledatas.bson', 16 | 'weboverallroledatas.metadata.json', 17 | 'weboverallstats.bson', 18 | 'weboverallstats.metadata.json', 19 | 'webstatisticspages.bson', 20 | 'webstatisticspages.metadata.json' 21 | ], 22 | headline: 'headline.js', 23 | tmpFolder: 'tmp/', 24 | mongo: { 25 | host: 'localhost', 26 | port: '27017', 27 | db: 'championgg', 28 | user: '', 29 | password: '' 30 | }, 31 | queue: { 32 | prefix: 'championggQ_', 33 | host: 'localhost', 34 | pass: '', 35 | port: 6379 36 | }, 37 | fastly: { 38 | api_key: process.env.FASTLY_API_KEY, 39 | purgeall: 'https://api.fastly.com/service/' + process.env.FASTLY_SERVICE_ID + '/purge_all' 40 | } 41 | }, 42 | production: { 43 | githubRoot: 'https://raw.githubusercontent.com/joel1st/championweb/master/', 44 | githubRoute : 'https://raw.githubusercontent.com/joel1st/championweb/master/db/championgg/', 45 | files: [ 46 | 'webchampionpages.bson', 47 | 'webchampionpages.metadata.json', 48 | 'webchampionroles.bson', 49 | 'webchampionroles.metadata.json', 50 | 'webhomepagesummaries.bson', 51 | 'webhomepagesummaries.metadata.json', 52 | 'webmatchuppages.bson', 53 | 'webmatchuppages.metadata.json', 54 | 'weboverallroledatas.bson', 55 | 'weboverallroledatas.metadata.json', 56 | 'weboverallstats.bson', 57 | 'weboverallstats.metadata.json', 58 | 'webstatisticspages.bson', 59 | 'webstatisticspages.metadata.json' 60 | ], 61 | headline: 'headline.js', 62 | tmpFolder: 'tmp/', 63 | mongo: { 64 | host: process.env.WORKER_MONGO_HOST, 65 | port: process.env.WORKER_MONGO_PORT, 66 | db: process.env.WORKER_MONGO_DB, 67 | user: process.env.WORKER_MONGO_USER, 68 | password: process.env.WORKER_MONGO_PASSWORD 69 | }, 70 | queue: { 71 | prefix: process.env.QUEUE_PREFIX || 'championggQ_', 72 | host: process.env.REDIS_HOST || 'localhost', 73 | pass: process.env.REDIS_PASS || '', 74 | port: process.env.REDIS_PORT || 6379 75 | }, 76 | fastly: { 77 | api_key: process.env.FASTLY_API_KEY, 78 | purgeall: 'https://api.fastly.com/service/' + process.env.FASTLY_SERVICE_ID + '/purge_all' 79 | } 80 | } 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var mongoose = require('mongoose'); 3 | mongoose.connect(process.env.MONGO_DB || 'mongodb://localhost/championgg'); 4 | var db = mongoose.connection; 5 | 6 | db.on('error', console.error.bind(console, 'connection error:')); 7 | 8 | db.once('open', function () { 9 | console.log('Connection Made!'); 10 | }); 11 | -------------------------------------------------------------------------------- /db/championgg/webchampionpages.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/webchampionpages.bson -------------------------------------------------------------------------------- /db/championgg/webchampionpages.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.webchampionpages" } ] } -------------------------------------------------------------------------------- /db/championgg/webchampionroles.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/webchampionroles.bson -------------------------------------------------------------------------------- /db/championgg/webchampionroles.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.webchampionroles" } ] } -------------------------------------------------------------------------------- /db/championgg/webhomepagesummaries.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/webhomepagesummaries.bson -------------------------------------------------------------------------------- /db/championgg/webhomepagesummaries.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.webhomepagesummaries" } ] } -------------------------------------------------------------------------------- /db/championgg/webmatchuppages.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/webmatchuppages.bson -------------------------------------------------------------------------------- /db/championgg/webmatchuppages.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.webmatchuppages" } ] } -------------------------------------------------------------------------------- /db/championgg/weboverallroledatas.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/weboverallroledatas.bson -------------------------------------------------------------------------------- /db/championgg/weboverallroledatas.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.weboverallroledatas" } ] } -------------------------------------------------------------------------------- /db/championgg/weboverallstats.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/weboverallstats.bson -------------------------------------------------------------------------------- /db/championgg/weboverallstats.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.weboverallstats" } ] } -------------------------------------------------------------------------------- /db/championgg/webstatisticspages.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/db/championgg/webstatisticspages.bson -------------------------------------------------------------------------------- /db/championgg/webstatisticspages.metadata.json: -------------------------------------------------------------------------------- 1 | { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "championgg.webstatisticspages" } ] } -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt){ 2 | "use strict"; 3 | require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks); 4 | 5 | grunt.initConfig({ 6 | watch: { 7 | js: { 8 | files: [ 9 | 'routes/*.js', 10 | 'bin/*.js', 11 | 'logic/*.js', 12 | 'models/*.js', 13 | 'public/js/*.js', 14 | '*.js' 15 | ], 16 | tasks: ['jshint'] 17 | } 18 | }, 19 | pkg: grunt.file.readJSON('package.json'), 20 | jshint: { 21 | files: [ 22 | 'routes/*.js', 23 | 'bin/*.js', 24 | 'logic/*.js', 25 | 'models/*.js', 26 | 'public/js/*.js', 27 | '!public/js/master.min.js', 28 | '*.js' 29 | ], 30 | options: { 31 | // options here to override JSHint defaults 32 | node: true, 33 | loopfunc: true, 34 | globals: { 35 | jQuery: false, 36 | console: true, 37 | module: true, 38 | require: true 39 | } 40 | } 41 | }, 42 | concat: { 43 | js: { 44 | src: ['public/dist/js/angular.js', 'public/dist/js/angular-bootstrap.js', 45 | 'public/dist/js/dirDisqus.js', 'public/dist/js/chart.js', 46 | 'public/dist/js/tc-angular-chartjs.js', 'public/js/champion_data.js', 'public/js/chart_options.js', 'public/js/championgg_tooltip.js', 47 | 'public/js/app.js', 'public/js/champion_page.js', 'public/js/matchup_page.js', 'public/js/statistics_page.js'], 48 | dest: 'public/js/master.min.js' 49 | }, 50 | }, 51 | uglify: { 52 | build: { 53 | files: { 54 | 'public/js/master.min.js': 'public/js/master.min.js' 55 | } 56 | } 57 | }, 58 | cssmin: { 59 | build: { 60 | src: ['public/css/master.css', 'public/css/sprite.css'], 61 | dest: 'public/css/master.min.css' 62 | } 63 | } 64 | }); 65 | 66 | grunt.registerTask('production', ['concat', 'uglify','cssmin']); 67 | grunt.registerTask('default', []); 68 | }; -------------------------------------------------------------------------------- /headline.js: -------------------------------------------------------------------------------- 1 | // Message to display at the top of the site. 2 | // Standard message: We are currently aggregating patch 6.14 data - check back in 3 days! 3 | module.exports = ""; 4 | -------------------------------------------------------------------------------- /logic/lower_case_champ.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var champList = require('../api_data/champions.json'); 3 | 4 | /** 5 | * Converts the champion list keys to lower case. It then compares 6 | * the input to see if there is a match. (this is useful for checking, 7 | * if a url has entered a champion key - but doesn't match the correct casing). 8 | * @param {string} champName - the champ name to compare against the champList. 9 | * @return {string|undefined} - if a match is found, the champ key is returned, otherwise undefined. 10 | */ 11 | var lowerCaseChamp = function(champName) { 12 | for (var prop in champList) { 13 | if (prop.toLowerCase() === champName.toLowerCase()) { 14 | return prop; 15 | } 16 | } 17 | }; 18 | 19 | 20 | module.exports = lowerCaseChamp; -------------------------------------------------------------------------------- /logic/produce_error.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var errors = { 3 | champNotFound: 'That champ or role doesn\'t appear to exist!', 4 | pageNotFound: 'We couldn\'t find the page you are looking for - sorry!', 5 | serverMaintenance: 'For some reason we couldn\'t get the page to load - Chances are we\'re working on updating data - if it isn\'t fixed in the next few minutes please let us know!', 6 | invalidMatchup: 'That appears to be an invalid or old matchup!' 7 | }; 8 | 9 | /** 10 | * A function for generating errors. 11 | * @param {string} errorType - the key for the error type (which corresponds with the keys 12 | * in the error object above). 13 | * @param {number} errorNumber - the response number of the error (defaults to 404). 14 | * @return {object} - the error object. 15 | */ 16 | var produceError = function(errorType, errorNumber) { 17 | var err = new Error(errors[errorType]); 18 | err.status = errorNumber || 404; 19 | return err; 20 | }; 21 | 22 | module.exports = produceError; -------------------------------------------------------------------------------- /logic/role_hash_table.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * The varius role keys and values. 4 | * Used for determining legitimate roles for champion page routes. 5 | */ 6 | var roleList = { 7 | 'Top': 'TOP', 8 | 'Middle': 'MIDDLE', 9 | 'Support': 'DUO_SUPPORT', 10 | 'ADC': 'DUO_CARRY', 11 | 'Jungle': 'JUNGLE', 12 | 'top': 'TOP', 13 | 'middle': 'MIDDLE', 14 | 'support': 'DUO_SUPPORT', 15 | 'adc': 'DUO_CARRY', 16 | 'jungle': 'JUNGLE', 17 | 'adcsupport': 'ADCSUPPORT', 18 | 'synergy': 'SYNERGY' 19 | }; 20 | 21 | var roleKey = { 22 | 'TOP': 'Top', 23 | 'MIDDLE': 'Middle', 24 | 'DUO_SUPPORT': 'Support', 25 | 'DUO_CARRY': 'ADC', 26 | 'JUNGLE': 'Jungle', 27 | 'ADCSUPPORT': 'adcsupport', 28 | 'SYNERGY': 'synergy' 29 | }; 30 | 31 | exports.roleList = roleList; 32 | exports.roleKey = roleKey; -------------------------------------------------------------------------------- /middleware/overall_data.js: -------------------------------------------------------------------------------- 1 | var WebOverallStats = require('../models/web_overall_stats.js'); 2 | var produceError = require('../logic/produce_error.js'); 3 | 4 | /** 5 | * The core object is used in all views template for overall data. 6 | * Values from database are loaded from the webOverallStats 7 | * collection and added to core object. 8 | * @type {Object} 9 | */ 10 | var ddPatch = require('../api_data/dd_patch.json').ddPatch 11 | var core = { 12 | ddPatch: ddPatch, 13 | resetCache: ddPatch + Math.random().toFixed(6), 14 | masteryOrder: ['Offense','Defense','Utility'], 15 | headline: require('../headline.js') 16 | }; 17 | // Data retrieved from DB: 18 | // gamesAnalyzed:"3,549,640", 19 | // patch:"5.10", 20 | // patchHistory: ["5.6","5.7","5.8","5.9","5.10"] 21 | 22 | module.exports = function(req, res, next){ 23 | /** 24 | * Set the core object as a local for the view 25 | */ 26 | res.locals.core = core; 27 | 28 | /** 29 | * If no data has been retrieved from data base yet, 30 | * request overall stats data from collection and add it 31 | * to the core object. 32 | */ 33 | if (!core.championsAnalyzed){ 34 | WebOverallStats.findOne({}, function(err, data) { 35 | if (err) { 36 | return next(produceError('serverMaintenance', 503)); 37 | } else if (!data) { 38 | return next(produceError('serverMaintenance', 503)); 39 | } else { 40 | core.championsAnalyzed = data.championsAnalyzed; 41 | core.patch = data.patch; 42 | core.patchHistory = data.patchHistory; 43 | next(); 44 | } 45 | }); 46 | } else { 47 | next(); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /models/web_champion_page.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webChampionPage = new mongoose.Schema({ 4 | role: String, 5 | key: String, 6 | general: [{ 7 | title:String, 8 | titleLink: String, 9 | val:String, 10 | position:Number, 11 | change:Number 12 | }], 13 | overallPosition: { 14 | position: Number, 15 | change:Number 16 | }, 17 | championMatrix:[Number], 18 | patchPlay:[Number], 19 | gameLength:[Number], 20 | experienceRate:[Number], 21 | experienceSample:[Number], 22 | patchWin:[Number], 23 | dmgComposition:{ 24 | physicalDmg: Number, 25 | magicDmg: Number, 26 | trueDmg: Number 27 | }, 28 | items:{ 29 | mostGames: { 30 | items: [{ 31 | id:Number, 32 | name:String, 33 | }], 34 | games: Number, 35 | winPercent: Number 36 | }, 37 | highestWinPercent: { 38 | items: [{ 39 | id:Number, 40 | name:String, 41 | }], 42 | games: Number, 43 | winPercent: Number 44 | } 45 | }, 46 | firstItems:{ 47 | mostGames: { 48 | items: [{ 49 | id:Number, 50 | name:String, 51 | }], 52 | games: Number, 53 | winPercent: Number 54 | }, 55 | highestWinPercent: { 56 | items: [{ 57 | id:Number, 58 | name:String, 59 | }], 60 | games: Number, 61 | winPercent: Number 62 | } 63 | }, 64 | trinkets: [{ 65 | item: { 66 | id:Number, 67 | name:String, 68 | }, 69 | games: Number, 70 | winPercent: Number 71 | }], 72 | summoners:{ 73 | mostGames: { 74 | summoner1: { 75 | name:String, 76 | url: String 77 | }, 78 | summoner2: { 79 | name:String, 80 | url: String 81 | }, 82 | games: Number, 83 | winPercent: Number 84 | }, 85 | highestWinPercent: { 86 | summoner1: { 87 | name:String, 88 | url: String 89 | }, 90 | summoner2: { 91 | name:String, 92 | url: String 93 | }, 94 | games: Number, 95 | winPercent: Number 96 | } 97 | }, 98 | skills:{ 99 | skillInfo: [{ 100 | name: String, 101 | img: String, 102 | key: String 103 | }], 104 | mostGames: { 105 | order: [String], 106 | games: Number, 107 | winPercent: Number 108 | }, 109 | highestWinPercent: { 110 | order: [String], 111 | games: Number, 112 | winPercent: Number 113 | } 114 | }, 115 | masteries:{ 116 | mostGames: { 117 | masteries: [{ 118 | tree: String, 119 | total: Number, 120 | data: {row1: [{mastery:Number, points:Number}], 121 | row2: [{mastery:Number, points:Number}], 122 | row3: [{mastery:Number, points:Number}], 123 | row4: [{mastery:Number, points:Number}], 124 | row5: [{mastery:Number, points:Number}], 125 | row6: [{mastery:Number, points:Number}]} 126 | }], 127 | games: Number, 128 | winPercent: Number 129 | }, 130 | highestWinPercent: { 131 | masteries: [{ 132 | tree: String, 133 | total: Number, 134 | data: {row1: [{mastery:Number, points:Number}], 135 | row2: [{mastery:Number, points:Number}], 136 | row3: [{mastery:Number, points:Number}], 137 | row4: [{mastery:Number, points:Number}], 138 | row5: [{mastery:Number, points:Number}], 139 | row6: [{mastery:Number, points:Number}]} 140 | }], 141 | games: Number, 142 | winPercent: Number 143 | } 144 | }, 145 | runes:{ 146 | mostGames: { 147 | runes: [{ 148 | id: Number, 149 | number: Number, 150 | name: String, 151 | img: String, 152 | description: String, 153 | }], 154 | games: Number, 155 | winPercent: Number 156 | }, 157 | highestWinPercent: { 158 | runes: [{ 159 | id: Number, 160 | number: Number, 161 | name: String, 162 | img: String, 163 | description: String, 164 | }], 165 | games: Number, 166 | winPercent: Number 167 | } 168 | }, 169 | unique:{ //champions like viktor 170 | mostGames: { 171 | order: [Number], 172 | games: Number, 173 | winPercent: Number 174 | }, 175 | highestWinPercent: { 176 | order: [Number], 177 | games: Number, 178 | winPercent: Number 179 | } 180 | }, 181 | matchups:[{ // For all champions 182 | key:String, 183 | statScore:Number, 184 | games:Number, 185 | winRate:Number, 186 | winRateChange:Number 187 | }], 188 | adcsupport:[{ // only support/ad 189 | key:String, 190 | statScore:Number, 191 | games:Number, 192 | winRate:Number, 193 | winRateChange:Number 194 | }], 195 | synergy:[{ // only support/ad 196 | key:String, 197 | statScore:Number, 198 | games:Number, 199 | winRate:Number, 200 | winRateChange:Number 201 | }] 202 | }); 203 | 204 | module.exports = mongoose.model('WebChampionPage', webChampionPage); -------------------------------------------------------------------------------- /models/web_champion_roles.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | /** 3 | * Used to build up the long champ list on the homepage and the 4 | * individual champion roles on the left hand side of the champion pages. 5 | */ 6 | var webChampionRoles = new mongoose.Schema({ 7 | key: String, 8 | name: String, 9 | lastUpdated: Number, 10 | roles:[{ 11 | role:String, 12 | title:String, 13 | games:Number, 14 | percentPlayed:Number 15 | }] 16 | }); 17 | 18 | module.exports = mongoose.model('WebChampionRoles', webChampionRoles); -------------------------------------------------------------------------------- /models/web_home_page_summaries.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webHomePageSummaries = new mongoose.Schema({ 4 | id: Number, 5 | data: [{ 6 | title: String, 7 | total: Number, 8 | mostImproved:{ 9 | key: String, 10 | name: String, 11 | difference: Number, 12 | overall: Number 13 | }, 14 | leastImproved:{ 15 | key: String, 16 | name: String, 17 | difference: Number, 18 | overall: Number 19 | }, 20 | highestWinRate:{ 21 | key: String, 22 | name: String, 23 | value: Number 24 | }, 25 | lowestWinRate:{ 26 | key: String, 27 | name: String, 28 | value: Number 29 | }, 30 | bestOverall:{ 31 | key: String, 32 | name: String, 33 | value: Number 34 | }, 35 | worstOverall:{ 36 | key: String, 37 | name: String, 38 | value: Number 39 | } 40 | }] 41 | }); 42 | 43 | module.exports = mongoose.model('WebHomePageSummaries', webHomePageSummaries); -------------------------------------------------------------------------------- /models/web_matchup_page.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webMatchupPage = new mongoose.Schema({ 4 | champ1: { 5 | id: Number, 6 | key: String, 7 | name: String, 8 | role: String, 9 | roleTitle: String, 10 | performance: Number 11 | }, 12 | champ2: { 13 | id: Number, 14 | key: String, 15 | name: String, 16 | role: String, 17 | roleTitle: String, 18 | performance: Number 19 | }, 20 | role: String, 21 | dateAdded: Number, 22 | totalGames:Number, 23 | general: [{ 24 | title: String, 25 | champ1:{ 26 | val: Number, 27 | change: Number, 28 | score: Number //score to use is champ 1 is quering page 29 | }, 30 | champ2:{ 31 | val: Number, 32 | change: Number, 33 | score: Number //score to use if champ 2 is quering page 34 | }, 35 | }], 36 | championMatrix:{ 37 | labels:[String], 38 | champ1:[Number], 39 | champ2:[Number] 40 | }, 41 | goldLength:{ 42 | champ1:[Number], 43 | champ2:[Number] 44 | } 45 | }); 46 | 47 | module.exports = mongoose.model('WebMatchupPage', webMatchupPage); -------------------------------------------------------------------------------- /models/web_overall_role_data.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webOverallRoleData = new mongoose.Schema({ 4 | role:String, 5 | totalNumber: Number, 6 | matrixLabels:[String], 7 | patchPlay: [Number] 8 | }); 9 | 10 | module.exports = mongoose.model('WebOverallRoleData', webOverallRoleData); -------------------------------------------------------------------------------- /models/web_overall_stats.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webOverallStats = new mongoose.Schema({ 4 | patchHistory: [String], 5 | patch: String, 6 | championsAnalyzed: String, 7 | }); 8 | 9 | module.exports = mongoose.model('WebOverallStats', webOverallStats); -------------------------------------------------------------------------------- /models/web_statistics_page.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var webStatisticsPage = new mongoose.Schema({ 4 | key: String, 5 | title: String, 6 | role: String, 7 | general: { 8 | winPercent: Number, 9 | playPercent: Number, 10 | banRate: Number, 11 | experience: Number, 12 | kills: Number, 13 | deaths: Number, 14 | assists: Number, 15 | totalDamageDealtToChampions: Number, 16 | totalDamageTaken: Number, 17 | totalHeal: Number, 18 | largestKillingSpree: Number, 19 | minionsKilled: Number, 20 | neutralMinionsKilledTeamJungle: Number, 21 | neutralMinionsKilledEnemyJungle: Number, 22 | goldEarned: Number, 23 | overallPosition: Number, 24 | overallPositionChange: Number 25 | } 26 | }); 27 | 28 | module.exports = mongoose.model('WebStatisticsPage', webStatisticsPage); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "application-name", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "1.9.2", 10 | "compression": "1.2.0", 11 | "debug": "2.1.0 ", 12 | "ejs": "1.0.0", 13 | "express": "4.10.2", 14 | "mongoose": "3.8.19", 15 | "morgan": "1.5.0 ", 16 | "q": "1.4.1", 17 | "serve-favicon": "2.1.7" 18 | }, 19 | "devDependencies": { 20 | "grunt": "~0.4.0", 21 | "grunt-contrib-cssmin": "*", 22 | "grunt-contrib-uglify": "*", 23 | "grunt-contrib-watch": "*", 24 | "grunt-contrib-concat": "*", 25 | "grunt-contrib-jshint": "*", 26 | "grunt-htmlhint": "*", 27 | "matchdep": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/cpmstar/cpmstar_siteskin_iframebuster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 38 | -------------------------------------------------------------------------------- /public/dist/css/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.2 - 2015-02-08 2 | * http://jqueryui.com 3 | * Includes: core.css, tooltip.css 4 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px} -------------------------------------------------------------------------------- /public/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/dist/js/dirDisqus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A directive to embed a Disqus comments widget on your AngularJS page. 3 | * 4 | * For documentation, see the README.md file in this directory 5 | * 6 | * Created by Michael on 22/01/14. 7 | * Copyright Michael Bromley 2014 8 | * Available under the MIT license. 9 | */ 10 | angular.module('dirDisqus', []).directive('disqus', ['$window', function($window) { 11 | return { 12 | restrict: 'E', 13 | scope: { 14 | disqus_shortname: '@disqusShortname', 15 | disqus_identifier: '@disqusIdentifier', 16 | disqus_title: '@disqusTitle', 17 | disqus_url: '@disqusUrl', 18 | disqus_category_id: '@disqusCategoryId', 19 | disqus_disable_mobile: '@disqusDisableMobile', 20 | readyToBind: "@", 21 | disqusId: "@" 22 | }, 23 | template: '
', 24 | link: function(scope) { 25 | 26 | // ensure that the disqus_identifier and disqus_url are both set, otherwise we will run in to identifier conflicts when using URLs with "#" in them 27 | // see http://help.disqus.com/customer/portal/articles/662547-why-are-the-same-comments-showing-up-on-multiple-pages- 28 | if (typeof scope.disqus_identifier === 'undefined' || typeof scope.disqus_url === 'undefined') { 29 | throw "Please ensure that the `disqus-identifier` and `disqus-url` attributes are both set."; 30 | } 31 | 32 | scope.$watch("readyToBind", function(isReady) { 33 | 34 | // If the directive has been called without the 'ready-to-bind' attribute, we 35 | // set the default to "true" so that Disqus will be loaded straight away. 36 | if ( !angular.isDefined( isReady ) ) { 37 | isReady = "true"; 38 | } 39 | if (scope.$eval(isReady)) { 40 | // put the config variables into separate global vars so that the Disqus script can see them 41 | $window.disqus_shortname = scope.disqus_shortname; 42 | $window.disqus_identifier = scope.disqus_identifier; 43 | $window.disqus_title = scope.disqus_title; 44 | $window.disqus_url = scope.disqus_url; 45 | $window.disqus_category_id = scope.disqus_category_id; 46 | $window.disqus_disable_mobile = scope.disqus_disable_mobile; 47 | 48 | // get the remote Disqus script and insert it into the DOM, but only if it not already loaded (as that will cause warnings) 49 | if (!$window.DISQUS) { 50 | var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; 51 | dsq.src = '//' + scope.disqus_shortname + '.disqus.com/embed.js'; 52 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 53 | } else { 54 | $window.DISQUS.reset({ 55 | reload: true, 56 | config: function () { 57 | this.page.identifier = scope.disqus_identifier; 58 | this.page.url = scope.disqus_url; 59 | this.page.title = scope.disqus_title; 60 | } 61 | }); 62 | } 63 | } 64 | }); 65 | } 66 | }; 67 | }]); -------------------------------------------------------------------------------- /public/dist/js/tc-angular-chartjs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * tc-angular-chartjs - v1.0.9 - 2014-10-14 3 | * Copyright (c) 2014 Carl Craig 4 | * Dual licensed with the Apache-2.0 or MIT license. 5 | */ 6 | (function() { 7 | "use strict"; 8 | angular.module("tc.chartjs", []).directive("tcChartjs", TcChartjs).directive("tcChartjsLine", TcChartjsLine).directive("tcChartjsBar", TcChartjsBar).directive("tcChartjsRadar", TcChartjsRadar).directive("tcChartjsPolararea", TcChartjsPolararea).directive("tcChartjsPie", TcChartjsPie).directive("tcChartjsDoughnut", TcChartjsDoughnut).directive("tcChartjsLegend", TcChartjsLegend).factory("TcChartjsFactory", TcChartjsFactory); 9 | function TcChartjs(TcChartjsFactory) { 10 | return new TcChartjsFactory(); 11 | } 12 | TcChartjs.$inject = [ "TcChartjsFactory" ]; 13 | function TcChartjsLine(TcChartjsFactory) { 14 | return new TcChartjsFactory("line"); 15 | } 16 | TcChartjsLine.$inject = [ "TcChartjsFactory" ]; 17 | function TcChartjsBar(TcChartjsFactory) { 18 | return new TcChartjsFactory("bar"); 19 | } 20 | TcChartjsBar.$inject = [ "TcChartjsFactory" ]; 21 | function TcChartjsRadar(TcChartjsFactory) { 22 | return new TcChartjsFactory("radar"); 23 | } 24 | TcChartjsRadar.$inject = [ "TcChartjsFactory" ]; 25 | function TcChartjsPolararea(TcChartjsFactory) { 26 | return new TcChartjsFactory("polararea"); 27 | } 28 | TcChartjsPolararea.$inject = [ "TcChartjsFactory" ]; 29 | function TcChartjsPie(TcChartjsFactory) { 30 | return new TcChartjsFactory("pie"); 31 | } 32 | TcChartjsPie.$inject = [ "TcChartjsFactory" ]; 33 | function TcChartjsDoughnut(TcChartjsFactory) { 34 | return new TcChartjsFactory("doughnut"); 35 | } 36 | TcChartjsDoughnut.$inject = [ "TcChartjsFactory" ]; 37 | function TcChartjsFactory() { 38 | return function(chartType) { 39 | var directive = { 40 | restrict: "A", 41 | scope: { 42 | data: "=chartData", 43 | options: "=chartOptions", 44 | type: "@chartType", 45 | legend: "=chartLegend", 46 | chart: "=chart" 47 | }, 48 | link: link 49 | }; 50 | return directive; 51 | function link($scope, $elem, $attrs) { 52 | var ctx = $elem[0].getContext("2d"); 53 | var chart = new Chart(ctx); 54 | var chartObj; 55 | var showLegend = false; 56 | var autoLegend = false; 57 | var exposeChart = false; 58 | var legendElem = null; 59 | for (var attr in $attrs) { 60 | if (attr === "chartLegend") { 61 | showLegend = true; 62 | } else if (attr === "chart") { 63 | exposeChart = true; 64 | } else if (attr === "autoLegend") { 65 | autoLegend = true; 66 | } 67 | } 68 | $scope.$watch("data", function(value) { 69 | if (value) { 70 | if (chartObj) { 71 | chartObj.destroy(); 72 | } 73 | if (chartType) { 74 | chartObj = chart[cleanChartName(chartType)]($scope.data, $scope.options); 75 | } else if ($scope.type) { 76 | chartObj = chart[cleanChartName($scope.type)]($scope.data, $scope.options); 77 | } else { 78 | throw "Error creating chart: Chart type required."; 79 | } 80 | if (showLegend) { 81 | $scope.legend = chartObj.generateLegend(); 82 | } 83 | if (autoLegend) { 84 | if (legendElem) { 85 | legendElem.remove(); 86 | } 87 | angular.element($elem[0]).after(chartObj.generateLegend()); 88 | legendElem = angular.element($elem[0]).next(); 89 | } 90 | if (exposeChart) { 91 | $scope.chart = chartObj; 92 | } 93 | } 94 | }, true); 95 | } 96 | function cleanChartName(type) { 97 | var typeLowerCase = type.toLowerCase(); 98 | switch (typeLowerCase) { 99 | case "line": 100 | return "Line"; 101 | 102 | case "bar": 103 | return "Bar"; 104 | 105 | case "radar": 106 | return "Radar"; 107 | 108 | case "polararea": 109 | return "PolarArea"; 110 | 111 | case "pie": 112 | return "Pie"; 113 | 114 | case "doughnut": 115 | return "Doughnut"; 116 | 117 | default: 118 | return type; 119 | } 120 | } 121 | }; 122 | } 123 | function TcChartjsLegend() { 124 | var directive = { 125 | restrict: "A", 126 | scope: { 127 | legend: "=chartLegend" 128 | }, 129 | link: link 130 | }; 131 | return directive; 132 | function link($scope, $elem) { 133 | $scope.$watch("legend", function(value) { 134 | if (value) { 135 | $elem.html(value); 136 | } 137 | }, true); 138 | } 139 | } 140 | })(); -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/favicon.ico -------------------------------------------------------------------------------- /public/googled8153283379da1fb.html: -------------------------------------------------------------------------------- 1 | google-site-verification: googled8153283379da1fb.html -------------------------------------------------------------------------------- /public/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/bg.jpg -------------------------------------------------------------------------------- /public/img/champion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/champion.jpg -------------------------------------------------------------------------------- /public/img/header-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/header-bg.jpg -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/logo.png -------------------------------------------------------------------------------- /public/img/mastery0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/mastery0.jpg -------------------------------------------------------------------------------- /public/img/mastery1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/mastery1.jpg -------------------------------------------------------------------------------- /public/img/mastery2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/mastery2.jpg -------------------------------------------------------------------------------- /public/img/small_champion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joel1st/championweb/f98e5d775bc903d1454d8e41651770fbf44d434b/public/img/small_champion.jpg -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | var sortData = function(champ1, champ2, position) { 2 | champ1 = matchupData.championList[champ1].id; 3 | champ2 = matchupData.championList[champ2].id; 4 | return (champ1 < champ2) ? [champ1, champ2] : [champ2, champ1]; 5 | }; 6 | 7 | 8 | (function(angular, matchupData) { 9 | 10 | var appCore = angular.module('core', ['ui.bootstrap', 'championggTooltip']); 11 | 12 | appCore.run(['$templateCache', function($templateCache) { 13 | $templateCache.put('template/typeahead/typeahead-popup.html', 14 | ''); 20 | 21 | $templateCache.put('menu.html', 22 | ''+ 23 | ''+ 24 | ''+ 25 | ''); 26 | 27 | $templateCache.put('template/tooltip/tooltip-popup.html', 28 | '
'+ 29 | '
'+ 30 | '
{{content}}
'+ 31 | '
'); 32 | }]); 33 | 34 | 35 | appCore.controller('searchCtrl', ['$scope', function($scope) { 36 | $scope.selected = undefined; 37 | $scope.championMenu = []; 38 | 39 | for (var prop in matchupData.championList) { 40 | if (Object.hasOwnProperty.call(matchupData.championList, prop)) { 41 | $scope.championMenu.push({ 42 | key: matchupData.championList[prop].key, 43 | name: matchupData.championList[prop].name 44 | }); 45 | } 46 | } 47 | 48 | $scope.getPage = function(){ 49 | window.location.assign('/champion/'+ $scope.selected); 50 | }; 51 | 52 | $scope.determineSend = function(keyCode) { 53 | if (keyCode === 13) { 54 | var name = $scope.selected.key || $scope.selected; 55 | window.location.assign('/champion/' + name); 56 | } 57 | }; 58 | }]); 59 | 60 | appCore.factory('localStorageAccess', ['$window', '$rootScope', function($window, $rootScope) { 61 | angular.element($window).on('storage', function(event) { 62 | console.log('local storage changed'); 63 | if (event.key === 'matchupRating' || event.key === 'chosenSort') { 64 | $rootScope.$apply(); 65 | } 66 | }); 67 | 68 | var service = { 69 | 70 | save: function(key, data) { 71 | localStorage[key] = angular.toJson(data); 72 | return this; 73 | }, 74 | 75 | retrieve: function(key) { 76 | return angular.fromJson(localStorage[key]); 77 | } 78 | }; 79 | 80 | return service; 81 | }]); 82 | 83 | 84 | appCore.filter('startsWith', function() { 85 | function strStartsWith(str, prefix) { 86 | var wordSplit = str.split(' '); 87 | 88 | function determineString(word) { 89 | return ((word + "").toLowerCase()).indexOf(prefix.toLowerCase()) === 0 || ((str + "").toLowerCase()).indexOf(prefix.toLowerCase()) === 0; 90 | } 91 | 92 | for (var i = 0; i < wordSplit.length; i++) { 93 | if (determineString(wordSplit[i])) { 94 | return true; 95 | } 96 | } 97 | return false; 98 | } 99 | 100 | 101 | return function(items, amount) { 102 | var filtered = []; 103 | 104 | angular.forEach(items, function(item) { 105 | if (strStartsWith(item.name || item.title, amount)) { 106 | filtered.push(item); 107 | } 108 | }); 109 | 110 | return filtered; 111 | }; 112 | }); 113 | 114 | })(angular, matchupData); -------------------------------------------------------------------------------- /public/js/champion_data.js: -------------------------------------------------------------------------------- 1 | matchupData.championList={"Aatrox":{"id":266,"key":"Aatrox","name":"Aatrox","title":"the Darkin Blade"},"Ahri":{"id":103,"key":"Ahri","name":"Ahri","title":"the Nine-Tailed Fox"},"Akali":{"id":84,"key":"Akali","name":"Akali","title":"the Fist of Shadow"},"Alistar":{"id":12,"key":"Alistar","name":"Alistar","title":"the Minotaur"},"Amumu":{"id":32,"key":"Amumu","name":"Amumu","title":"the Sad Mummy"},"Anivia":{"id":34,"key":"Anivia","name":"Anivia","title":"the Cryophoenix"},"Annie":{"id":1,"key":"Annie","name":"Annie","title":"the Dark Child"},"Ashe":{"id":22,"key":"Ashe","name":"Ashe","title":"the Frost Archer"},"AurelionSol":{"id":136,"key":"AurelionSol","name":"Aurelion Sol","title":"The Star Forger"},"Azir":{"id":268,"key":"Azir","name":"Azir","title":"the Emperor of the Sands"},"Bard":{"id":432,"key":"Bard","name":"Bard","title":"the Wandering Caretaker"},"Blitzcrank":{"id":53,"key":"Blitzcrank","name":"Blitzcrank","title":"the Great Steam Golem"},"Brand":{"id":63,"key":"Brand","name":"Brand","title":"the Burning Vengeance"},"Braum":{"id":201,"key":"Braum","name":"Braum","title":"the Heart of the Freljord"},"Caitlyn":{"id":51,"key":"Caitlyn","name":"Caitlyn","title":"the Sheriff of Piltover"},"Camille":{"id":164,"key":"Camille","name":"Camille","title":"the Steel Shadow"},"Cassiopeia":{"id":69,"key":"Cassiopeia","name":"Cassiopeia","title":"the Serpent's Embrace"},"Chogath":{"id":31,"key":"Chogath","name":"Cho'Gath","title":"the Terror of the Void"},"Corki":{"id":42,"key":"Corki","name":"Corki","title":"the Daring Bombardier"},"Darius":{"id":122,"key":"Darius","name":"Darius","title":"the Hand of Noxus"},"Diana":{"id":131,"key":"Diana","name":"Diana","title":"Scorn of the Moon"},"Draven":{"id":119,"key":"Draven","name":"Draven","title":"the Glorious Executioner"},"DrMundo":{"id":36,"key":"DrMundo","name":"Dr. Mundo","title":"the Madman of Zaun"},"Ekko":{"id":245,"key":"Ekko","name":"Ekko","title":"the Boy Who Shattered Time"},"Elise":{"id":60,"key":"Elise","name":"Elise","title":"the Spider Queen"},"Evelynn":{"id":28,"key":"Evelynn","name":"Evelynn","title":"the Widowmaker"},"Ezreal":{"id":81,"key":"Ezreal","name":"Ezreal","title":"the Prodigal Explorer"},"Fiddlesticks":{"id":9,"key":"Fiddlesticks","name":"Fiddlesticks","title":"the Harbinger of Doom"},"Fiora":{"id":114,"key":"Fiora","name":"Fiora","title":"the Grand Duelist"},"Fizz":{"id":105,"key":"Fizz","name":"Fizz","title":"the Tidal Trickster"},"Galio":{"id":3,"key":"Galio","name":"Galio","title":"the Colossus"},"Gangplank":{"id":41,"key":"Gangplank","name":"Gangplank","title":"the Saltwater Scourge"},"Garen":{"id":86,"key":"Garen","name":"Garen","title":"The Might of Demacia"},"Gnar":{"id":150,"key":"Gnar","name":"Gnar","title":"the Missing Link"},"Gragas":{"id":79,"key":"Gragas","name":"Gragas","title":"the Rabble Rouser"},"Graves":{"id":104,"key":"Graves","name":"Graves","title":"the Outlaw"},"Hecarim":{"id":120,"key":"Hecarim","name":"Hecarim","title":"the Shadow of War"},"Heimerdinger":{"id":74,"key":"Heimerdinger","name":"Heimerdinger","title":"the Revered Inventor"},"Illaoi":{"id":420,"key":"Illaoi","name":"Illaoi","title":"the Kraken Priestess"},"Irelia":{"id":39,"key":"Irelia","name":"Irelia","title":"the Will of the Blades"},"Ivern":{"id":427,"key":"Ivern","name":"Ivern","title":"the Green Father"},"Janna":{"id":40,"key":"Janna","name":"Janna","title":"the Storm's Fury"},"JarvanIV":{"id":59,"key":"JarvanIV","name":"Jarvan IV","title":"the Exemplar of Demacia"},"Jax":{"id":24,"key":"Jax","name":"Jax","title":"Grandmaster at Arms"},"Jayce":{"id":126,"key":"Jayce","name":"Jayce","title":"the Defender of Tomorrow"},"Jhin":{"id":202,"key":"Jhin","name":"Jhin","title":"the Virtuoso"},"Jinx":{"id":222,"key":"Jinx","name":"Jinx","title":"the Loose Cannon"},"Kalista":{"id":429,"key":"Kalista","name":"Kalista","title":"the Spear of Vengeance"},"Karma":{"id":43,"key":"Karma","name":"Karma","title":"the Enlightened One"},"Karthus":{"id":30,"key":"Karthus","name":"Karthus","title":"the Deathsinger"},"Kassadin":{"id":38,"key":"Kassadin","name":"Kassadin","title":"the Void Walker"},"Katarina":{"id":55,"key":"Katarina","name":"Katarina","title":"the Sinister Blade"},"Kayle":{"id":10,"key":"Kayle","name":"Kayle","title":"The Judicator"},"Kennen":{"id":85,"key":"Kennen","name":"Kennen","title":"the Heart of the Tempest"},"Khazix":{"id":121,"key":"Khazix","name":"Kha'Zix","title":"the Voidreaver"},"Kindred":{"id":203,"key":"Kindred","name":"Kindred","title":"The Eternal Hunters"},"Kled":{"id":240,"key":"Kled","name":"Kled","title":"the Cantankerous Cavalier"},"KogMaw":{"id":96,"key":"KogMaw","name":"Kog'Maw","title":"the Mouth of the Abyss"},"Leblanc":{"id":7,"key":"Leblanc","name":"LeBlanc","title":"the Deceiver"},"LeeSin":{"id":64,"key":"LeeSin","name":"Lee Sin","title":"the Blind Monk"},"Leona":{"id":89,"key":"Leona","name":"Leona","title":"the Radiant Dawn"},"Lissandra":{"id":127,"key":"Lissandra","name":"Lissandra","title":"the Ice Witch"},"Lucian":{"id":236,"key":"Lucian","name":"Lucian","title":"the Purifier"},"Lulu":{"id":117,"key":"Lulu","name":"Lulu","title":"the Fae Sorceress"},"Lux":{"id":99,"key":"Lux","name":"Lux","title":"the Lady of Luminosity"},"Malphite":{"id":54,"key":"Malphite","name":"Malphite","title":"Shard of the Monolith"},"Malzahar":{"id":90,"key":"Malzahar","name":"Malzahar","title":"the Prophet of the Void"},"Maokai":{"id":57,"key":"Maokai","name":"Maokai","title":"the Twisted Treant"},"MasterYi":{"id":11,"key":"MasterYi","name":"Master Yi","title":"the Wuju Bladesman"},"MissFortune":{"id":21,"key":"MissFortune","name":"Miss Fortune","title":"the Bounty Hunter"},"MonkeyKing":{"id":62,"key":"MonkeyKing","name":"Wukong","title":"the Monkey King"},"Mordekaiser":{"id":82,"key":"Mordekaiser","name":"Mordekaiser","title":"the Iron Revenant"},"Morgana":{"id":25,"key":"Morgana","name":"Morgana","title":"Fallen Angel"},"Nami":{"id":267,"key":"Nami","name":"Nami","title":"the Tidecaller"},"Nasus":{"id":75,"key":"Nasus","name":"Nasus","title":"the Curator of the Sands"},"Nautilus":{"id":111,"key":"Nautilus","name":"Nautilus","title":"the Titan of the Depths"},"Nidalee":{"id":76,"key":"Nidalee","name":"Nidalee","title":"the Bestial Huntress"},"Nocturne":{"id":56,"key":"Nocturne","name":"Nocturne","title":"the Eternal Nightmare"},"Nunu":{"id":20,"key":"Nunu","name":"Nunu","title":"the Yeti Rider"},"Olaf":{"id":2,"key":"Olaf","name":"Olaf","title":"the Berserker"},"Orianna":{"id":61,"key":"Orianna","name":"Orianna","title":"the Lady of Clockwork"},"Pantheon":{"id":80,"key":"Pantheon","name":"Pantheon","title":"the Artisan of War"},"Poppy":{"id":78,"key":"Poppy","name":"Poppy","title":"Keeper of the Hammer"},"Quinn":{"id":133,"key":"Quinn","name":"Quinn","title":"Demacia's Wings"},"Rakan":{"id":497,"key":"Rakan","name":"Rakan","title":"The Charmer"},"Rammus":{"id":33,"key":"Rammus","name":"Rammus","title":"the Armordillo"},"RekSai":{"id":421,"key":"RekSai","name":"Rek'Sai","title":"the Void Burrower"},"Renekton":{"id":58,"key":"Renekton","name":"Renekton","title":"the Butcher of the Sands"},"Rengar":{"id":107,"key":"Rengar","name":"Rengar","title":"the Pridestalker"},"Riven":{"id":92,"key":"Riven","name":"Riven","title":"the Exile"},"Rumble":{"id":68,"key":"Rumble","name":"Rumble","title":"the Mechanized Menace"},"Ryze":{"id":13,"key":"Ryze","name":"Ryze","title":"the Rune Mage"},"Sejuani":{"id":113,"key":"Sejuani","name":"Sejuani","title":"Fury of the North"},"Shaco":{"id":35,"key":"Shaco","name":"Shaco","title":"the Demon Jester"},"Shen":{"id":98,"key":"Shen","name":"Shen","title":"the Eye of Twilight"},"Shyvana":{"id":102,"key":"Shyvana","name":"Shyvana","title":"the Half-Dragon"},"Singed":{"id":27,"key":"Singed","name":"Singed","title":"the Mad Chemist"},"Sion":{"id":14,"key":"Sion","name":"Sion","title":"The Undead Juggernaut"},"Sivir":{"id":15,"key":"Sivir","name":"Sivir","title":"the Battle Mistress"},"Skarner":{"id":72,"key":"Skarner","name":"Skarner","title":"the Crystal Vanguard"},"Sona":{"id":37,"key":"Sona","name":"Sona","title":"Maven of the Strings"},"Soraka":{"id":16,"key":"Soraka","name":"Soraka","title":"the Starchild"},"Swain":{"id":50,"key":"Swain","name":"Swain","title":"the Master Tactician"},"Syndra":{"id":134,"key":"Syndra","name":"Syndra","title":"the Dark Sovereign"},"TahmKench":{"id":223,"key":"TahmKench","name":"Tahm Kench","title":"the River King"},"Taliyah":{"id":163,"key":"Taliyah","name":"Taliyah","title":"the Stoneweaver"},"Talon":{"id":91,"key":"Talon","name":"Talon","title":"the Blade's Shadow"},"Taric":{"id":44,"key":"Taric","name":"Taric","title":"the Shield of Valoran"},"Teemo":{"id":17,"key":"Teemo","name":"Teemo","title":"the Swift Scout"},"Thresh":{"id":412,"key":"Thresh","name":"Thresh","title":"the Chain Warden"},"Tristana":{"id":18,"key":"Tristana","name":"Tristana","title":"the Yordle Gunner"},"Trundle":{"id":48,"key":"Trundle","name":"Trundle","title":"the Troll King"},"Tryndamere":{"id":23,"key":"Tryndamere","name":"Tryndamere","title":"the Barbarian King"},"TwistedFate":{"id":4,"key":"TwistedFate","name":"Twisted Fate","title":"the Card Master"},"Twitch":{"id":29,"key":"Twitch","name":"Twitch","title":"the Plague Rat"},"Udyr":{"id":77,"key":"Udyr","name":"Udyr","title":"the Spirit Walker"},"Urgot":{"id":6,"key":"Urgot","name":"Urgot","title":"the Headsman's Pride"},"Varus":{"id":110,"key":"Varus","name":"Varus","title":"the Arrow of Retribution"},"Vayne":{"id":67,"key":"Vayne","name":"Vayne","title":"the Night Hunter"},"Veigar":{"id":45,"key":"Veigar","name":"Veigar","title":"the Tiny Master of Evil"},"Velkoz":{"id":161,"key":"Velkoz","name":"Vel'Koz","title":"the Eye of the Void"},"Vi":{"id":254,"key":"Vi","name":"Vi","title":"the Piltover Enforcer"},"Viktor":{"id":112,"key":"Viktor","name":"Viktor","title":"the Machine Herald"},"Vladimir":{"id":8,"key":"Vladimir","name":"Vladimir","title":"the Crimson Reaper"},"Volibear":{"id":106,"key":"Volibear","name":"Volibear","title":"the Thunder's Roar"},"Warwick":{"id":19,"key":"Warwick","name":"Warwick","title":"the Uncaged Wrath of Zaun"},"Xayah":{"id":498,"key":"Xayah","name":"Xayah","title":"the Rebel"},"Xerath":{"id":101,"key":"Xerath","name":"Xerath","title":"the Magus Ascendant"},"XinZhao":{"id":5,"key":"XinZhao","name":"Xin Zhao","title":"the Seneschal of Demacia"},"Yasuo":{"id":157,"key":"Yasuo","name":"Yasuo","title":"the Unforgiven"},"Yorick":{"id":83,"key":"Yorick","name":"Yorick","title":"Shepherd of Souls"},"Zac":{"id":154,"key":"Zac","name":"Zac","title":"the Secret Weapon"},"Zed":{"id":238,"key":"Zed","name":"Zed","title":"the Master of Shadows"},"Ziggs":{"id":115,"key":"Ziggs","name":"Ziggs","title":"the Hexplosives Expert"},"Zilean":{"id":26,"key":"Zilean","name":"Zilean","title":"the Chronokeeper"},"Zyra":{"id":143,"key":"Zyra","name":"Zyra","title":"Rise of the Thorns"}}; -------------------------------------------------------------------------------- /public/js/championgg_tooltip.js: -------------------------------------------------------------------------------- 1 | (function(angular, matchupData) { 2 | 3 | var champggTooltip = angular.module('championggTooltip', []); 4 | 5 | 6 | champggTooltip.filter('to_trusted', ['$sce', function($sce) { 7 | return function(text) { 8 | return $sce.trustAsHtml(text); 9 | }; 10 | }]); 11 | 12 | 13 | champggTooltip.factory('ToolTip', ['$window', function($window){ 14 | return { 15 | determineDirection: function(position, tipDetails, width){ 16 | var ref = position; 17 | var newWidth = 240 || width; 18 | var cssObj = { 19 | "opacity":1, 20 | "top" : ref.bottom - ref.height - tipDetails.height + $window.scrollY + "px", 21 | "left" : ref.right - (newWidth - ((newWidth - ref.width)/2)) + $window.scrollX + "px", 22 | "height" : "auto", 23 | "width" : newWidth + "px" 24 | }; 25 | return cssObj; 26 | }, 27 | getTemplate: function(tElement, tAttrs){ 28 | var beginning = "
"; 29 | var obj = { 30 | masteries: "

{{tooltipContent.name}}

"+ 31 | "
"+ 32 | "
"+ 33 | "
" 34 | }; 35 | var end = "
Loading...
"+ 36 | "
"; 37 | return beginning + obj[tAttrs.apiType] + end; 38 | } 39 | }; 40 | }]); 41 | 42 | 43 | champggTooltip.directive('toolContainer', ['ToolTip', '$timeout', '$http', function(ToolTip, $timeout, $http){ 44 | return { 45 | restrict: "E", 46 | template: ToolTip.getTemplate, 47 | scope:{ 48 | position:"=", 49 | apiType: "@", 50 | apiPrimaryId: "@", 51 | apiSecondaryId: "@" 52 | }, 53 | link: function(scope, elem, attr){ 54 | elem.addClass('tooltip-hover'); 55 | 56 | var adjustCss = function(){ 57 | scope.tipDetails = elem[0].getBoundingClientRect(); 58 | elem.css(ToolTip.determineDirection(scope.position, scope.tipDetails)); 59 | }; 60 | 61 | $timeout(function(){ 62 | adjustCss(); 63 | }, 1); 64 | 65 | scope.tooltipContent = false; 66 | scope.$watch('tooltipContent', function(){ 67 | $timeout(function(){ 68 | adjustCss(); 69 | }, 1); 70 | }, true); 71 | 72 | $http.get('/static/'+scope.apiType+'/'+scope.apiPrimaryId).success(function(data, status, headers, config){ 73 | scope.tooltipContent = data; 74 | }).error(function(data,status,headers,config){ 75 | console.log(data); 76 | }); 77 | 78 | } 79 | }; 80 | }]); 81 | 82 | champggTooltip.directive('championTip', ['$compile', '$timeout', function($compile, $timeout){ 83 | return { 84 | restrict: "A", 85 | scope:{ 86 | apiType: "@", 87 | apiPrimaryId: "@", 88 | apiSecondaryId: "@" 89 | }, 90 | controller: ['$scope', function($scope){ 91 | $scope.positionInfo = {}; 92 | }], 93 | link: function(scope, elem, attr){ 94 | var elemCopy = elem; 95 | var timed = []; 96 | mouseenterFunction = function(){ 97 | timed[timed.length] = $timeout(function(){ 98 | scope.positionInfo = this.getBoundingClientRect(); 99 | var currentToolTip = ""; 100 | var tool = $compile(currentToolTip)(scope); 101 | angular.element(document.getElementsByTagName('body')[0]).prepend(tool); 102 | }.bind(this), 50); 103 | }; 104 | 105 | mouseoutFunction = function(){ 106 | for(var y = 0; y < timed.length; y++){ 107 | $timeout.cancel(timed[y]); 108 | } 109 | 110 | var ref = angular.element(document.getElementsByClassName('currentTooltip')); 111 | for(var i = 0; i < ref.length; i++){ 112 | ref[i].remove(); 113 | } 114 | }; 115 | 116 | elem.bind('mouseover', mouseenterFunction); 117 | elem.bind('mouseleave', mouseoutFunction); 118 | 119 | } 120 | }; 121 | }]); 122 | 123 | })(angular, matchupData); -------------------------------------------------------------------------------- /public/js/chart_options.js: -------------------------------------------------------------------------------- 1 | var radarChartSettings = { 2 | responsive: true, 3 | //Boolean - If we show the scale above the chart data 4 | scaleOverlay: false, 5 | 6 | tooltipFillColor: "rgba(0,0,0,0)", 7 | showTooltips: true, 8 | multiTooltipTemplate: "<%= value %>", 9 | customTooltips: function(tooltip) { 10 | var id, row; 11 | if (tooltip && tooltip.title) { 12 | if (!this.chart.highlighted) { 13 | this.chart.highlighted = []; 14 | } else { 15 | this.chart.highlighted.forEach(function(e) { 16 | e.className = e.className.replace(' highlighted', ''); 17 | }); 18 | } 19 | id = 'statistics-' + tooltip.title.replace(' ', '-').toLowerCase() + '-row'; 20 | row = document.getElementById(id); 21 | row.className += ' highlighted'; 22 | 23 | if (this.chart.highlighted.indexOf(row) === -1) this.chart.highlighted.push(row); 24 | } 25 | }, 26 | 27 | //Boolean - Whether to show lines for each scale point 28 | scaleShowLine: true, 29 | 30 | //String - Colour of the scale line 31 | scaleLineColor: "#333", 32 | 33 | //Number - Pixel width of the scale line 34 | scaleLineWidth: 1, 35 | 36 | //Boolean - Whether to show labels on the scale 37 | scaleShowLabels: false, 38 | 39 | //Interpolated JS string - can access value 40 | scaleLabel: "<%=value%>", 41 | 42 | //String - Scale label font declaration for the scale label 43 | scaleFontFamily: "'Arial'", 44 | 45 | //Number - Scale label font size in pixels 46 | scaleFontSize: 12, 47 | 48 | //String - Scale label font weight style 49 | scaleFontStyle: "normal", 50 | 51 | //String - Scale label font colour 52 | scaleFontColor: "#E0E0E0", 53 | 54 | //Boolean - Show a backdrop to the scale label 55 | scaleShowLabelBackdrop: true, 56 | 57 | //String - The colour of the label backdrop 58 | scaleBackdropColor: "rgba(255,255,255,0.75)", 59 | 60 | //Number - The backdrop padding above & below the label in pixels 61 | scaleBackdropPaddingY: 2, 62 | 63 | //Number - The backdrop padding to the side of the label in pixels 64 | scaleBackdropPaddingX: 2, 65 | 66 | //Boolean - Whether we show the angle lines out of the radar 67 | angleShowLineOut: true, 68 | 69 | //String - Colour of the angle line 70 | angleLineColor: "rgba(255,255,255,0.3)", 71 | 72 | //Number - Pixel width of the angle line 73 | angleLineWidth: 1, 74 | 75 | //String - Point label font declaration 76 | pointLabelFontFamily: "'Arial'", 77 | 78 | //String - Point label font weight 79 | pointLabelFontStyle: "normal", 80 | 81 | //Number - Point label font size in pixels 82 | pointLabelFontSize: 12, 83 | 84 | //String - Point label font colour 85 | pointLabelFontColor: "#E0E0E0", 86 | 87 | //Boolean - Whether to show a dot for each point 88 | pointDot: true, 89 | 90 | //Number - Radius of each point dot in pixels 91 | pointDotRadius: 3, 92 | 93 | //Number - Pixel width of point dot stroke 94 | pointDotStrokeWidth: 1, 95 | 96 | //Boolean - Whether to show a stroke for datasets 97 | datasetStroke: true, 98 | 99 | //Number - Pixel width of dataset stroke 100 | datasetStrokeWidth: 1, 101 | 102 | //Boolean - Whether to fill the dataset with a colour 103 | datasetFill: true, 104 | 105 | //Boolean - Whether to animate the chart 106 | animation: false, 107 | 108 | //Number - Number of animation steps 109 | animationSteps: 60, 110 | 111 | //String - Animation easing effect 112 | animationEasing: "easeOutQuart", 113 | legendTemplate: "" 114 | }; 115 | 116 | var lineChartSettings = { 117 | animation: false, 118 | responsive: true, 119 | //Boolean - If we show the scale above the chart data 120 | scaleOverlay: false, 121 | 122 | //Boolean - Whether to show lines for each scale point 123 | scaleShowLine: true, 124 | 125 | //String - Colour of the scale line 126 | scaleLineColor: "#333", 127 | 128 | //Number - Pixel width of the scale line 129 | scaleLineWidth: 1, 130 | 131 | //Boolean - Whether to show labels on the scale 132 | //scaleShowLabels : false, 133 | 134 | //Interpolated JS string - can access value 135 | scaleLabel: "<%=value%>", 136 | 137 | //String - Scale label font declaration for the scale label 138 | scaleFontFamily: "'Arial'", 139 | 140 | //Number - Scale label font size in pixels 141 | scaleFontSize: 12, 142 | 143 | //String - Scale label font weight style 144 | scaleFontStyle: "normal", 145 | //String - Scale label font colour 146 | scaleFontColor: "rgb(179, 179, 179)", 147 | legendTemplate: '' 148 | 149 | }; 150 | 151 | var pieChartSettings = { 152 | 153 | // Sets the chart to be responsive 154 | responsive: true, 155 | 156 | //Boolean - Whether we should show a stroke on each segment 157 | segmentShowStroke : true, 158 | 159 | //String - The colour of each segment stroke 160 | segmentStrokeColor : 'rgb(30, 35, 37)', 161 | 162 | //Number - The width of each segment stroke 163 | segmentStrokeWidth : 3, 164 | 165 | //Number - The percentage of the chart that we cut out of the middle 166 | percentageInnerCutout : 0, // This is 0 for Pie charts 167 | 168 | //Boolean - Whether we animate the rotation of the Doughnut 169 | animateRotate : false, 170 | 171 | //Boolean - Whether we animate scaling the Doughnut from the centre 172 | animateScale : false, 173 | 174 | tooltipTemplate: "<%if (label){%><%=label%> Games: <%}%><%= value %>%", 175 | //String - A legend template 176 | legendTemplate : '' 177 | 178 | }; 179 | -------------------------------------------------------------------------------- /public/js/matchup_page.js: -------------------------------------------------------------------------------- 1 | (function(angular, matchupData, sortData, radarChartSettings, lineChartSettings) { 2 | 3 | var appMatchup = angular.module('matchupPage', ['tc.chartjs', 'dirDisqus', 'core']); 4 | 5 | appMatchup.controller('matchupGraphs', ['$scope', function($scope) { 6 | $scope.champComparison = { 7 | data: { 8 | labels: matchupData.championMatrix.labels, 9 | datasets: [{ 10 | label: matchupData.champ1.name, 11 | fillColor: "rgba(101, 228, 245, 0.65)", 12 | strokeColor: "rgba(101, 228, 245, 1)", 13 | pointColor: "rgba(101, 228, 245, 1)", 14 | pointStrokeColor: "#fff", 15 | pointHighlightFill: "#fff", 16 | pointHighlightStroke: "rgba(101, 228, 245, 1)", 17 | data: matchupData.championMatrix.champ1 18 | }, { 19 | label: matchupData.champ2.name, 20 | fillColor: "rgba(255, 83, 83, 0.55)", 21 | strokeColor: "rgba(255, 83, 83, 0.99)", 22 | pointColor: "rgba(255, 83, 83, 0.99)", 23 | pointStrokeColor: "#fff", 24 | pointHighlightFill: "#fff", 25 | pointHighlightStroke: "rgba(255, 83, 83, 0.99)", 26 | data: matchupData.championMatrix.champ2 27 | }] 28 | }, 29 | settings: radarChartSettings 30 | }; 31 | 32 | $scope.goldIncome = { 33 | data: { 34 | labels: ["0-10", "10-20", "20-30", "30+"], 35 | datasets: [{ 36 | label: "champ1", 37 | fillColor: "rgba(101, 228, 245, 0.65)", 38 | strokeColor: "rgba(101, 228, 245, 1)", 39 | pointColor: "rgba(101, 228, 245, 1)", 40 | pointStrokeColor: "#fff", 41 | pointHighlightFill: "#fff", 42 | pointHighlightStroke: "rgba(101, 228, 245, 1)", 43 | data: matchupData.goldLength.champ1 44 | }, { 45 | label: "champ2", 46 | fillColor: "rgba(255, 83, 83, 0.55)", 47 | strokeColor: "rgba(255, 83, 83, 0.99)", 48 | pointColor: "rgba(255, 83, 83, 0.99)", 49 | pointStrokeColor: "#fff", 50 | pointHighlightFill: "#fff", 51 | pointHighlightStroke: "rgba(255, 83, 83, 0.99)", 52 | data: matchupData.goldLength.champ2 53 | }] 54 | }, 55 | settings: lineChartSettings 56 | }; 57 | }]); 58 | })(angular, matchupData, sortData, radarChartSettings, lineChartSettings); -------------------------------------------------------------------------------- /public/js/statistics_jquery.js: -------------------------------------------------------------------------------- 1 | // jquery 2 | (function($){ 3 | var $fixedHeader = $("#header-fixed"); 4 | var $table = $("#table-1"); 5 | var $fixedHeaderTd = $("#header-fixed td"); 6 | var $originalHeaderTd = $("#original-header td"); 7 | 8 | function fixWidths(){ 9 | clearTimeout(timeOut); 10 | var timeOut = setTimeout(function(){ 11 | $fixedHeader.width($table.width()); 12 | for(var t=0;t<$fixedHeaderTd.length;t++){ 13 | $fixedHeaderTd.eq(t).width($originalHeaderTd.eq(t).width()); 14 | } 15 | },200); 16 | } 17 | 18 | fixWidths(); 19 | 20 | $(window).resize(fixWidths); 21 | 22 | $fixedHeaderTd.add($originalHeaderTd).on('click', fixWidths); 23 | 24 | $(window).bind("scroll", function() { 25 | var $this = $(this); 26 | var leftOffset = $this.scrollLeft(); 27 | var offset = $this.scrollTop(); 28 | var tableOffset = $table.offset().top; 29 | 30 | if (offset >= tableOffset) { 31 | 32 | $fixedHeader.css('margin-left','-'+leftOffset+'px'); 33 | 34 | if($fixedHeader.is(":hidden")){ 35 | //Fixes discrepancy between fixedHeader and tables width upon filtering or changing sort role. 36 | if($fixedHeader.width() != $table.width()){ 37 | $fixedHeader.width($table.width()); 38 | for(var t=0;t<$fixedHeaderTd.length;t++){ 39 | $fixedHeaderTd.eq(t).width($originalHeaderTd.eq(t).width()); 40 | } 41 | } 42 | $fixedHeader.show(); 43 | } 44 | } 45 | else if (offset < tableOffset) { 46 | $fixedHeader.hide(); 47 | } 48 | }); 49 | })(jQuery); 50 | -------------------------------------------------------------------------------- /public/js/statistics_page.js: -------------------------------------------------------------------------------- 1 | // angular 2 | (function(angular, matchupData) { 3 | 4 | var statsApp = angular.module('statsPage', ['core']); 5 | 6 | statsApp.controller('data', ['$scope', '$location', function($scope, $location) { 7 | var urlParams = $location.search(); 8 | $scope.championData = matchupData.stats; 9 | 10 | $scope.search = { 11 | title: urlParams.search || '' 12 | }; 13 | 14 | $scope.roleSort = { 15 | role: urlParams.roleSort || '' 16 | }; 17 | 18 | $scope.Math = Math; 19 | 20 | $scope.indexNumber = function(index, dataLength){ 21 | var reverseList = false; 22 | if($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition'){ 23 | reverseList = true; 24 | } 25 | 26 | if((!reverseList && $scope.order === '-') || (reverseList && $scope.order === '')){ 27 | return index + 1; 28 | } else { 29 | return dataLength - index; 30 | } 31 | }; 32 | 33 | $scope.searchUrl = function() { 34 | $location.search('search', $scope.search.title); 35 | }; 36 | 37 | $scope.chosenRole = function() { 38 | $location.search('roleSort', $scope.roleSort.role); 39 | }; 40 | 41 | $scope.changeOrder = function(){ 42 | $scope.order = ($scope.order === '-') ? '' : '-'; 43 | $location.search('order', ($scope.order === '-') ? 'descend' : 'ascend'); 44 | }; 45 | 46 | $scope.changeSelection = function(property) { 47 | if (property !== 'role' && property !== 'title') { 48 | property = 'general.' + property; 49 | } 50 | if ($scope.sortExpression.sortBy === property) { 51 | $scope.order = ($scope.order === '-') ? '' : '-'; 52 | } else { 53 | $scope.sortExpression.lastSortBy = $scope.sortExpression.sortBy; 54 | $scope.sortExpression.sortBy = property; 55 | if ((($scope.sortExpression.lastSortBy === 'role' || $scope.sortExpression.lastSortBy === 'title' || $scope.sortExpression.lastSortBy === 'general.overallPosition') && property !== 'role' && property !== 'title' && property !== 'general.overallPosition') || (($scope.sortExpression.lastSortBy !== 'role' || $scope.sortExpression.lastSortBy !== 'title' || $scope.sortExpression.lastSortBy !== 'general.overallPosition') && (property === 'role' || property === 'title' || property === 'general.overallPosition'))) { 56 | $scope.order = ($scope.order === '-') ? '' : '-'; 57 | } 58 | } 59 | $location.search('sortBy', $scope.sortExpression.sortBy); 60 | $location.search('order', ($scope.order === '-') ? 'descend' : 'ascend'); 61 | }; 62 | $scope.determineSelected = function(property) { 63 | var propName = $scope.sortExpression.sortBy.split('.').reverse()[0]; 64 | if (property === propName) { 65 | return true; 66 | } 67 | }; 68 | 69 | $scope.determineOrder = function(direction) { 70 | if (direction === 'down') { 71 | if ($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition') { 72 | return $scope.order !== ''; 73 | } 74 | return $scope.order === ''; 75 | } 76 | 77 | if ($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition') { 78 | return $scope.order === ''; 79 | } 80 | return $scope.order !== ''; 81 | }; 82 | 83 | $scope.sortExpression = { 84 | sortBy: urlParams.sortBy || 'title', 85 | lastSortBy: 'role' 86 | }; 87 | $scope.order = ''; 88 | if (urlParams.order === 'descend') { 89 | $scope.order = '-'; 90 | } 91 | 92 | }]); 93 | })(angular, matchupData); -------------------------------------------------------------------------------- /public/opensearchdescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Champion.gg 5 | Search Champion.gg 6 | Champion.gg 7 | 9 | 10 | -------------------------------------------------------------------------------- /public/riot.html: -------------------------------------------------------------------------------- 1 | 36fabc0e-3ed0-461d-805b-0dcf0151fd6b -------------------------------------------------------------------------------- /public/template/typeahead/typeahead-popup.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /routes/api_static.js: -------------------------------------------------------------------------------- 1 | /* GET users listing. */ 2 | "use strict"; 3 | var apiData = require('../api_data'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | /** 8 | * Route set up for indepth mastery info. 9 | */ 10 | router.get('/masteries/:id', function(req, res) { 11 | res.setHeader('Cache-Control', 'public, max-age=86400'); //cache masteries for 3 minutes. 12 | 13 | var id = req.params.id; 14 | if(apiData.masteries.hasOwnProperty(id)){ 15 | res.json(apiData.masteries[id]); 16 | } else { 17 | res.statusCode = 404; 18 | res.send('invalid request'); 19 | } 20 | }); 21 | 22 | /** 23 | * Route set up for when we add indepth tool tips to items 24 | * --> not currently used. 25 | */ 26 | router.get('/items/:id', function(req, res) { 27 | var id = req.params.id; 28 | if(apiData.items.hasOwnProperty(id)){ 29 | res.json(apiData.items[id]); 30 | } else { 31 | res.statusCode = 404; 32 | res.send('invalid request'); 33 | } 34 | }); 35 | 36 | 37 | 38 | /** 39 | * Route set up for when we add indepth summoner spell information 40 | * --> not currently used. 41 | */ 42 | router.get('/summoners/:id', function(req, res) { 43 | var id = req.params.id; 44 | var match = false; 45 | for (var prop in apiData.summoners){ 46 | if(apiData.summoners.hasOwnProperty(prop) && apiData.summoners[prop].id === Number(id)){ 47 | res.json(apiData.summoners[prop]); 48 | match = true; 49 | break; 50 | } 51 | } 52 | if (!match){ 53 | res.statusCode = 404; 54 | res.send('invalid request'); 55 | } 56 | }); 57 | 58 | 59 | /** 60 | * Route set up for when we add summoner skill order information 61 | * --> not currently used - may be working, but commented out incase, 62 | * there are potential bugs. 63 | */ 64 | 65 | // router.get('/skills/:champion/:id', function(req, res) { 66 | // var id = req.params.id; 67 | // var champion = req.params.champion; 68 | // var championSkills; 69 | 70 | 71 | // var champFound = apiData.skills.hasOwnProperty(champion); 72 | // var keys = Object.keys(apiData.skills); 73 | // for (var i in keys) { 74 | // if (champFound) break; 75 | // if (keys[i].toLowerCase() === champion.toLowerCase()) { 76 | // champion = keys[i]; 77 | // champFound = true; 78 | // break; 79 | // } 80 | // } 81 | 82 | // if (champFound) { 83 | // championSkills = apiData.skills[champion].spells; 84 | // if (id in championSkills) { 85 | // res.json(championSkills[id]); 86 | // } 87 | // else { 88 | // res.statusCode = 404; 89 | // res.send(champion + " doesn't have that skill."); 90 | // } 91 | // } 92 | // }); 93 | 94 | 95 | module.exports = router; 96 | -------------------------------------------------------------------------------- /routes/champion.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var WebChampionPage = require('../models/web_champion_page.js'); 3 | var WebChampionRoles = require('../models/web_champion_roles.js'); 4 | var WebOverallRoleData = require('../models/web_overall_role_data.js'); 5 | var champList = require('../api_data/champions.json'); 6 | var roleHashTable = require('../logic/role_hash_table.js'); 7 | var produceError = require('../logic/produce_error.js'); 8 | var lowerCaseChamp = require('../logic/lower_case_champ.js'); 9 | var express = require('express'); 10 | var q = require('q'); 11 | var router = express.Router(); 12 | 13 | /** 14 | * pageData sets the general page info for the current route. 15 | * For example - championPage is the name of the angular module. 16 | */ 17 | var pageData = { 18 | appName: 'championPage', 19 | name: 'champion', 20 | description: '', 21 | title: '' 22 | }; 23 | 24 | function getChampionRoles(champKey){ 25 | var deferred = q.defer(); 26 | 27 | WebChampionRoles.findOne({ 28 | key: champKey 29 | }, function(err, doc) { 30 | if (err) { 31 | deferred.reject('server_maintenance'); 32 | 33 | } else if (!doc || doc.roles.length === 0) { 34 | deferred.reject('new_champion'); 35 | 36 | } else { 37 | deferred.resolve(doc.toObject()); 38 | } 39 | }); 40 | return deferred.promise; 41 | } 42 | 43 | function getChampionPage(champKey, role, res){ 44 | var deferred = q.defer(); 45 | 46 | WebChampionPage.findOne({ 47 | key: champKey, 48 | role: role 49 | }, function(err, doc) { 50 | if (err) { 51 | deferred.reject('server_maintenance'); 52 | 53 | } else if (!doc) { 54 | if (res){ 55 | res.redirect('/champion/'+champKey); 56 | } else { 57 | deferred.reject('server_maintenance'); 58 | } 59 | 60 | 61 | } else { 62 | deferred.resolve(doc.toObject()); 63 | } 64 | }); 65 | 66 | return deferred.promise; 67 | } 68 | 69 | function getOverallRoleData(role){ 70 | var deferred = q.defer(); 71 | 72 | WebOverallRoleData.findOne({ 73 | role: role 74 | }, function(err, doc) { 75 | if (err) { 76 | deferred.reject('server_maintenance'); 77 | 78 | } else if (!doc) { 79 | deferred.reject('server_maintenance'); 80 | 81 | } else { 82 | deferred.resolve(doc.toObject()); 83 | } 84 | }); 85 | 86 | return deferred.promise; 87 | } 88 | 89 | function generateResponseObj(champion, generalRole, championData){ 90 | var resObj = { 91 | pageData: pageData, 92 | champion: champion, 93 | generalRole: generalRole, 94 | championData: championData 95 | }; 96 | resObj.pageData.title = champion.name + ' ' + champion.roleTitle + ' Stats, Builds, Runes, Masteries and Counters'; 97 | resObj.pageData.description = "LoL Statistics, Builds, Runes, Masteries, Skill Orders, Counters and Matchups for "+champion.name+" when played "+champion.roleTitle+". Statistics include "+champion.name+ "'s Win Rate, Play Rate and Ban Rate. Counters include who "+champion.name + " " + champion.roleTitle + " is Strong or Weak Against."; 98 | return resObj; 99 | } 100 | 101 | router.get('/:champ', function(req, res, next) { 102 | var champKey = req.params.champ; 103 | var champion = {}; 104 | var generalRole = {}; 105 | var championData = {}; 106 | var champMatch = typeof champList[champKey] !== 'undefined'; 107 | 108 | if (champMatch || lowerCaseChamp(champKey)) { 109 | if (!champMatch) { 110 | champKey = lowerCaseChamp(champKey); 111 | } 112 | 113 | getChampionRoles(champKey) 114 | 115 | .then(function(doc){ 116 | champion = doc; 117 | champion.role = doc.roles[0].role; 118 | champion.roleTitle = roleHashTable.roleKey[doc.roles[0].role]; 119 | 120 | return q.all([ 121 | getChampionPage(champKey, champion.role), 122 | getOverallRoleData(champion.role) 123 | ]).spread(function(_championData_, _generalRole_){ 124 | championData = _championData_; 125 | generalRole = _generalRole_; 126 | res.render('champion', generateResponseObj(champion, generalRole, championData)); 127 | 128 | }, function(){ 129 | next(produceError('serverMaintenance', 503)); 130 | 131 | }); 132 | 133 | }, function(err){ 134 | if(err === 'new_champion'){ 135 | championData = null; 136 | generalRole = null; 137 | champion = { 138 | key: champKey, 139 | name: champList[champKey].name 140 | }; 141 | res.render('new_champion', generateResponseObj(champion, generalRole, championData)); 142 | } else { 143 | next(produceError('serverMaintenance', 503)); 144 | } 145 | }); 146 | 147 | } else { 148 | return next(produceError('champNotFound')); 149 | } 150 | }); 151 | 152 | router.get('/:champ/:role', function(req, res, next) { 153 | var champKey = req.params.champ; 154 | var champRole = req.params.role; //make function that converts role title to role key. 155 | var champion = {}; 156 | var generalRole = {}; 157 | var championData = {}; 158 | 159 | var champMatch = typeof champList[champKey] !== 'undefined'; 160 | if (typeof roleHashTable.roleList[champRole] !== 'undefined' && (champMatch || lowerCaseChamp(champKey))) { 161 | if (!champMatch) { 162 | champKey = lowerCaseChamp(champKey); 163 | } 164 | champRole = roleHashTable.roleList[champRole]; 165 | 166 | getChampionPage(champKey, champRole, res) 167 | .then(function(_championData_){ 168 | q.all([ 169 | getChampionRoles(champKey), 170 | getOverallRoleData(champRole) 171 | ]).spread(function(_championRoles_, _generalRole_){ 172 | champion = _championRoles_; 173 | champion.role = champRole; 174 | champion.roleTitle = roleHashTable.roleKey[champRole]; 175 | championData = _championData_; 176 | generalRole = _generalRole_; 177 | res.render('champion', generateResponseObj(champion, generalRole, championData)); 178 | 179 | }, function(){ 180 | next(produceError('serverMaintenance', 503)); 181 | 182 | }); 183 | 184 | }, function(){ 185 | next(produceError('serverMaintenance', 503)); 186 | 187 | }); 188 | 189 | } else { 190 | return next(produceError('champNotFound')); 191 | } 192 | 193 | }); 194 | 195 | module.exports = router; 196 | -------------------------------------------------------------------------------- /routes/faq.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var express = require('express'); 3 | var router = express.Router(); 4 | 5 | router.get('/', function(req, res) { 6 | res.render('faq', { 7 | pageData: { 8 | appName: 'core', 9 | name: 'faq', 10 | title: 'All your questions answered here!' 11 | } 12 | }); 13 | }); 14 | 15 | module.exports = router; -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var WebChampionRoles = require('../models/web_champion_roles.js'); 3 | var WebHomePageSummaries = require('../models/web_home_page_summaries.js'); 4 | var lowerCaseChamp = require('../logic/lower_case_champ.js'); 5 | var produceError = require('../logic/produce_error.js'); 6 | var champList = require('../api_data/champions.json'); 7 | var express = require('express'); 8 | var router = express.Router(); 9 | 10 | router.get('/', function(req, res, next) { 11 | var retrievedYet = false; 12 | var champData = []; 13 | var summaries = []; 14 | 15 | function responseObj() { 16 | res.render('index', { 17 | summaries: summaries, 18 | data: champData, 19 | pageData: { 20 | appName: 'core', 21 | name: 'home', 22 | title: 'LoL Champion Stats, Builds, Runes, Masteries, Counters and Matchups!', 23 | description:'Champion.gg provides League of Legends champion statistics, builds, runes, masteries, skill orders and counters by role - including Win Rate, Ban Rate, Play Rate and much more!' 24 | } 25 | }); 26 | } 27 | 28 | WebChampionRoles.find({}).sort({ 29 | name: 1 30 | }).exec(function(err, data) { 31 | if (err) { 32 | return next(produceError('serverMaintenance', 503)); 33 | } else if (!data) { 34 | return next(produceError('serverMaintenance', 503)); 35 | } else { 36 | champData = data; 37 | if (retrievedYet) { 38 | responseObj(); 39 | } else { 40 | retrievedYet = true; 41 | } 42 | } 43 | }); 44 | 45 | WebHomePageSummaries.findOne({ 46 | id: 1 47 | }, function(err, data) { 48 | if (err) { 49 | return next(produceError('serverMaintenance', 503)); 50 | } else if (!data) { 51 | return next(produceError('serverMaintenance', 503)); 52 | } else { 53 | summaries = data.data; 54 | if (retrievedYet) { 55 | responseObj(); 56 | } else { 57 | retrievedYet = true; 58 | } 59 | } 60 | }); 61 | }); 62 | 63 | router.get('/:champ', function(req, res, next) { 64 | var champKey = req.params.champ; 65 | if (typeof champList[champKey] !== 'undefined' || lowerCaseChamp(champKey)) { 66 | res.redirect('/champion/' + req.params.champ); 67 | } else { 68 | return next(produceError('pageNotFound', 404)); 69 | } 70 | }); 71 | 72 | module.exports = router; -------------------------------------------------------------------------------- /routes/matchup.js: -------------------------------------------------------------------------------- 1 | /* GET users listing. */ 2 | "use strict"; 3 | var WebMatchupPage = require('../models/web_matchup_page.js'); 4 | var produceError = require('../logic/produce_error.js'); 5 | var lowerCaseChamp = require('../logic/lower_case_champ.js'); 6 | var roleHashTable = require('../logic/role_hash_table.js'); 7 | var champList = require('../api_data/champions.json'); 8 | var express = require('express'); 9 | var router = express.Router(); 10 | 11 | router.get('/:champ1/:champ2/:role', function(req, res, next) { 12 | 13 | var champ1 = req.params.champ1; 14 | var champ2 = req.params.champ2; 15 | var champRole = req.params.role; 16 | var pageData, votes; 17 | var champ1Match = typeof champList[champ1] !== 'undefined'; 18 | var champ2Match = typeof champList[champ2] !== 'undefined'; 19 | 20 | function matchupResponse() { 21 | var title = generateTitle(); 22 | res.render('matchup', { 23 | data: pageData, 24 | pageData: { 25 | appName: 'matchupPage', 26 | name: 'matchups', 27 | title: title, 28 | description: title 29 | } 30 | }); 31 | } 32 | 33 | function generateTitle() { 34 | if (pageData.role === 'SYNERGY') { 35 | return pageData.champ1.name + ' ' + pageData.champ1.roleTitle + ' Synergy With ' + pageData.champ2.name + ' ' + pageData.champ2.roleTitle; 36 | } 37 | return pageData.champ1.name + ' ' + pageData.champ1.roleTitle + ' against ' + pageData.champ2.name + ' ' + pageData.champ2.roleTitle; 38 | } 39 | 40 | if ((champ1Match || lowerCaseChamp(champ1)) && (champ2Match || lowerCaseChamp(champ2)) && typeof roleHashTable.roleList[champRole] !== 'undefined') { 41 | if (!champ1Match) { 42 | champ1 = lowerCaseChamp(champ1); 43 | } 44 | if (!champ2Match) { 45 | champ2 = lowerCaseChamp(champ2); 46 | } 47 | champ1 = champList[champ1].id; 48 | champ2 = champList[champ2].id; 49 | champRole = roleHashTable.roleList[champRole]; 50 | 51 | WebMatchupPage.findOne({ 52 | 'champ1.id': champ1, 53 | 'champ2.id': champ2, 54 | role: champRole 55 | }, function(err, doc) { 56 | 57 | if (err) { 58 | return next(produceError('serverMaintenance', 503)); 59 | } else if (!doc) { 60 | return next(produceError('invalidMatchup')); 61 | } else { 62 | pageData = doc; 63 | pageData.roleTitle = roleHashTable.roleKey[pageData.role]; 64 | matchupResponse(); 65 | } 66 | }); 67 | 68 | } else { 69 | return next(produceError('invalidMatchup')); 70 | } 71 | }); 72 | 73 | module.exports = router; -------------------------------------------------------------------------------- /routes/matchup_json.js: -------------------------------------------------------------------------------- 1 | /* GET users listing. */ 2 | "use strict"; 3 | var WebMatchupPage = require('../models/web_matchup_page.js'); 4 | var roleHashTable = require('../logic/role_hash_table.js'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | 8 | router.get('/:champ1/:champ2/:role', function(req, res) { 9 | 10 | var champ1 = req.params.champ1; 11 | var champ2 = req.params.champ2; 12 | var champRole = req.params.role; 13 | 14 | if (typeof roleHashTable.roleKey[champRole] !== 'undefined') { 15 | WebMatchupPage.findOne({ 16 | 'champ1.id': champ1, 17 | 'champ2.id': champ2, 18 | role: champRole 19 | }, function(err, data) { 20 | if (err) { 21 | console.log(err); 22 | res.send('Try Again'); 23 | } else { 24 | res.json(data); 25 | } 26 | }); 27 | } else { 28 | res.statusCode = 404; 29 | res.send('invalid request'); 30 | } 31 | }); 32 | 33 | module.exports = router; -------------------------------------------------------------------------------- /routes/statistics.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var WebStatisticsPage = require('../models/web_statistics_page.js'); 3 | var produceError = require('../logic/produce_error.js'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | router.get('/', function(req, res, next) { 7 | 8 | WebStatisticsPage.find({}, function(err, doc) { 9 | if (err) { 10 | return next(produceError('serverMaintenance', 503)); 11 | } else if (!doc) { 12 | return next(produceError('serverMaintenance', 503)); 13 | } else { 14 | res.render('statistics', { 15 | data: doc, 16 | pageData: { 17 | appName: 'statsPage', 18 | name: 'stats', 19 | title: 'League of Legends Stats by Champion Role for the Current Patch', 20 | description: "League of Legends Statistics including Win Rate, Ban Rate, Play Rate, Kills, Deaths by Champions and the roles they play." 21 | } 22 | }); 23 | } 24 | }); 25 | }); 26 | 27 | module.exports = router; -------------------------------------------------------------------------------- /update_data.sh: -------------------------------------------------------------------------------- 1 | defaultmsg="updated data" 2 | read -p "Please enter a git message (default : '$defaultmsg'):" gitmsg 3 | if [ "$gitmsg" == "" ]; then 4 | gitmsg="$defaultmsg" 5 | fi 6 | echo $gitmsg 7 | 8 | mongodump --db championgg --collection weboverallstats --out ./db 9 | mongodump --db championgg --collection webchampionpages --out ./db 10 | mongodump --db championgg --collection webchampionroles --out ./db 11 | mongodump --db championgg --collection webmatchuppages --out ./db 12 | mongodump --db championgg --collection weboverallroledatas --out ./db 13 | mongodump --db championgg --collection webhomepagesummaries --out ./db 14 | mongodump --db championgg --collection webstatisticspages --out ./db 15 | 16 | grunt production 17 | 18 | git add -A && git commit -m "$gitmsg" && git push origin master 19 | 20 | cd ../ssh && sh ssh_in.sh; -------------------------------------------------------------------------------- /update_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/ubuntu/championweb/ 3 | git remote update 4 | 5 | LOCAL=$(git rev-parse @) 6 | REMOTE=$(git rev-parse @{u}) 7 | BASE=$(git merge-base @ @{u}) 8 | 9 | if [ $LOCAL = $REMOTE ]; then 10 | echo "Up-to-date" 11 | elif [ $LOCAL = $BASE ]; then 12 | echo "Behind" 13 | git pull 14 | sh ./bin/update_server.sh 15 | elif [ $REMOTE = $BASE ]; then 16 | echo "Ahead" 17 | git reset --hard origin/master 18 | sh ./bin/update_server.sh 19 | else 20 | echo "Diverged" 21 | git reset --hard origin/master 22 | sh ./bin/update_server.sh 23 | fi 24 | -------------------------------------------------------------------------------- /views/champion.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 | 3 |
4 |
5 |
6 |
7 | 8 |
9 | <% include champion/champion_image_roles.ejs %> 10 |
11 | 12 |
13 | <% include champion/champion_statistics.ejs %> 14 |
15 | 16 |
17 | <% include champion/winrate_playrate_damage_advert_trinket.ejs %> 18 |
19 | 20 |
21 | <% include champion/gamelength_experience_summoners.ejs %> 22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 | <% if (championData.skills.highestWinPercent.winPercent) { %> 36 | <% include champion/skill_order.ejs %> 37 | <% } %> 38 |
39 |
40 | <% if (championData.runes.highestWinPercent) { %> 41 | <% include champion/runes.ejs %> 42 | <% } %> 43 |
44 |
45 | 46 |
47 |
48 |
49 | <% if (championData.items.highestWinPercent) { %> 50 | <% include champion/core_build.ejs %> 51 | <% } %> 52 |
53 |
54 | <% include champion/first_items.ejs %> 55 |
56 |
57 |
58 | <% if (championData.masteries.mostGames.masteries[0]) { %> 59 | <% include champion/masteries.ejs %> 60 | <% } %> 61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 | <% include champion/counters_matchups.ejs %> 69 |
70 | 71 |
72 |
73 |
74 |
75 | <% include champion/advertisement.ejs %> 76 |
77 |
78 | <% include champion/reddit.ejs %> 79 |
80 |
81 |
82 |
83 | 84 |
85 | 86 | 93 | <% include scripts.ejs %> 94 | 95 | <% include footer.ejs %> 96 | -------------------------------------------------------------------------------- /views/champion/advertisement.ejs: -------------------------------------------------------------------------------- 1 |

Advertising Support

2 | 23 | -------------------------------------------------------------------------------- /views/champion/champion_image_roles.ejs: -------------------------------------------------------------------------------- 1 | 2 |

<%= champion.name %>

3 | -------------------------------------------------------------------------------- /views/champion/champion_statistics.ejs: -------------------------------------------------------------------------------- 1 |

Statistics

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <% for(var i=0; i 13 | > 14 | 19 | 20 | 21 | 27 | 31 | 32 | 33 | <% } %> 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 |
TypeAverage Role PlacementPlacement Change this patch
15 | 16 | <%= championData.general[i].title %> 17 | 18 | <%= championData.general[i].val %><% if(i < 3){ %>%<%}%> 22 | 23 | <%= championData.general[i].position %> 24 | 25 | / <%= generalRole.totalNumber %> 26 | 28 | 29 | 30 | <%- Math.abs(championData.general[i].change) %>
Overall Placement<%= championData.overallPosition.position %> / <%= generalRole.totalNumber %> 39 | <%- Math.abs(championData.overallPosition.change) %>
43 | 44 | 45 |

Overall Champion Matrix

46 |
47 | 48 |
49 |
-------------------------------------------------------------------------------- /views/champion/core_build.ejs: -------------------------------------------------------------------------------- 1 |

Most Frequent Core Build

2 |
3 | <% for(var k=0; k 4 | 5 | 6 | 7 | <% if (k!==5){ %> 8 | > 9 | <%}%> 10 | <% } %> 11 |
12 | <%= championData.items.mostGames.winPercent %>% Win Rate | <%= championData.items.mostGames.games %> Games 13 |
14 |
15 | 16 |

Highest Win % Core Build

17 |
18 | <% for(var l=0; l 19 | 20 | 21 | 22 | <% if (l!==5){ %> 23 | > 24 | <%}%> 25 | <% } %> 26 |
27 | <%= championData.items.highestWinPercent.winPercent %>% Win Rate | <%= championData.items.highestWinPercent.games %> Games 28 |
29 |
30 | -------------------------------------------------------------------------------- /views/champion/counters_matchups.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | Display matchups with at least 6 | 14 | games. 15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 |

<%= champion.roleTitle %> Champions that Counter <%= champion.name %>

23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 |
31 |

<%= champion.roleTitle %> Champions that <%= champion.name %> Counters

32 | 33 |
34 | 35 | 36 |
37 | 38 |
39 | 40 | <%if (champion.role === 'DUO_SUPPORT' || champion.role === 'DUO_CARRY') { %> 41 | 42 |
43 |
44 |
45 |

<%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Counter <%= champion.name %>

46 |
47 | 48 | 49 |
50 | 51 |
52 |
53 |

<%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that <%= champion.name %> Counters

54 |
55 | 56 | 57 |
58 |
59 | 60 |
61 |
62 |
63 |

<%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Synergise poorly with <%= champion.name %>

64 |
65 | 66 | 67 |
68 | 69 |
70 |
71 |

<%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Synergise well with <%= champion.name %>

72 |
73 | 74 | 75 |
76 |
77 | 78 | <% } %> 79 | 80 |
81 | 82 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /views/champion/first_items.ejs: -------------------------------------------------------------------------------- 1 |

Most Frequent Starters

2 |
3 | <% for(var k=0; k 4 | 5 | 6 | 7 | <% } %> 8 |
9 | <%= championData.firstItems.mostGames.winPercent %>% Win Rate | <%= championData.firstItems.mostGames.games %> Games 10 |
11 |
12 |

Highest Win % Starters

13 |
14 | <% for(var l=0; l 15 | 16 | 17 | 18 | <% } %> 19 |
20 | <%= championData.firstItems.highestWinPercent.winPercent %>% Win Rate | <%= championData.firstItems.highestWinPercent.games %> Games 21 |
22 |
-------------------------------------------------------------------------------- /views/champion/gamelength_experience_summoners.ejs: -------------------------------------------------------------------------------- 1 |

Win Rate % by Game Length

2 |
3 | 4 |
5 |
6 |

7 | Win Rate % by Games Played 8 | (2014-2015 Ranked Experience) 9 |

10 |
11 | 12 |
13 |
14 | 15 |

Playerbase Experience Distribution

16 |
17 |
18 | 19 |
20 |
21 | 2015-2016 Ranked Games 22 |
23 |
24 |
25 | 26 |

Most Frequent Summoners

27 |
28 | 29 | 30 | 31 |
<%= championData.summoners.mostGames.winPercent %>% Win Rate | <%= championData.summoners.mostGames.games %> Games
32 |
33 | 34 |

Highest Win % Summoners

35 |
36 | 37 | 38 |
<%= championData.summoners.highestWinPercent.winPercent %>% Win Rate | <%= championData.summoners.highestWinPercent.games %> Games
39 |
40 | -------------------------------------------------------------------------------- /views/champion/masteries.ejs: -------------------------------------------------------------------------------- 1 |

Most Frequent Masteries

2 |
3 | <% for (var k = 0; k 4 |
5 |
6 | <%= championData.masteries.mostGames.masteries[k].tree %> - <%= championData.masteries.mostGames.masteries[k].total %> 7 |
8 | <% for (var z = 1; z<=6; z++){ %> 9 | <% var masteryColumn = championData.masteries.mostGames.masteries[k].data['row'+z]; %> 10 |
11 | <% for(var y = 0; y < masteryColumn.length; y++) { %> 12 | <% if(masteryColumn[y].mastery){ %> 13 | <% if(masteryColumn[y].points){ %> 14 |
15 |
16 | <% for(var p = 0; p < masteryColumn[y].points; p++){ %> 17 |
18 | <% } %> 19 |
20 |
21 | <% } else { %> 22 |
23 |
24 | <% } %> 25 | <% if(z % 2 == 1 && y == 0){ %> 26 |
27 | <% } %> 28 | <% } %> 29 | <% } %> 30 |
31 | <% } %> 32 |
33 | <% } %> 34 |
35 |
36 | <%= championData.masteries.mostGames.winPercent %>% Win Rate | <%= championData.masteries.mostGames.games %> Games 37 |
38 | 39 | 40 |

Highest Win % Masteries

41 |
42 | <% for (var k = 0; k 43 |
44 |
45 | <%= championData.masteries.highestWinPercent.masteries[k].tree %> - <%= championData.masteries.highestWinPercent.masteries[k].total %> 46 |
47 | <% for (var z = 1; z<=6; z++){ %> 48 | <% var masteryColumn = championData.masteries.highestWinPercent.masteries[k].data['row'+z]; %> 49 |
50 | <% for(var y = 0; y < masteryColumn.length; y++) { %> 51 | <% if(masteryColumn[y].mastery){ %> 52 | <% if(masteryColumn[y].points){ %> 53 |
54 |
55 | <% for(var p = 0; p < masteryColumn[y].points; p++){ %> 56 |
57 | <% } %> 58 |
59 |
60 | <% } else { %> 61 |
62 |
63 | <% } %> 64 | <% if(z % 2 == 1 && y == 0){ %> 65 |
66 | <% } %> 67 | <% } %> 68 | <% } %> 69 |
70 | <% } %> 71 |
72 | <% } %> 73 |
74 |
75 | <%= championData.masteries.highestWinPercent.winPercent %>% Win Rate | <%= championData.masteries.highestWinPercent.games %> Games 76 |
77 | -------------------------------------------------------------------------------- /views/champion/reddit.ejs: -------------------------------------------------------------------------------- 1 |

Recent <%= champion.name %> Threads on Reddit

2 |
3 |
4 |
5 |

6 | 7 | {{thread.data.title}} 8 | 9 |

10 | 11 | View Summary 12 | 13 | 14 | {{thread.data.num_comments}} comments. 15 | 16 | 17 | Created on {{thread.data.created * 1000 | date:'d MMMM yyyy'}} 18 | 19 |
20 |
21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /views/champion/runes.ejs: -------------------------------------------------------------------------------- 1 |

Most Frequent Runes

2 |
3 | <% for(var i = 0; i < championData.runes.mostGames.runes.length; i++){ %> 4 |
5 |
6 | <%= championData.runes.mostGames.runes[i].number %>x <%= championData.runes.mostGames.runes[i].name %> 7 | <%= championData.runes.mostGames.runes[i].description %> 8 |
9 |
10 |
11 |
12 | <% } %> 13 |
14 |
15 | <%= championData.runes.mostGames.winPercent %>% Win Rate | <%= championData.runes.mostGames.games %> Games 16 |
17 | 18 | 19 |

Highest Win % Runes

20 |
21 | <% for(var i = 0; i < championData.runes.highestWinPercent.runes.length; i++){ %> 22 |
23 |
24 | <%= championData.runes.highestWinPercent.runes[i].number %>x <%= championData.runes.highestWinPercent.runes[i].name %> 25 | <%= championData.runes.highestWinPercent.runes[i].description %> 26 |
27 |
28 |
29 |
30 | <% } %> 31 |
32 |
33 | <%= championData.runes.highestWinPercent.winPercent %>% Win Rate | <%= championData.runes.highestWinPercent.games %> Games 34 |
-------------------------------------------------------------------------------- /views/champion/skill_order.ejs: -------------------------------------------------------------------------------- 1 |

Most Frequent Skill Order

2 |
3 |
4 |
5 |
6 | <% for (var d = 0; d < 18; d++){ %> 7 |
8 | <%= d + 1 %> 9 |
10 | <% } %> 11 |
12 |
13 | <% for (var i = 0 ; i < 4; i++){ %> 14 |
15 | 16 |
17 | <% var skillGames = championData.skills.mostGames.order %> 18 | <% for (var b = 0 ; b < skillGames.length; b++){ %> 19 |
20 | <%= skillGames[b][0] === ''+(i+1) ? championData.skills.skillInfo[i].key : "" %> 21 | <% if(skillGames[b].length === 3 && skillGames[b][0] === ''+(i+1)){ %> 22 | <%= championData.skills.skillInfo[skillGames[b][2]-1].key %> 23 | <% } %> 24 |
25 | <% } %> 26 |
27 |
28 | <% } %> 29 |
30 |
31 | <%= championData.skills.mostGames.winPercent %>% Win Rate | <%= championData.skills.mostGames.games %> Games 32 |
33 | 34 | 35 |

Highest Win % Skill Order

36 |
37 |
38 |
39 |
40 | <% for (var d = 0; d < 18; d++){ %> 41 |
42 | <%= d + 1 %> 43 |
44 | <% } %> 45 |
46 |
47 | <% for (var i = 0 ; i < 4; i++){ %> 48 |
49 | 50 |
51 | <% var skillGames = championData.skills.highestWinPercent.order %> 52 | <% for (var b = 0 ; b < skillGames.length; b++){ %> 53 |
54 | <%= skillGames[b][0] === ''+(i+1) ? championData.skills.skillInfo[i].key : "" %> 55 | <% if(skillGames[b].length === 3 && skillGames[b][0] === ''+(i+1)){ %> 56 | <%= championData.skills.skillInfo[skillGames[b][2]-1].key %> 57 | <% } %> 58 |
59 | <% } %> 60 |
61 |
62 | <% } %> 63 |
64 |
65 | <%= championData.skills.highestWinPercent.winPercent %>% Win Rate | <%= championData.skills.highestWinPercent.games %> Games 66 |
-------------------------------------------------------------------------------- /views/champion/viktor_upgrade.ejs: -------------------------------------------------------------------------------- 1 |
2 |

Most Frequent Upgrades

3 | <% var viktor = championData.unique.mostGames; %> 4 | <% for(var i = 0; i 5 |
6 | 7 | <%= championData.skills.skillInfo[viktor.order[i]-1].key %> 8 | 9 |
10 | <% if (i!==2){ %> > <%}%> 11 | 12 | <% } %> 13 |
<%= viktor.winPercent %>% Win Rate | <%= viktor.games %> Games
14 |
15 |
16 |

Highest Win % Upgrades

17 | 18 | <% var viktor = championData.unique.highestWinPercent; %> 19 | <% for(var i = 0; i 20 |
21 | 22 | <%= championData.skills.skillInfo[viktor.order[i]-1].key %> 23 | 24 |
25 | <% if (i!==2){ %> > <%}%> 26 | 27 | <% } %> 28 |
<%= viktor.winPercent %>% Win Rate | <%= viktor.games %> Games
29 |
-------------------------------------------------------------------------------- /views/champion/winrate_playrate_damage_advert_trinket.ejs: -------------------------------------------------------------------------------- 1 |

Win Rate % By Patch

2 |
3 | 4 |
5 |
6 |

Play Rate % By Patch

7 |
8 | 9 |
10 |
11 |
12 | 13 |
14 | 17 |
18 |
19 | 20 |

Damage Composition

21 |
22 |
23 |
24 |
25 |
26 | 31 | 32 |
33 |

Trinket Stats

34 | <% var trinkets = championData.trinkets; %> 35 | <% for(var i = 0; i 36 |
37 | 38 |
39 | <%= trinkets[i].winPercent %>% 40 | Win Rate 41 | <%= trinkets[i].games %> 42 | Games
43 |
44 | <% } %> 45 | 46 |
47 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 | 3 |
4 |

Teemo, we have a problem...

5 |

<%= message %>

6 |
7 |
8 | Think something on the site is broken? Please send me a message... (on reddit) /u/joeldo 9 |
10 | <% include scripts.ejs %> 11 | 12 | <% include footer.ejs %> 13 | -------------------------------------------------------------------------------- /views/faq.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 | 3 |

Frequently Asked Questions

4 | 5 |
6 |
7 |
8 |

What stats do you use?

9 |

Champion.gg uses game data from Platinum to Challenger from NA, EUW, EUNE and Korea for the current patch, available through Riot's API 10 | 11 |

What advantage does Champion.gg have over any other statistic sites?

12 |

Most other LoL statistic websites are lacking in three areas. First, data doesn't necessarily reflect a champion's true performance if their play in certain roles isn't taken into account - this is something Champion.gg is very well equipped for; any champion can be selected in their played role to get tailored statistics.

13 |

14 | Second, many statistical websites present their data over the past month/week/day. This gives an inaccurate view of the champion on the current patch. This is because the sample size can cover more than the current patch or be too small (in the case of a day). Champion.gg bases all data off the current patch apart from the player experience section - which uses the past 2 patches. 15 |

16 |

17 | Third, many statistic websites don't show the win rates when a champion is (potentially) played at their best - Champion.gg provides win rates for the best builds, masteries, runes, summoners and skill order. 18 |

19 | 20 |

How is the Overall Placement/Performance Ranking determined?

21 |

22 | Thanks for asking! The overall performance ranking takes more than win rate into account! Depending on the particular role, different attributes (such as Win Rate, Play Rate, Ban Rate, Kills, Deaths, CS, Damage Dealt/Taken etc.) are weighted at different levels to provide an overview of how the champion performs as a whole. For example, an ADC champion has a higher weighting for 'Damage Dealt' than that of a support champion. 23 |

24 | 25 |

How is the Counters Statistical Rating determined?

26 |

27 | The statistical rating of counters is determined in a similar way as the overall performance ranking, but it also takes into account how well a champion normally performs, and the effect the particular matchup has on this performance. It also takes into account who has a stronger performance in the matchup. 28 |

29 | 30 |

How are the win rates/play rates of full builds calculated?

31 |

32 | Because the number of games where a full build is successfully completed is very low, it is hard to accurately determine the overall win rate of a given build. This is because completed builds typically have extremely high win rates (players that are ahead have more items completed). 33 |

34 |

35 | Therefore, Champion.gg places a similar weighting for partially completed builds that follow the same build path as the full build. This helps provide a better overview of the build as a whole as it takes into account all stages of the build. In order to further stabilize the win rate, it is normalized against the win rate of the champions role. 36 |

37 | 38 | 39 |
40 |
41 |

How do you determine what role a champion plays in?

42 |

43 | Champion.gg uses Riot's API which provides the lane and role of a particular champion. This is based on the areas a particular champion spends the majority of the early game/laning phase. 44 |

45 | 46 |

How frequently is data updated?

47 |

48 | Data is updated as soon as possible whenever a new patch is released (usually within 3 days). Towards the end of a patch, data is updated less frequently--Champion.gg has over 300gb of champion data, so processing can take around 15 hours. 49 |

50 | 51 |

Why does x champion have such a high/low win rate?

52 |

53 | Champions such as Urgot are seen very very rarely in Platinum+ games. Consequently, there isn't always a large enough sample size to determine whether the statistics are a true reflection of the champion's performance. If small sample size isn't an issue, chances are Riot is having balancing issues. 54 |

55 | 56 |

How can I support Champion.gg?

57 |

58 | Champion.gg is and always will be free to use - it does, however, cost to process, download, and host gigabytes of data each day. There are a number of ways you can help out! You can share Champion.gg with your friends/groups, you can white list Champion.gg through your adblocker, or for those who don't like advertisements, you can consider donating. 59 |

60 | 61 |

Is Champion.gg open source?

62 |

63 | I have the web development/design side of Champion.gg on GitHub - You can take a look for yourself! The data aggregation/processing side of things is in a private repo until I clean it up. 64 |

65 | 66 |

What programming language(s) did you use?

67 |

68 | Champion.gg is a full JavaScript endeavour, utilising Node.js/Express on the back end with MongoDB serving the data. The front end uses Angular.js as its primary framework. Data aggregation/analysis was performed with MongoDB's aggregation framework / Node.js. 69 |

70 | 71 |

How can I contact you?

72 |

73 | Send me a message on Reddit at /u/joeldo, through the Champion.gg Facebook page or GitHub 74 |

75 |
76 |
77 |
78 | 79 | 80 | <% include scripts.ejs %> 81 | 82 | 83 | 84 | 85 | <% include footer.ejs %> 86 | -------------------------------------------------------------------------------- /views/footer.ejs: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 21 |
22 | 29 | 30 | 31 | 47 | 48 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | -------------------------------------------------------------------------------- /views/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Champion.gg - <%= pageData.title %> 8 | 9 | 10 | <% if(process.env.NODE_ENV === 'production') { %> 11 | 12 | <% } else { %> 13 | 14 | 15 | <% } %> 16 | 17 | 28 | 31 | 32 | 36 | 37 | 38 | <%if (pageData.name === 'home') { %> 39 | 53 | 54 | 65 | <% } else if (pageData.name === 'champion') { %> 66 | 80 | 81 | 93 | <% } else { %> 94 | 108 | 109 | 119 | <% } %> 120 | 121 | 122 | 123 |
124 |
125 | 144 | 182 |
183 | 184 | 185 |
186 | <%if (pageData.name === 'home') { %> 187 | 188 |
189 | 192 |
193 | <% } else if (pageData.name === 'champion') { %> 194 | 195 |
196 | 199 |
200 | <% } else { %> 201 | 202 |
203 | 206 |
207 | <% } %> 208 |
209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 |
3 |
4 |
5 | 6 | Win Rates 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% for(var y=0;y 18 | 19 | 20 | 26 | 33 | 34 | 35 | <% } %> 36 | 37 |
RoleHighestLowest
<%= summaries[y].title %> 21 |
22 | <%= summaries[y].highestWinRate.name %> 23 |
24 | <%= summaries[y].highestWinRate.value %>% 25 |
27 | 28 |
29 | <%= summaries[y].lowestWinRate.name %> 30 |
31 | <%= summaries[y].lowestWinRate.value %>% 32 |
38 |
39 |
40 | Overall Performance Ranking 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | <% for(var l=0;l 51 | 52 | 53 | 58 | 63 | 64 | 65 | <% } %> 66 | 67 |
RoleBestWorst
<%= summaries[l].title %> 54 |
55 | <%= summaries[l].bestOverall.name %>
56 | 57 |
59 | 60 |
61 | <%= summaries[l].worstOverall.name %>
62 |
68 | 69 |
70 |
71 | Champion Ranking Changes 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | <% for(var x=0;x 83 | 84 | 85 | 90 | 96 | 97 | 98 | <% } %> 99 | 100 |
RoleMost ImprovedLeast Improved
<%= summaries[x].title %> 86 |
87 | <%= summaries[x].mostImproved.name %>
88 | <%=summaries[x].mostImproved.difference %> 89 |
91 | 92 |
93 | <%= summaries[x].leastImproved.name %>
94 | <%=summaries[x].leastImproved.difference %> 95 |
101 |
102 |
103 | 104 | 105 | 106 |
107 | Choose a champion for in-depth statistics and counters. 108 |
109 | <% for(var i=0;i 110 |
111 | 122 |
123 | <% } %> 124 |
125 |
126 | 127 |
128 | 131 |
132 | 133 | 134 |
135 | 138 |
139 |
140 | 141 | 142 |
143 |
144 | 145 | <% include scripts.ejs %> 146 | 147 | <% include footer.ejs %> 148 | -------------------------------------------------------------------------------- /views/matchup.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 |
3 |

4 | <%= data.champ1.name %> <%= data.champ1.roleTitle %> <%= (data.role==='SYNERGY')? 'Synergy With' : 'Against'%> <%= data.champ2.name %> <%= data.champ2.roleTitle %> 5 |

6 |
7 | <% for(var t=1; t<3; t++) {%> 8 | 9 | 22 | <% } %> 23 |
24 | 25 | 26 |

27 | <% if(data.role !== 'SYNERGY'){ %> 28 | <% if(data.champ1.performance >= 5.75){ %> <%=data.champ1.name%> has the advantage! <% } %> 29 | <% if(data.champ1.performance <= 4.25) { %> <%=data.champ2.name%> has the advantage! <% } %> 30 | <% if(!(data.champ1.performance >= 5.75)&&!(data.champ1.performance <= 4.25)){ %> 31 | Skill based matchup! 32 | <% } %> 33 | <% } else { %> 34 | <% if(data.champ1.performance >= 5.75){ %> <%=data.champ1.name%> and <%=data.champ2.name%> have Strong Synergy! <% } %> 35 | <% if(data.champ1.performance <= 4.25) { %> <%=data.champ1.name%> and <%=data.champ2.name%> have Weak Synergy! <% } %> 36 | <% if(!(data.champ1.performance >= 5.75)&&!(data.champ1.performance <= 4.25)){ %> 37 | Standard Synergy 38 | <% } %> 39 | <% } %> 40 | 41 |

42 | 43 | 44 | 45 | Total Games Analyzed: <%= data.totalGames %>
46 |
47 |

Statistical Performance

48 |
49 | 50 |
51 |
52 |
53 | 54 | 55 | <% if(data.role !== 'ADCSUPPORT' && data.role !== 'SYNERGY'){ %> 56 |
57 |
58 |
59 |

Matchup Performance

60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 |

Gold Over Time

68 |
69 | 70 |
71 |
72 |
73 | 74 |
75 |
76 | <% } %> 77 | 78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | <% for(var i=0; i 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | <% } %> 105 | 106 | 107 |
TypeMatchup AverageChangeMatchup AverageChange
<%=data.general[i].title%><%=data.general[i].champ1.val%><% if (data.general[i].title === 'Win Rate'){%>%<% } %> <%- Math.abs(data.general[i].champ1.change) %><%=data.general[i].champ2.val%><% if (data.general[i].title === 'Win Rate'){%>%<% } %> <%- Math.abs(data.general[i].champ2.change) %>
108 | 109 |
110 |
111 |
112 | 113 |
114 |
115 |
116 | 117 | 123 | 124 | <% include scripts.ejs %> 125 | <% include footer.ejs %> 126 | -------------------------------------------------------------------------------- /views/new_champion.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 | 3 |
4 |
5 |
6 |
7 | 8 |

<%= champion.name %>

9 |
10 |
11 |

12 | We're currently in the process of generating stats for <%= champion.name %> - Check Back Soon! 13 |

14 |
15 |
16 |
17 |
18 |
19 | Think something on the site is broken? Please send me a message... (on reddit) /u/joeldo 20 |
21 | <% include scripts.ejs %> 22 | 23 | <% include footer.ejs %> 24 | -------------------------------------------------------------------------------- /views/scripts.ejs: -------------------------------------------------------------------------------- 1 | <% if(process.env.NODE_ENV === 'production') { %> 2 | 3 | <% } else { %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% } %> 18 | 19 | -------------------------------------------------------------------------------- /views/statistics.ejs: -------------------------------------------------------------------------------- 1 | <% include header.ejs %> 2 | 3 | 4 |
5 |

Current Patch Statistics

7 |
8 |
9 | 10 |
11 |
12 | Sort Role 13 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
RankChampionRoleWin PercentPlay PercentBan RatePlayerbase Avg. GamesKillsDeathsAssistsLargest Killing SpreeDamage DealtDamage TakenTotal HealingMinions KilledEnemy Jungle CSTeam Jungle CSGold EarnedRole PositionPosition Change
RankChampionRoleWin PercentPlay PercentBan RatePlayerbase Avg. GamesKillsDeathsAssistsLargest Killing SpreeDamage DealtDamage TakenTotal HealingMinions KilledEnemy Jungle CSTeam Jungle CSGold EarnedRole PositionPosition Change
{{indexNumber($index, filteredChampions.length)}} 79 | 80 |
81 | {{champion.title}} 82 |
83 |
{{champion.role}} 86 | {{champion.general.winPercent}}% 87 | {{champion.general.playPercent}}%{{champion.general.banRate}}%{{champion.general.experience}}{{champion.general.kills}}{{champion.general.deaths}}{{champion.general.assists}}{{champion.general.largestKillingSpree}}{{champion.general.totalDamageDealtToChampions}}{{champion.general.totalDamageTaken}}{{champion.general.totalHeal}}{{champion.general.minionsKilled}}{{champion.general.neutralMinionsKilledEnemyJungle}}{{champion.general.neutralMinionsKilledTeamJungle}}{{champion.general.goldEarned}}{{champion.general.overallPosition}}{{Math.abs(champion.general.overallPositionChange)}}
107 |
108 |
109 | 110 |
111 | 112 |
113 | 116 |
117 |
118 | 119 | 122 | <% include scripts.ejs %> 123 | 124 | 125 | 126 | <% include footer.ejs %> 127 | --------------------------------------------------------------------------------